Aptos Agent Skills (https://github.com/aptos-labs/aptos-agent-skills) cover common workflows: write-contracts, generate-tests, security-audit, deploy-contracts, use-ts-sdk, ts-sdk-transactions, create-aptos-project, analyze-gas-optimization, modernize-move. Install the skill that matches the task before deep work. This is the curated low-token Aptos developer documentation set for AI agents and IDE assistants. # Build the Future of Web3 on Aptos > Everything you need to build a best-in-class Web3 experience. Getting Started * [Deploy Your First Move Smart Contract](/build/guides/first-move-module) Compile & publish Move modules to devnet in minutes. * [Your First Transaction](/build/guides/first-transaction) Write and read on-chain data using the TypeScript SDK. * [Code with AI (MCP)](/build/ai/aptos-mcp) Give Cursor and Claude Code direct access to Aptos APIs. * [NEW! Agent Skills](/build/ai/aptos-agent-skills) Move and TS SDK skills for Claude Code, Cursor, Copilot. Tools * [Testnet Faucet](/network/faucet) Fund your testnet account with APT to start building. * [Official SDKs](/build/sdks) TypeScript, Go, Java, Python, Rust, C++, Unity, and more. * [Aptos CLI](/build/cli) Compile, test, publish contracts; accounts & keys; localnet. Smart Contracts * [NEW! Move on Aptos (VS Code Extension)](/build/smart-contracts/move-vscode-extension) Aptos Labs' official extension for Move development. * [Objects](/build/smart-contracts/objects) Composable on-chain primitives for flexible asset ownership, addressing, & programmability. * [The Move Book](https://aptos-labs.github.io/move-book/) Understand Move syntax, types, resources, & best practices. * [Vibe Code a full-stack dApp on Learn](https://learn.aptoslabs.com/en/hackathon/vibe-coder-to-aptos-guide/introduction) Interactive AI workshop to quickly build a full-stack dApp. On-Chain Features * [Sponsored Transactions](/build/guides/sponsored-transactions) Pay for users' gas so they can use your dApp with zero APT. * [Keyless Accounts](/build/guides/aptos-keyless) Onboard users and sign without wallets or seed phrases. * [NEW! Orderless Transactions](/build/guides/orderless-transactions) High-volume apps can be safer by sending transactions out of order with replay-protection nonce. * [On-chain Randomness](/build/smart-contracts/randomness) Verifiable random number = fair games, lotteries, & drops. Resources * [NEW! LLMs.txt Integration](/llms-txt) AI-optimized documentation format, paste it in your favorite large context AI and get moving! * [Query, Index, or Stream On-Chain Data](/build/indexer) Query Indexer API, index contracts, stream raw transactions. * [Apply for a Grant](https://aptosnetwork.com/grants) Connect * [GitHub Developer Discussions](https://github.com/aptos-labs/aptos-developer-discussions/discussions) * [Ecosystem Directory](https://aptosnetwork.com/ecosystem/directory) * [Discord](https://discord.gg/aptosnetwork) * [Telegram](https://t.me/aptos) # Get Started Building on Aptos > Learn how to build on Aptos with smart contracts, indexer queries, SDKs, APIs, and comprehensive developer resources ## What would you like to learn? [](#what-would-you-like-to-learn) [Smart Contracts](/build/smart-contracts) Learn how to write smart contracts on Aptos with the Move programming language. [Query Data](/build/indexer) Use the Aptos Indexer to query for on-chain data efficiently. [Blockchain Infrastructure](/network/blockchain/blockchain-deep-dive) Learn the different components of the Aptos blockchain's infrastructure. ## What developer tools should I use? [](#what-developer-tools-should-i-use) [SDKs](/build/sdks) Use our TypeScript, Python, Rust, and other SDKs to submit transactions and read on-chain data. [Indexer](/build/indexer) Query for on-chain data like account balances, historical transactions, NFTs by account, and more. [CLI](/build/cli) Compile and profile Move smart contracts, run a local network, and more with the Aptos CLI. Here's an interactive example of our [Indexer](/build/indexer) and how you can query for the Current Fungible Asset Balances of an account. More usage examples can be found in [example queries](/build/indexer/indexer-api/fungible-asset-balances). ## Coming from another ecosystem? [](#coming-from-another-ecosystem) Quickly ramp up on some of the differences and similarities between Aptos and other ecosystems. [Ethereum / EVM to Aptos Cheatsheet](/build/get-started/ethereum-cheatsheet) [Solana / SVM to Aptos Cheatsheet](/build/get-started/solana-cheatsheet) [VM Comparison](/network/blockchain/move#comparison-to-other-vms) ## Do you have any examples? [](#do-you-have-any-examples) We've got all kinds of examples and guides, catered to what you're looking for. ### End-to-end guides [](#end-to-end-guides) [Your First Transaction](/build/guides/first-transaction) This tutorial describes how to generate and submit transactions to the Aptos blockchain, and verify these submitted transactions. [Your First NFT](/build/guides/your-first-nft) This tutorial describes how to create and transfer non-fungible assets on the Aptos blockchain. [Your First Fungible Asset](/build/guides/first-fungible-asset) This tutorial introduces how you can compile, deploy, and mint your own fungible asset (FA), named FACoin. ### Smart Contract guides [](#smart-contract-guides) See the [Smart Contract](/build/smart-contracts) section for more info [Create a Smart Contract](/build/guides/build-e2e-dapp/1-create-smart-contract) This is the first chapter of the tutorial on building an end-to-end dapp on Aptos. [Aptos Move Examples](https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/move-examples) 30+ examples on how to develop Move on Aptos [Move Tutorial](https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/move-examples/move-tutorial) Covers the basics of programming with Move ### Interactive guides [](#interactive-guides) [Aptos Learn](https://learn.aptoslabs.com/en/workshops) Basic and advanced guides [Move Spiders](https://movespiders.com) Learn about Move with a friendly spider mascot by Move Spiders [Aptos Shores](https://www.aptosshores.com) Learn about Move with Aptos Shores ## How do I setup a full node or validator? [](#how-do-i-setup-a-full-node-or-validator) [Setup a full node](/network/nodes/full-node) [Setup a validator](/network/nodes/validator-node) # Ethereum to Aptos Migration Guide > Comprehensive comparison and migration guide for Ethereum developers transitioning to Aptos blockchain development Aptos is built to allow you to quickly prototype and scale secure production applications. It combines a fast, cost-efficient, and stable blockchain layer with Move's compile-time safety that catches exploits before deployment, comprehensive tooling for rapid development, and a strong ecosystem of exchanges and bridges for seamless integration. ### High Level Overview [](#high-level-overview)
FeatureEthereumAptos
Account Addresses160-bit256-bit
Storage MindsetContract-based storageAccount centric mindset for code and data
Caller IDmsg.sender&signer reference
Smart ContractsSolidity, EVMMove, MoveVM
BenefitsMature, wide adoptionScalability, low latency, predictable fees
Transaction FeesVariable, can be highLower and more predictable
Sponsored TransactionsRequires third-party services or EIP-7702 wallet support (2025+)Natively supported via fee payer field. Geomi Gas Stations provides production infrastructure
Account StructureBalance in a single field, uses nonceModules and resources, uses sequence number
Data StoragePatricia Merkle TreesGlobal storage with resources and modules
UpgradeabilityProxy patternsDirect module upgrades
Safety & SecurityVulnerable to attacks like reentrancyMitigates common vulnerabilities
Dispatch TypeDynamic dispatchStatic dispatch
Frontend SDKEthers.js libraryAptos Typescript SDK
NFT StandardsERC-721, ERC-1155Digital Asset
FT StandardERC-20, factory patternSee Fungible Asset, copy paste in your module: use aptos_framework::fungible_asset...
Example CodeERC-20 (new contract per deploy)Fungible Asset (single reusable module)
Legacy [`Coin`](/build/smart-contracts/aptos-coin) documentation still covers the original standard; most new deployments should prefer the Fungible Asset module referenced above. ### Comparing Token Standards in Detail [](#comparing-token-standards-in-detail)
SolidityMove (Aptos)
Token StructureEach token is its own contract.Every token is a typed FungibleAsset instantiation that reuses the same published module.
Token StandardMust conform to standards like ERC-20; implementations can vary per deploy.Uniform interface and implementation enforced by the shared module; new tokens simply register a new type rather than redeploying code.
Balance StorageBalances stored in contract using a mapping structure.Resource-Oriented Balance: balances live in an extensible object owned by the user's account.
Transfer MechanismTokens can be transferred without receiver's explicit permission.Transfers can skip receiver permission, but only when the FA explicitly enables primary-store auto creation (visible in the token's creation code).
### Comparing EVM and Move VM in Detail [](#comparing-evm-and-move-vm-in-detail) * **EVM**: Known for its flexibility and dynamic dispatch, which allows a wide range of smart contract behaviors. This flexibility, however, can lead to complexities in parallel execution and network operations. * **Move VM**: Focuses on safety and efficiency with a more integrated approach between the VM and the programming language. Its data storage model allows for better parallelization, and its static dispatch method enhances security and predictability.
EVM (Ethereum Virtual Machine)Move VM (Move Virtual Machine)
Data StorageData is stored in the smart contract's storage space.Data is stored across smart contracts, user accounts, and objects.
ParallelizationParallel execution is limited due to shared storage space.More parallel execution enabled due to flexible split storage design.
VM and Language IntegrationSeparate layers for EVM and smart contract languages (e.g., Solidity).Seamless integration between VM layer and Move language, with native functions written in Rust executable in Move.
Critical Network OperationsImplementation of network operations can be complex and less direct.Critical operations like validator set management natively implemented in Move, allowing for direct execution.
Function CallingDynamic dispatch allows for arbitrary smart contract calls.Static dispatch aligns with a focus on security and predictable behavior.
Type SafetyContract types provide a level of type safety.Module structs and generics in Move offer robust type safety.
Transaction SafetyUses nonces for transaction ordering and safety.Uses sequence numbers for transaction ordering and safety.
Authenticated StorageYes, with smart contract storage.Yes, leveraging Move’s resource model.
Object AccessibilityObjects are not globally accessible; bound to smart contract scope.Guaranteed global accessibility of objects.
## Migration Tips for Ethereum Developers [](#migration-tips-for-ethereum-developers) * [Storage](#tab-panel-129) * [Authentication](#tab-panel-130) * [Resources & Abilities](#tab-panel-131) * [Upgradability](#tab-panel-132) * [Deploying Modules](#tab-panel-133) Ethereum stores all data in contract storage using mappings. Aptos uses an account-centric model where each account stores their own resources. Instead of a contract maintaining a `mapping(address => T)`, each user stores their own `T` resource at their address. ``` module my_hackathon_account::prototype { use std::string; use std::signer; // Unlike Solidity's mapping(address => string), each account stores their own resource struct MessageHolder has key, store, drop { message: string::String, } entry fun set_message(account: &signer, message: string::String) acquires MessageHolder { let addr = signer::address_of(account); // Check if resource exists at this account (like checking mapping[addr]) if (exists(addr)) { move_from(addr); // Remove old resource }; // Store resource at the user's address (in their account, not in contract storage!) move_to(account, MessageHolder { message }); } #[view] public fun get_message(addr: address): string::String acquires MessageHolder { assert!(exists(addr), 0); // Read message stored at the user's address borrow_global(addr).message }} ``` By default, modules deploy to your account address. For production apps, consider deploying to [Objects](/build/smart-contracts/objects), which creates a unique address per deployment and enables transferable code ownership. See [Using Objects](/build/smart-contracts/object/using-objects) for implementation details. Learn more: [`global storage operators`](https://aptos-labs.github.io/move-book/global-storage.html), [`structs and resources`](https://aptos-labs.github.io/move-book/structs-and-enums.html) On Ethereum, `msg.sender` is set by the EVM, but many bugs come from using `tx.origin` for auth or trusting user-supplied addresses. Aptos' `&signer` goes further: it's an unforgeable capability created only by the VM for actual transaction signers, so any function that requires `&signer` can't be called with a spoofed identity. In dapps, wallets plus the [Aptos TypeScript SDK](/build/sdks/ts-sdk) and [wallet adapter](/build/sdks/wallet-adapter/wallets) bridge this signer identity from frontend to on-chain. ``` module my_hackathon_account::prototype { use std::string; use std::signer; struct MessageHolder has key, store, drop { message: string::String, } // Only the account owner can provide their &signer (unforgeable authentication) entry fun set_message(account: &signer, message: string::String) acquires MessageHolder { // Extract address from authenticated signer (no spoofing possible!) let addr = signer::address_of(account); if (exists(addr)) { move_from(addr); }; // account is guaranteed to be authentic move_to(account, MessageHolder { message }); } #[view] public fun get_message(addr: address): string::String acquires MessageHolder { assert!(exists(addr), 0); // Read message stored at the user's address borrow_global(addr).message }} ``` Create and fund an account using the [Aptos CLI](/build/cli): ``` aptos init ``` Learn more: [`signer`](https://aptos-labs.github.io/move-book/primitive-types.html) type, [`functions`](https://aptos-labs.github.io/move-book/functions.html) Move has four abilities: `copy`, `drop`, `store`, and `key`, which control how values can be used. In this example, MessageHolder deliberately omits `copy` so messages stored as resources can't be duplicated; you generally avoid `copy` on any type that represents on-chain state or assets. `key` + `store` allow it to live in global storage at an address, and `drop` lets you destroy the old resource safely when overwriting it. ``` module my_hackathon_account::prototype { use std::string; use std::signer; // Resources: structs with 'key' ability that live in global storage // key = can be stored at account addresses (makes it a "resource") // store = can be stored inside other structs // drop = can be destroyed/discarded implicitly struct MessageHolder has key, store, drop { message: string::String, } entry fun set_message(account: &signer, message: string::String) acquires MessageHolder { let addr = signer::address_of(account); // The 'drop' ability allows implicit destruction if (exists(addr)) { move_from(addr); // Old resource is destroyed (requires 'drop') }; move_to(account, MessageHolder { message }); } #[view] public fun get_message(addr: address): string::String acquires MessageHolder { assert!(exists(addr), 0); // Read message stored at the user's address borrow_global(addr).message }} ``` Learn more: [`abilities`](https://aptos-labs.github.io/move-book/generics-and-abilities.html), [`structs and resources`](https://aptos-labs.github.io/move-book/structs-and-enums.html) Aptos packages support two upgrade policies: `compatible` (default; only backward-compatible changes allowed) and `immutable` (no upgrades allowed). By default, packages published with the [Aptos CLI](/build/cli) using `aptos move publish` use the **compatible** policy, which lets you push new versions as long as you don't break struct layouts or public function signatures. To prevent all future upgrades, set the immutable policy: ``` aptos move publish --upgrade-policy immutable ``` Or configure in your `Move.toml`: ``` [package]name = "MyPackage"version = "1.0.0"upgrade_policy = "immutable" ``` See [Package Upgrades](https://aptos-labs.github.io/move-book/cli-deploy.html) for the exact compatibility rules. On Ethereum you "deploy a contract to a new address." On Aptos you **publish a package** of Move modules to an account (or object) address using the [Aptos CLI](/build/cli). Publish your package: ``` aptos move publish ``` Call an `entry` function after deployment: ``` aptos move run --function-id 'your_address::module_name::function_name' ``` Both account-based publishing and object-based deployment (via `aptos move deploy-object`) respect the package's upgrade policy. See [Your First Move Module](/build/guides/first-move-module) for a complete walkthrough and [Objects](/build/smart-contracts/objects) for object-centric patterns. # Your First Transaction > Create and submit your first transaction on Aptos blockchain - transfer coins between accounts with TypeScript and Python examples. Transactions are the fundamental way to change data on the Aptos blockchain. Think of them like sending a package: you need to specify what you're sending, who it's going to, and then track it until delivery is confirmed. In blockchain terms, transactions allow you to transfer coins, call smart contract functions, and update on-chain state. This tutorial will guide you through creating and submitting your first transaction on the Aptos blockchain. You'll learn how to: 1. Set up your development environment 2. Create test accounts and fund them 3. Build a transaction to transfer coins 4. Simulate the transaction to estimate costs 5. Sign and submit the transaction 6. Verify the transaction was executed successfully ## 1\. Setting Up Your Environment [](#1-setting-up-your-environment) * [TypeScript](#tab-panel-153) * [Python](#tab-panel-154) Before we can create transactions, we need to set up our development environment with the necessary tools and SDKs. 1. Install the TypeScript SDK Install the TypeScript SDK using your preferred package manager: * [npm](#tab-panel-134) * [yarn](#tab-panel-135) * [pnpm](#tab-panel-136) ``` npm install @aptos-labs/ts-sdk ``` ``` yarn add @aptos-labs/ts-sdk ``` ``` pnpm add @aptos-labs/ts-sdk ``` 2. Create a project directory Create a new directory for your project: ``` mkdir my-first-transactioncd my-first-transaction ``` 3. Create a new file Create a new file named `transaction.ts`: * [Mac/Linux](#tab-panel-137) * [Windows](#tab-panel-138) ``` touch transaction.ts ``` ``` type nul > transaction.ts ``` Before we can create transactions, we need to set up our development environment with the necessary tools and SDKs. 1. Install the Python SDK Install the Python SDK using pip: ``` pip install aptos-sdk ``` 2. Create a project directory Create a new directory for your project: ``` mkdir my-first-transactioncd my-first-transaction ``` 3. Create a new file Create a new file named `transaction.py`: * [Mac/Linux](#tab-panel-139) * [Windows](#tab-panel-140) ``` touch transaction.py ``` ``` type nul > transaction.py ``` ## 2\. Creating Test Accounts [](#2-creating-test-accounts) * [TypeScript](#tab-panel-143) * [Python](#tab-panel-144) In blockchain, all transactions must come from an account. Let's create two test accounts: one to send coins (Alice) and one to receive them (Bob). 1. Set up the client First, we need to initialize the Aptos client that will connect to the blockchain. Open `transaction.ts` in your editor and add: ``` import { Account, Aptos, AptosConfig, Network,} from "@aptos-labs/ts-sdk"; async function main() { // Initialize the Aptos client const config = new AptosConfig({ network: Network.DEVNET }); const aptos = new Aptos(config); console.log("Connected to Aptos devnet"); // More code will go here} main().catch(console.error); ``` 2. Generate accounts Add this code inside your `main()` function to create two accounts - Alice (sender) and Bob (receiver): ``` // Generate two accountsconst alice = Account.generate();const bob = Account.generate(); console.log("=== Addresses ===");console.log(`Alice's address: ${alice.accountAddress}`);console.log(`Bob's address: ${bob.accountAddress}`); ``` 3. Fund the accounts Add this code after generating the accounts to get test coins from the faucet: ``` // Fund the accounts with test APT from the devnet faucetconsole.log("\n=== Funding accounts ===");await aptos.fundAccount({ accountAddress: alice.accountAddress, amount: 100_000_000, // 1 APT = 100,000,000 octas});console.log("Accounts funded successfully"); // Check initial balancesconst aliceBalance = await aptos.getAccountAPTAmount({ accountAddress: alice.accountAddress,});const bobBalance = await aptos.getAccountAPTAmount({ accountAddress: bob.accountAddress,}); console.log("\n=== Initial Balances ===");console.log(`Alice: ${aliceBalance} octas`);console.log(`Bob: ${bobBalance} octas`); ``` 4. Run the code Let's test our code so far: ``` npx ts-node transaction.ts ``` You should see output similar to: ``` Connected to Aptos devnet=== Addresses ===Alice's address: 0x978c213990c4833df71548df7ce49d54c759d6b6d932de22b24d56060b7af2aaBob's address: 0x7af2d6c93a2feafc9b69b5e8ad9d6b513b260f62f23f3a384a3a2e4a84694a9b === Funding accounts ===Accounts funded successfully === Initial Balances ===Alice: 100000000 octasBob: 0 octas ``` In blockchain, all transactions must come from an account. Let's create two test accounts: one to send coins (Alice) and one to receive them (Bob). 1. Set up the client First, we need to initialize the Aptos client that will connect to the blockchain. Open `transaction.py` in your editor and add: ``` import asynciofrom aptos_sdk.account import Accountfrom aptos_sdk.async_client import FaucetClient, RestClientfrom aptos_sdk.transactions import EntryFunction, TransactionPayload, TransactionArgument, RawTransactionfrom aptos_sdk.bcs import Serializerimport time # Network configurationNODE_URL = "https://fullnode.devnet.aptoslabs.com/v1"FAUCET_URL = "https://faucet.devnet.aptoslabs.com" async def main(): # Initialize the clients rest_client = RestClient(NODE_URL) faucet_client = FaucetClient(FAUCET_URL, rest_client) print("Connected to Aptos devnet") # More code will go here if __name__ == "__main__": asyncio.run(main()) ``` 2. Generate accounts Add this code inside your `main()` function to create two accounts - Alice (sender) and Bob (receiver): ``` # Generate two accountsalice = Account.generate()bob = Account.generate() print("=== Addresses ===")print(f"Alice's address: {alice.address()}")print(f"Bob's address: {bob.address()}") ``` 3. Fund the accounts Add this code after generating the accounts to get test coins from the faucet: ``` # Fund the accounts with test APT from the devnet faucetprint("\n=== Funding accounts ===")alice_amount = 100_000_000 # 1 APT = 100,000,000 octasbob_amount = 0 # Bob starts with 0 APT await faucet_client.fund_account(alice.address(), alice_amount)print("Account funded successfully") # Check initial balancesalice_balance = await rest_client.account_balance(alice.address())bob_balance = await rest_client.account_balance(bob.address()) print("\n=== Initial Balances ===")print(f"Alice: {alice_balance} octas")print(f"Bob: {bob_balance} octas") ``` 4. Run the code Let's test our code so far: ``` python transaction.py ``` You should see output similar to: ``` Connected to Aptos devnet=== Addresses ===Alice's address: 0x978c213990c4833df71548df7ce49d54c759d6b6d932de22b24d56060b7af2aaBob's address: 0x7af2d6c93a2feafc9b69b5e8ad9d6b513b260f62f23f3a384a3a2e4a84694a9b === Funding accounts ===Accounts funded successfully === Initial Balances ===Alice: 100000000 octasBob: 0 octas ``` ## 3\. Building a Transaction [](#3-building-a-transaction) * [TypeScript](#tab-panel-145) * [Python](#tab-panel-146) Now that we have funded accounts, let's create a transaction to transfer coins from Alice to Bob. This is like filling out a form specifying what you want to send and to whom. 1. Understand transaction structure A transaction in Aptos has several key components: 1. **Sender**: The account initiating the transaction (Alice) 2. **Function**: The on-chain function to call (in this case, a coin transfer) 3. **Arguments**: Data needed by the function (recipient address and amount) 4. **Gas parameters**: Maximum gas amount and gas unit price 5. **Expiration time**: When the transaction is no longer valid if not executed 6. **Sequence number**: A counter that prevents replay attacks 2. Build the transaction Let's add code to build a transaction that transfers 1000 octas from Alice to Bob: Add this code to your `main()` function: ``` // 1. Build the transactionconsole.log("\n=== 1. Building the transaction ===");const transaction = await aptos.transaction.build.simple({ sender: alice.accountAddress, data: { function: "0x1::aptos_account::transfer", functionArguments: [bob.accountAddress, 1000], // Transfer 1000 octas },});console.log("Transaction built successfully"); // Access transaction details from the raw transactionconst rawTxn = transaction.rawTransaction;console.log(`Sender: ${rawTxn.sender}`);console.log(`Sequence Number: ${rawTxn.sequence_number}`);console.log(`Max Gas Amount: ${rawTxn.max_gas_amount}`);console.log(`Gas Unit Price: ${rawTxn.gas_unit_price}`);console.log(`Expiration Timestamp: ${new Date(Number(rawTxn.expiration_timestamp_secs) * 1000).toISOString()}`); ``` Now that we have funded accounts, let's create a transaction to transfer coins from Alice to Bob. This is like filling out a form specifying what you want to send and to whom. 1. Understand transaction structure A transaction in Aptos has several key components: 1. **Sender**: The account initiating the transaction (Alice) 2. **Function**: The on-chain function to call (in this case, a coin transfer) 3. **Arguments**: Data needed by the function (recipient address and amount) 4. **Gas parameters**: Maximum gas amount and gas unit price 5. **Expiration time**: When the transaction is no longer valid if not executed 6. **Sequence number**: A counter that prevents replay attacks 2. Build the transaction Add the following code to your `main()` function to build a transaction that transfers 1000 octas from Alice to Bob: ``` # 1. Build the transactionprint("\n=== 1. Building the transaction ===") # Create the entry function payload# This specifies which function to call and with what argumentsentry_function = EntryFunction.natural( "0x1::aptos_account", # Module address and name "transfer", # Function name [], # Type arguments (empty for this function) [ # Function arguments with their serialization type TransactionArgument(bob.address(), Serializer.struct), # Recipient address TransactionArgument(1000, Serializer.u64), # Amount to transfer (1000 octas) ],) # Get the chain ID for the transactionchain_id = await rest_client.chain_id() # Get the sender's current sequence numberaccount_data = await rest_client.account(alice.address())sequence_number = int(account_data["sequence_number"]) # Create the raw transaction with all required fieldsraw_transaction = RawTransaction( sender=alice.address(), # Sender's address sequence_number=sequence_number, # Sequence number to prevent replay attacks payload=TransactionPayload(entry_function), # The function to call max_gas_amount=2000, # Maximum gas units to use gas_unit_price=100, # Price per gas unit in octas expiration_timestamps_secs=int(time.time()) + 600, # Expires in 10 minutes chain_id=chain_id, # Chain ID to ensure correct network) print("Transaction built successfully")print(f"Sender: {raw_transaction.sender}")print(f"Sequence Number: {raw_transaction.sequence_number}")print(f"Max Gas Amount: {raw_transaction.max_gas_amount}")print(f"Gas Unit Price: {raw_transaction.gas_unit_price}")print(f"Expiration Timestamp: {time.ctime(raw_transaction.expiration_timestamps_secs)}") ``` ## 4\. Simulating the Transaction [](#4-simulating-the-transaction) * [TypeScript](#tab-panel-147) * [Python](#tab-panel-148) Before submitting a transaction, it's wise to simulate it first to estimate the gas cost. This is like checking shipping costs before sending a package. 1. Simulate the transaction Add this code after building the transaction: ``` // 2. Simulate the transactionconsole.log("\n=== 2. Simulating the transaction ===");const [simulationResult] = await aptos.transaction.simulate.simple({ signerPublicKey: alice.publicKey, transaction,}); const gasUsed = parseInt(simulationResult.gas_used);const gasUnitPrice = parseInt(simulationResult.gas_unit_price);console.log(`Estimated gas units: ${gasUsed}`);console.log(`Estimated gas cost: ${gasUsed * gasUnitPrice} octas`);console.log(`Transaction would ${simulationResult.success ? "succeed" : "fail"}`); ``` Before submitting a transaction, it's wise to simulate it first to estimate the gas cost. This is like checking shipping costs before sending a package. 1. Simulate the transaction Add this code after building the transaction: ``` # 2. Simulate the transactionprint("\n=== 2. Simulating the transaction ===") # Create a BCS transaction for simulation# This doesn't actually submit the transaction to the blockchainsimulation_transaction = await rest_client.create_bcs_transaction(alice, TransactionPayload(entry_function)) # Simulate the transaction to estimate gas costs and check for errorssimulation_result = await rest_client.simulate_transaction(simulation_transaction, alice) # Extract and display the simulation resultsgas_used = int(simulation_result[0]['gas_used'])gas_unit_price = int(simulation_result[0]['gas_unit_price'])success = simulation_result[0]['success'] print(f"Estimated gas units: {gas_used}")print(f"Estimated gas cost: {gas_used * gas_unit_price} octas")print(f"Transaction would {'succeed' if success else 'fail'}") ``` ## 5\. Signing and Submitting the Transaction [](#5-signing-and-submitting-the-transaction) * [TypeScript](#tab-panel-149) * [Python](#tab-panel-150) Now that we've built and simulated the transaction, we need to sign it with Alice's private key and submit it to the blockchain. 1. Sign the transaction Signing proves that Alice authorized this transaction: Add this code after simulating the transaction: ``` // 3. Sign the transactionconsole.log("\n=== 3. Signing the transaction ===");const senderAuthenticator = aptos.transaction.sign({ signer: alice, transaction,});console.log("Transaction signed successfully"); ``` 2. Submit the transaction Add this code after signing the transaction to submit the signed transaction to the blockchain: ``` // 4. Submit the transactionconsole.log("\n=== 4. Submitting the transaction ===");const pendingTransaction = await aptos.transaction.submit.simple({ transaction, senderAuthenticator,});console.log(`Transaction submitted with hash: ${pendingTransaction.hash}`); ``` Now that we've built and simulated the transaction, we need to sign it with Alice's private key and submit it to the blockchain. 1. Sign the transaction Signing proves that Alice authorized this transaction: Add this code after simulating the transaction: ``` # 3. Sign the transactionprint("\n=== 3. Signing the transaction ===") # Sign the raw transaction with the sender's private key# This creates a cryptographic signature that proves the sender authorized this transactionsigned_transaction = await rest_client.create_bcs_signed_transaction( alice, # Account with the private key TransactionPayload(entry_function), # The payload from our transaction sequence_number=sequence_number # Use the same sequence number as before) print("Transaction signed successfully")# We can't easily extract the signature from the signed transaction object,# but we can confirm it was created ``` 2. Submit the transaction Add this code after signing the transaction to submit the signed transaction to the blockchain: ``` # 4. Submit the transactionprint("\n=== 4. Submitting the transaction ===") # Submit the signed transaction to the blockchain# This broadcasts the transaction to the network for processingtx_hash = await rest_client.submit_bcs_transaction(signed_transaction) print(f"Transaction submitted with hash: {tx_hash}") ``` ## 6\. Waiting for Confirmation [](#6-waiting-for-confirmation) * [TypeScript](#tab-panel-151) * [Python](#tab-panel-152) After submitting a transaction, we need to wait for it to be processed by the blockchain. This is like waiting for a package to be delivered. 1. Wait for transaction completion Add this code after submitting the transaction: ``` // 5. Wait for the transaction to completeconsole.log("\n=== 5. Waiting for transaction completion ===");const txnResult = await aptos.waitForTransaction({ transactionHash: pendingTransaction.hash,});console.log(`Transaction completed with status: ${txnResult.success ? "SUCCESS" : "FAILURE"}`); // If you want to see more details about the transaction:console.log(`VM Status: ${txnResult.vm_status}`);console.log(`Gas used: ${txnResult.gas_used}`); ``` 2. Verify the results Add this code after waiting for the transaction to check the balances and confirm the transfer worked: ``` // Check final balancesconst aliceFinalBalance = await aptos.getAccountAPTAmount({ accountAddress: alice.accountAddress,});const bobFinalBalance = await aptos.getAccountAPTAmount({ accountAddress: bob.accountAddress,}); console.log("\n=== Final Balances ===");console.log(`Alice: ${aliceFinalBalance} octas (spent ${aliceBalance - aliceFinalBalance} octas on transfer and gas)`);console.log(`Bob: ${bobFinalBalance} octas (received 1000 octas)`); ``` 3. Run the complete code ``` npx ts-node transaction.ts ``` You should see output similar to: ``` Connected to Aptos devnet=== Addresses ===Alice's address: 0x978c213990c4833df71548df7ce49d54c759d6b6d932de22b24d56060b7af2aaBob's address: 0x7af2d6c93a2feafc9b69b5e8ad9d6b513b260f62f23f3a384a3a2e4a84694a9b === Funding accounts ===Accounts funded successfully === Initial Balances ===Alice: 100000000 octasBob: 0 octas === 1. Building the transaction ===Transaction built successfullySender: 0x978c213990c4833df71548df7ce49d54c759d6b6d932de22b24d56060b7af2aaSequence Number: 0Max Gas Amount: 20000Gas Unit Price: 100Expiration Timestamp: 2025-03-05T22:59:21.000Z === 2. Simulating the transaction ===Estimated gas units: 146Estimated gas cost: 14600 octasTransaction would succeed === 3. Signing the transaction ===Transaction signed successfully === 4. Submitting the transaction ===Transaction submitted with hash: 0x3a8a3e34a1c64ad9d7636a3a827b7ec3bb12d73825b36fa06d425c5a3b42cccc === 5. Waiting for transaction completion ===Transaction completed with status: SUCCESSVM Status: Executed successfullyGas used: 146 === Final Balances ===Alice: 99984400 octas (spent 15600 octas on transfer and gas)Bob: 1000 octas (received 1000 octas) ``` After submitting a transaction, we need to wait for it to be processed by the blockchain. This is like waiting for a package to be delivered. 1. Wait for transaction completion Add this code after submitting the transaction: ``` # 5. Wait for the transaction to completeprint("\n=== 5. Waiting for transaction completion ===") # Wait for the transaction to be processed by the blockchain# This polls the blockchain until the transaction is confirmedawait rest_client.wait_for_transaction(tx_hash) # Get the transaction details to check its statustransaction_details = await rest_client.transaction_by_hash(tx_hash)success = transaction_details["success"]vm_status = transaction_details["vm_status"]gas_used = transaction_details["gas_used"] print(f"Transaction completed with status: {'SUCCESS' if success else 'FAILURE'}")print(f"VM Status: {vm_status}")print(f"Gas used: {gas_used}") ``` 2. Verify the results Add this code after waiting for the transaction to check the balances and confirm the transfer worked: ``` # Check final balancesalice_final_balance = await rest_client.account_balance(alice.address())bob_final_balance = await rest_client.account_balance(bob.address()) print("\n=== Final Balances ===")print(f"Alice: {alice_final_balance} octas (spent {alice_balance - alice_final_balance} octas on transfer and gas)")print(f"Bob: {bob_final_balance} octas (received 1000 octas)") ``` 3. Run the complete code ``` python transaction.py ``` You should see output similar to: ``` Connected to Aptos devnet=== Addresses ===Alice's address: 0x978c213990c4833df71548df7ce49d54c759d6b6d932de22b24d56060b7af2aaBob's address: 0x7af2d6c93a2feafc9b69b5e8ad9d6b513b260f62f23f3a384a3a2e4a84694a9b === Funding accounts ===Accounts funded successfully === Initial Balances ===Alice: 100000000 octasBob: 0 octas === 1. Building the transaction ===Transaction built successfullySender: 0x978c213990c4833df71548df7ce49d54c759d6b6d932de22b24d56060b7af2aaSequence Number: 0Max Gas Amount: 2000Gas Unit Price: 100Expiration Timestamp: Wed Mar 05 22:59:21 2025 === 2. Simulating the transaction ===Estimated gas units: 146Estimated gas cost: 14600 octasTransaction would succeed === 3. Signing the transaction ===Transaction signed successfully === 4. Submitting the transaction ====== 3. Signing the transaction ===Transaction signed successfully === 4. Submitting the transaction ===Transaction submitted with hash: 0x3a8a3e34a1c64ad9d7636a3a827b7ec3bb12d73825b36fa06d425c5a3b42cccc === 5. Waiting for transaction completion ===Transaction completed with status: SUCCESSVM Status: Executed successfullyGas used: 146 === Final Balances ===Alice: 99984400 octas (spent 15600 octas on transfer and gas)Bob: 1000 octas (received 1000 octas) ``` ## 7\. (Optional) Explore Your Transaction On-Chain [](#7-optional-explore-your-transaction-on-chain) Now that you've successfully executed a transaction, you can explore it on the Aptos Explorer. This will help you understand how transactions are recorded on the blockchain and what information is publicly available. 1. Copy your transaction hash From your terminal output, copy the transaction hash that was printed after submission. It looks something like this: ``` Transaction submitted with hash: 0x3a8a3e34a1c64ad9d7636a3a827b7ec3bb12d73825b36fa06d425c5a3b42cccc ``` 2. Open the Aptos Explorer Go to the [Aptos Explorer](https://explorer.aptoslabs.com/?network=devnet). 3. Ensure you are on Devnet network Look for "Devnet" in the top right corner, or switch networks by clicking the dropdown and selecting Devnet. ![Switching to Devnet network in Aptos Explorer](/_vercel/image?url=_astro%2Fexplorer_devnet.D3PWblc6.png&w=320&q=100&dpl=dpl_D4B3ALZU3BeSgmAM8HaDXYy9SSQC) 4. Search for your transaction Paste your transaction hash into the search bar in the middle of the page. Caution Do not press enter! There is a known bug where searching with Enter does not work. 5. View the transaction details Wait for the results to appear, then click on the transaction hash to view its details. You should see information about your transaction, including: * Status (should be "Success") * Timestamp * Gas used * Sender and recipient addresses * Amount transferred 6. Explore further From the transaction details page, you can: * Click on the sender or recipient addresses to view their account details * See the exact changes made to the blockchain state * View the transaction payload and arguments ## 8\. Next Steps [](#8-next-steps) Congratulations! You've successfully created and executed your first transaction on the Aptos blockchain. Here are some suggestions for what to explore next: **Learn about more complex transactions**: * [Multi-Agent Signatures](/build/sdks/ts-sdk/building-transactions/multi-agent-transactions) - Transactions requiring multiple signers * [Sponsoring Transactions](/build/sdks/ts-sdk/building-transactions/sponsoring-transactions) - Having another account pay gas fees * [Batching Transactions](/build/sdks/ts-sdk/building-transactions/batching-transactions) - Sending multiple transactions efficiently **Explore smart contracts or account basics**: * [Your First Move Module](/build/guides/first-move-module) - Create your own smart contract * [Account Basics](/network/blockchain/accounts) [Join the Aptos Discord](https://discord.gg/aptoslabs) and share what you're building! ## Full Code Sample [](#full-code-sample) The complete code samples below combine all the snippets we've covered in this tutorial: * [TypeScript](#tab-panel-141) * [Python](#tab-panel-142) ``` import { Account, Aptos, AptosConfig, Network } from "@aptos-labs/ts-sdk"; async function main() { // Initialize the Aptos client const config = new AptosConfig({ network: Network.DEVNET }); const aptos = new Aptos(config); console.log("Connected to Aptos devnet"); // More code will go here // Generate two accounts const alice = Account.generate(); const bob = Account.generate(); console.log("=== Addresses ==="); console.log(`Alice's address: ${alice.accountAddress}`); console.log(`Bob's address: ${bob.accountAddress}`); // Fund the accounts with test APT from the devnet faucet console.log("\n=== Funding accounts ==="); await aptos.fundAccount({ accountAddress: alice.accountAddress, amount: 100_000_000, // 1 APT = 100,000,000 octas }); await aptos.fundAccount({ accountAddress: bob.accountAddress, amount: 0, // Bob starts with 0 APT }); console.log("Accounts funded successfully"); // Check initial balances const aliceBalance = await aptos.getAccountAPTAmount({ accountAddress: alice.accountAddress, }); const bobBalance = await aptos.getAccountAPTAmount({ accountAddress: bob.accountAddress, }); console.log("\n=== Initial Balances ==="); console.log(`Alice: ${aliceBalance} octas`); console.log(`Bob: ${bobBalance} octas`); // 1. Build the transaction console.log("\n=== 1. Building the transaction ==="); const transaction = await aptos.transaction.build.simple({ sender: alice.accountAddress, data: { function: "0x1::aptos_account::transfer", functionArguments: [bob.accountAddress, 1000], // Transfer 1000 octas }, }); console.log("Transaction built successfully"); // Use type assertion to bypass TypeScript's type checking const txnAny = transaction as any; console.log(`Sender: ${alice.accountAddress}`); // Use the known sender address console.log(`Sequence Number: ${txnAny.sequenceNumber || "N/A"}`); console.log(`Max Gas Amount: ${txnAny.maxGasAmount || "N/A"}`); console.log(`Gas Unit Price: ${txnAny.gasUnitPrice || "N/A"}`); console.log( `Expiration Timestamp: ${new Date( Number(txnAny.expirationTimestampSecs || 0) * 1000 ).toISOString()}` ); // 2. Simulate the transaction console.log("\n=== 2. Simulating the transaction ==="); const [simulationResult] = await aptos.transaction.simulate.simple({ signerPublicKey: alice.publicKey, transaction, }); console.log(`Estimated gas units: ${simulationResult.gas_used}`); console.log( `Estimated gas cost: ${ Number(simulationResult.gas_used) * Number(simulationResult.gas_unit_price) } octas` ); console.log( `Transaction would ${simulationResult.success ? "succeed" : "fail"}` ); // 3. Sign the transaction console.log("\n=== 3. Signing the transaction ==="); const senderAuthenticator = aptos.transaction.sign({ signer: alice, transaction, }); console.log("Transaction signed successfully"); // Use type assertion to bypass TypeScript's type checking const authAny = senderAuthenticator as any; const signatureStr = typeof authAny.signature === 'string' ? authAny.signature : JSON.stringify(authAny.signature || ''); console.log(`Signature: ${signatureStr.slice(0, 20)}...`); // 4. Submit the transaction console.log("\n=== 4. Submitting the transaction ==="); const pendingTransaction = await aptos.transaction.submit.simple({ transaction, senderAuthenticator, }); console.log(`Transaction submitted with hash: ${pendingTransaction.hash}`); // 5. Wait for the transaction to complete console.log("\n=== 5. Waiting for transaction completion ==="); const txnResult = await aptos.waitForTransaction({ transactionHash: pendingTransaction.hash, }); console.log( `Transaction completed with status: ${ txnResult.success ? "SUCCESS" : "FAILURE" }` ); // If you want to see more details about the transaction: console.log(`VM Status: ${txnResult.vm_status}`); console.log(`Gas used: ${txnResult.gas_used}`); // Check final balances const aliceFinalBalance = await aptos.getAccountAPTAmount({ accountAddress: alice.accountAddress, }); const bobFinalBalance = await aptos.getAccountAPTAmount({ accountAddress: bob.accountAddress, }); console.log("\n=== Final Balances ==="); console.log( `Alice: ${aliceFinalBalance} octas (spent ${ aliceBalance - aliceFinalBalance } octas on transfer and gas)` ); console.log(`Bob: ${bobFinalBalance} octas (received 1000 octas)`);} main().catch(console.error); ``` ``` import asynciofrom aptos_sdk.account import Accountfrom aptos_sdk.async_client import FaucetClient, RestClientfrom aptos_sdk.transactions import EntryFunction, TransactionPayload, TransactionArgument, RawTransactionfrom aptos_sdk.bcs import Serializerimport time # Network configurationNODE_URL = "https://fullnode.devnet.aptoslabs.com/v1"FAUCET_URL = "https://faucet.devnet.aptoslabs.com" async def main(): # Initialize the clients rest_client = RestClient(NODE_URL) faucet_client = FaucetClient(FAUCET_URL, rest_client) print("Connected to Aptos devnet") # Generate two accounts alice = Account.generate() bob = Account.generate() print("=== Addresses ===") print(f"Alice's address: {alice.address()}") print(f"Bob's address: {bob.address()}") # More code will go here # Fund the accounts with test APT from the devnet faucet print("\n=== Funding accounts ===") alice_amount = 100_000_000 # 1 APT = 100,000,000 octas bob_amount = 0 # Bob starts with 0 APT await faucet_client.fund_account(alice.address(), alice_amount) await faucet_client.fund_account(bob.address(), bob_amount) print("Accounts funded successfully") # Check initial balances alice_balance = await rest_client.account_balance(alice.address()) bob_balance = await rest_client.account_balance(bob.address()) print("\n=== Initial Balances ===") print(f"Alice: {alice_balance} octas") print(f"Bob: {bob_balance} octas") # 1. Build the transaction print("\n=== 1. Building the transaction ===") # Create the entry function payload # This specifies which function to call and with what arguments entry_function = EntryFunction.natural( "0x1::aptos_account", # Module address and name "transfer", # Function name [], # Type arguments (empty for this function) [ # Function arguments with their serialization type TransactionArgument(bob.address(), Serializer.struct), # Recipient address TransactionArgument(1000, Serializer.u64), # Amount to transfer (1000 octas) ], ) # Get the chain ID for the transaction chain_id = await rest_client.chain_id() # Get the sender's current sequence number account_data = await rest_client.account(alice.address()) sequence_number = int(account_data["sequence_number"]) # Create the raw transaction with all required fields raw_transaction = RawTransaction( sender=alice.address(), # Sender's address sequence_number=sequence_number, # Sequence number to prevent replay attacks payload=TransactionPayload(entry_function), # The function to call max_gas_amount=2000, # Maximum gas units to use gas_unit_price=100, # Price per gas unit in octas expiration_timestamps_secs=int(time.time()) + 600, # Expires in 10 minutes chain_id=chain_id, # Chain ID to ensure correct network ) print("Transaction built successfully") print(f"Sender: {raw_transaction.sender}") print(f"Sequence Number: {raw_transaction.sequence_number}") print(f"Max Gas Amount: {raw_transaction.max_gas_amount}") print(f"Gas Unit Price: {raw_transaction.gas_unit_price}") print(f"Expiration Timestamp: {time.ctime(raw_transaction.expiration_timestamps_secs)}") # 2. Simulate the transaction print("\n=== 2. Simulating the transaction ===") # Create a BCS transaction for simulation # This doesn't actually submit the transaction to the blockchain simulation_transaction = await rest_client.create_bcs_transaction(alice, TransactionPayload(entry_function)) # Simulate the transaction to estimate gas costs and check for errors simulation_result = await rest_client.simulate_transaction(simulation_transaction, alice) # Extract and display the simulation results gas_used = int(simulation_result[0]['gas_used']) gas_unit_price = int(simulation_result[0]['gas_unit_price']) success = simulation_result[0]['success'] print(f"Estimated gas units: {gas_used}") print(f"Estimated gas cost: {gas_used * gas_unit_price} octas") print(f"Transaction would {'succeed' if success else 'fail'}") # 3. Sign the transaction print("\n=== 3. Signing the transaction ===") # Sign the raw transaction with the sender's private key # This creates a cryptographic signature that proves the sender authorized this transaction signed_transaction = await rest_client.create_bcs_signed_transaction( alice, # Account with the private key TransactionPayload(entry_function), # The payload from our transaction sequence_number=sequence_number # Use the same sequence number as before ) print("Transaction signed successfully") # We can't easily extract the signature from the signed transaction object, # but we can confirm it was created # 4. Submit the transaction print("\n=== 4. Submitting the transaction ===") # Submit the signed transaction to the blockchain # This broadcasts the transaction to the network for processing tx_hash = await rest_client.submit_bcs_transaction(signed_transaction) print(f"Transaction submitted with hash: {tx_hash}") # 5. Wait for the transaction to complete print("\n=== 5. Waiting for transaction completion ===") # Wait for the transaction to be processed by the blockchain # This polls the blockchain until the transaction is confirmed await rest_client.wait_for_transaction(tx_hash) # Get the transaction details to check its status transaction_details = await rest_client.transaction_by_hash(tx_hash) success = transaction_details["success"] vm_status = transaction_details["vm_status"] gas_used = transaction_details["gas_used"] print(f"Transaction completed with status: {'SUCCESS' if success else 'FAILURE'}") print(f"VM Status: {vm_status}") print(f"Gas used: {gas_used}") # Check final balances alice_final_balance = await rest_client.account_balance(alice.address()) bob_final_balance = await rest_client.account_balance(bob.address()) print("\n=== Final Balances ===") print(f"Alice: {alice_final_balance} octas (spent {alice_balance - alice_final_balance} octas on transfer and gas)") print(f"Bob: {bob_final_balance} octas (received 1000 octas)")if __name__ == "__main__": asyncio.run(main()) ``` # Your First Move Module > Learn to compile, test, publish, and interact with Move smart contracts on Aptos blockchain from setup to deployment. The Aptos blockchain allows developers to write Turing complete smart contracts (called “modules”) with the secure-by-design Move language. Smart contracts enable users to send money with the blockchain, but also write arbitrary code, even games! It all starts with the Aptos CLI creating an account which will store the deployed (”published”) Move module. This tutorial will help you understand Move Modules by guiding you through setting up a minimal Aptos environment, then how to compile, test, publish and interact with Move modules on the Aptos Blockchain. You will learn how to: 1. Setup your environment, install the CLI 2. Create a devnet account and fund it 3. Compile and test a Move module 4. Publish (or "deploy") a Move module to the Aptos blockchain 5. Interact with the module 6. Keep building with Aptos (next steps) ## 1\. Setup [](#1-setup) Changes to the blockchain are called “transactions”, and they require an account to pay the network fee (”gas fee”). We will need to create an account with some APT to pay that fee and own the published contract. In order to do that, we will need to use the Aptos CLI. 1. Install the Aptos CLI [Install the Aptos CLI](/build/cli) (if you haven't already). 2. Open a new terminal Open a new terminal window or tab. 3. Verify the installation Run `aptos --version` to verify you have it installed. ``` aptos --version ``` You should see a response like `aptos 4.6.1`. 4. Create a project folder Create a new folder for this tutorial by running: ``` mkdir my-first-module ``` 5. Navigate to the project folder Run `cd my-first-module` to go into your new folder. 6. Initialize your account Run `aptos init` and press 'enter' for each step of setup to create a test account on `devnet`. You should see a success message like this: ``` ---Aptos CLI is now set up for account 0x9ec1cfa30b885a5c9d595f32f3381ec16d208734913b587be9e210f60be9f9ba as profile default!{ "Result": "Success"} ``` ## 2\. (Optional) Explore What You Just Did On-Chain [](#2-optional-explore-what-you-just-did-on-chain) 1. Copy your account address Copy the address from the command line for your new account. The address looks like this `0x9ec1cfa30b885a5c9d595f32f3381ec16d208734913b587be9e210f60be9f9ba` and you can find it in the line: ``` Aptos CLI is now set up for account 0x9ec1cfa30b885a5c9d595f32f3381ec16d208734913b587be9e210f60be9f9ba as profile default! ``` 2. Open the Aptos Explorer Go to the [Aptos Explorer](https://explorer.aptoslabs.com/?network=devnet). This is the primary way to quickly check what is happening on devnet, testnet, or mainnet. We will use it later on to view our deployed contracts. 3. Ensure you are on Devnet network. Look for “Devnet" in the top right corner, or switch networks by clicking the “Mainnet” dropdown and selecting Devnet ![Switching to Devnet network in Aptos Explorer](/_vercel/image?url=_astro%2Fexplorer_devnet.D3PWblc6.png&w=320&q=100&dpl=dpl_D4B3ALZU3BeSgmAM8HaDXYy9SSQC) 4. Search for your account Paste your newly created address into the search bar. Caution Do not press enter! There is a known bug where searching with Enter does not work. 5. View the search results Wait for the results to appear, then click the top result. 6. Check the transaction You should see your newly created account and a transaction with the faucet function, funding it with devnet tokens. ![Viewing Account in Aptos Explorer](/_vercel/image?url=_astro%2Fexplorer_account.DlK3EDU1.png&w=1280&q=100&dpl=dpl_D4B3ALZU3BeSgmAM8HaDXYy9SSQC) 7. Verify your balance Click the "Coins" tab to see that you have 1 APT of the Aptos Coin. This will allow you to publish and interact with smart contracts on the aptos devnet. ## 3\. Writing and Compiling Your First Module [](#3-writing-and-compiling-your-first-module) Now that we have our environment set up and an account created, let's write and compile our first Move module. Unlike Ethereum where contracts exist independently, Move ties everything to accounts - both modules and their resources. Let's start with a simple example to understand the core concepts. ![Move Blockchain Diagram](/_vercel/image?url=_astro%2Fmove_blockchain.US8AdnUd.png&w=640&q=100&dpl=dpl_D4B3ALZU3BeSgmAM8HaDXYy9SSQC) This diagram illustrates the relationship between module ownership, token ownership, and the Move blockchain state. It helps visualize how modules and resources are tied to accounts, emphasizing the unique aspects of Move's design compared to other blockchain platforms. ### What is a Move Module? [](#what-is-a-move-module) Move modules are similar to smart contracts in other blockchains, with some key differences: * **Resources:** Unlike Solidity where state is stored in contract variables, Move uses "resources" - special data types that can only exist in one place at a time and are always tied to an account * **Module-based**: Rather than deploying entire contracts as independent units like in Solidity, Move code is organized into reusable modules that can share and handle resources across boundaries. Modules are more like standard library packages that can be published together or separately, offering finer-grained control over code organization. * **Safety by design:** Move's type system and resource semantics help prevent common smart contract vulnerabilities ### Your First Move Module [](#your-first-move-module) Before we start, go to your VSCode (or Cursor) and install the [Move On Aptos](/build/smart-contracts/move-vscode-extension) VSCode extension. 1. Open VSCode (or Cursor) and navigate to the Extensions tab. 2. Search for `Move On Aptos` published by `aptoslabs` and install the extension. This extension will help us with the syntax highlighting, auto-completion, and other features that will make our development experience easier. Our first module will be a simple message storage system that allows accounts to store and retrieve messages. Let's create a new move project within our `my-first-module` folder: 1. Initialize the project Initialize a new move project with `aptos move init --name my_first_module` This creates a project structure with a `sources` directory and a `Move.toml` file. 2. Create the module file Create a new file `sources/message.move` with our module code: ``` module my_first_module::message { use std::string; use std::signer; struct MessageHolder has key, store, drop { message: string::String, } public entry fun set_message(account: &signer, message: string::String) acquires MessageHolder { let account_addr = signer::address_of(account); if (exists(account_addr)) { move_from(account_addr); }; move_to(account, MessageHolder { message }); } public fun get_message(account_addr: address): string::String acquires MessageHolder { assert!(exists(account_addr), 0); let message_holder = borrow_global(account_addr); message_holder.message }} ``` Let's break down this module: * We define a `MessageHolder` resource type that can store a string message * `set_message` allows an account to store a message * `get_message` allows anyone to retrieve a stored message * The `acquires` keyword indicates which resources the functions need access to (MessageHolder, in this case) * `move_to` and `move_from` handle the storage of resources under accounts 3. Compile the module Compile the Move module we just created with `aptos move compile --named-addresses my_first_module=default` You should see a message like this if it succeeded: ``` ❯ aptos move compile --named-addresses my_first_module=defaultCompiling, may take a little while to download git dependencies...UPDATING GIT DEPENDENCY https://github.com/aptos-labs/aptos-framework.gitINCLUDING DEPENDENCY AptosFrameworkINCLUDING DEPENDENCY AptosStdlibINCLUDING DEPENDENCY MoveStdlibBUILDING my_first_module{ "Result": [ "9ec1cfa30b885a5c9d595f32f3381ec16d208734913b587be9e210f60be9f9ba::message" ]} ``` Great job! We are now ready to test and debug. ## 4\. Testing and Debugging [](#4-testing-and-debugging) Testing and debugging are crucial parts of Move module development. Move has built-in support for unit testing and debug printing. 1. Add debug prints First, let's modify our message module to add some debug prints. Update your `sources/message.move`: ``` module my_first_module::message { use std::string; use std::signer; use std::debug; // Add this for debug prints struct MessageHolder has key, store, drop { message: string::String, } public entry fun set_message(account: &signer, message: string::String) acquires MessageHolder { let account_addr = signer::address_of(account); debug::print(&message); // Print the message being set if (exists(account_addr)) { debug::print(&string::utf8(b"Updating existing message")); // Print debug info move_from(account_addr); } else { debug::print(&string::utf8(b"Creating new message")); // Print when creating new }; move_to(account, MessageHolder { message }); } public fun get_message(account_addr: address): string::String acquires MessageHolder { assert!(exists(account_addr), 0); let message_holder = borrow_global(account_addr); debug::print(&message_holder.message); // Print the retrieved message message_holder.message }} ``` 2. Create test file Create our tests: a new file `sources/message_tests.move` with: ``` #[test_only] module my_first_module::message_tests { use std::string; use std::signer; use my_first_module::message; #[test(sender= @my_first_module)] fun test_set_and_get_message(sender: &signer) { // Test setting a message message::set_message(sender, string::utf8(b"Hello World")); // Verify the message was set correctly let stored_message = message::get_message(signer::address_of(sender)); assert!(stored_message == string::utf8(b"Hello World"), 0) } #[test(sender=@my_first_module)] fun test_update_message(sender: &signer) { // Test setting a message message::set_message(sender, string::utf8(b"Hello World")); // Test updating the message message::set_message(sender, string::utf8(b"Hello Aptos")); // Verify the message was updated correctly let stored_message = message::get_message(signer::address_of(sender)); assert!(stored_message == string::utf8(b"Hello Aptos"), 0) } } ``` 3. Run the tests Now run the tests with `aptos move test --named-addresses my_first_module=default` You should see output if the tests pass: (See below for how to handle errors) ``` INCLUDING DEPENDENCY AptosFramework INCLUDING DEPENDENCY AptosStdlib INCLUDING DEPENDENCY MoveStdlib BUILDING my_first_module Running Move unit tests [debug] "Hello World" [debug] "Creating new message" [debug] "Hello World" [ PASS ] 0x852a264419a80b27771f072b5cae8c8b358d4450e135e134e065247376a4357a::message_tests::test_set_and_get_message [debug] "Hello World" [debug] "Creating new message" [debug] "Hello Aptos" [debug] "Updating existing message" [debug] "Hello Aptos" [ PASS ] 0x852a264419a80b27771f072b5cae8c8b358d4450e135e134e065247376a4357a::message_tests::test_update_message Test result: OK. Total tests: 2; passed: 2; failed: 0 { "Result": "Success" } ``` **If you encounter errors while testing, here are some common issues and solutions:** * Make sure all module dependencies are properly imported * Check that your account address matches in the `-named-addresses` parameter * Verify that test functions have the `#[test]` attribute * Ensure string literals are properly encoded ## 5\. Publishing Your Module [](#5-publishing-your-module) After successfully compiling and testing your module, you can publish it to the Aptos blockchain. This process deploys your code so that it's accessible on-chain. 1. Publish the module Publish your module with `aptos move publish --named-addresses my_first_module=default` You'll see output showing the compilation process and then a prompt asking about gas fees: ``` Compiling, may take a little while to download git dependencies...UPDATING GIT DEPENDENCY https://github.com/aptos-labs/aptos-framework.gitINCLUDING DEPENDENCY AptosFrameworkINCLUDING DEPENDENCY AptosStdlibINCLUDING DEPENDENCY MoveStdlibBUILDING my_first_modulepackage size 1271 bytesDo you want to submit a transaction for a range of [141300 - 211900] Octas at a gas unit price of 100 Octas? [yes/no] > ``` 2. Confirm the transaction Type `y` and press Enter to confirm the transaction. After confirmation, you'll receive a response showing the transaction details: ``` { "Result": { "transaction_hash": "0x95fce7344b066abda10c07dbf1ffa83e0d9c7bd400e2b143682a6c8a5f179dc2", "gas_used": 1413, "gas_unit_price": 100, "sender": "9ec1cfa30b885a5c9d595f32f3381ec16d208734913b587be9e210f60be9f9ba", "sequence_number": 0, "success": true, "timestamp_us": 1735351260227638, "version": 273029731, "vm_status": "Executed successfully" }} ``` ### (Optional) Seeing Your Contract On-Chain [](#optional-seeing-your-contract-on-chain) After successful publication, you can verify your module is on-chain by following these steps: 1. Open the Explorer Go to the [Aptos Explorer](https://explorer.aptoslabs.com/?network=devnet) 2. Check the transaction Search for your account address. You should notice that there is a new transaction in your account, the `code::publish_package_txn` function. 3. View your balance Click the "Coins" tab to see that you now have less than 1 APT of the Aptos Coin. ![Explorer Coins View](/_vercel/image?url=_astro%2Fexplorer_coins.BHJq95xO.png&w=1280&q=100&dpl=dpl_D4B3ALZU3BeSgmAM8HaDXYy9SSQC) You have spent a small amount on gas to deploy the contract so should have around `0.99855 APT` remaining. 4. Find your module Look under the "Modules" tab ![Exporer Modules View](/_vercel/image?url=_astro%2Fexplorer_modules.BhR9Jwde.png&w=1280&q=100&dpl=dpl_D4B3ALZU3BeSgmAM8HaDXYy9SSQC) 5. Verify the module You should see your "message" module listed ## 6\. Interacting with Your Module [](#6-interacting-with-your-module) Now that your module is published, you can interact with it through the Aptos CLI: 1. Set a message Set a message using the CLI: ``` aptos move run --function-id 'default::message::set_message' --args 'string:Hello, Aptos!' ``` You'll see a gas fee prompt similar to what you saw during publishing. 2. Confirm the transaction After confirming with `y`, you should get a success response like: ``` Transaction submitted: https://explorer.aptoslabs.com/txn/0x0c0b1e56a31d037280278327eb8fdfcc469a20213e5e65accf6e7c56af574449?network=devnet{ "Result": { "transaction_hash": "0x0c0b1e56a31d037280278327eb8fdfcc469a20213e5e65accf6e7c56af574449", "gas_used": 445, "gas_unit_price": 100, "sender": "9ec1cfa30b885a5c9d595f32f3381ec16d208734913b587be9e210f60be9f9ba", "sequence_number": 1, "success": true, "timestamp_us": 1735351754495208, "version": 273137362, "vm_status": "Executed successfully" }} ``` 3. View your message View your stored message by checking under Resources on the Explorer. 4. Celebrate! We did it! ## Next Steps [](#next-steps) Congratulations! You've successfully: 1. Compiled your first Move module 2. Added tests to help debug 3. Published your module on-chain 4. Used your contract through the CLI Now your published Move module can be connected to just like an API via one of our [many Official SDKs](/build/sdks)! Here are some **suggested next steps to get a deeper understanding of Move modules**: 1. Try modifying the module to add a new feature. You can use the [Move Book](https://aptos-labs.github.io/move-book/) to build your understanding of writing Move modules. 2. To understand how Move works on-chain, you can learn about Move's [resource system](/network/blockchain/resources). 3. If you're building an application to interact with contracts or look up data from on-chain, learn how to use the SDKs [here](/build/sdks). 4. Join the [Aptos Discord](https://discord.gg/aptoslabs) to connect with other developers. ## Supporting documentation [](#supporting-documentation) * [Account basics](/network/blockchain/accounts) * [TypeScript SDK](/build/sdks/ts-sdk) * [Python SDK](/build/sdks/python-sdk) * [REST API specification](/rest-api) # Accounts > Understand Aptos accounts, their addresses, authentication schemes, key rotation, sequence numbers, and how they control assets and resources on-chain. An account on Aptos controls a set of on-chain assets, including tokens and NFTs. These assets are represented by a Move language primitive called a **resource**, which enforces both access control and scarcity. Each account is identified by a 32-byte address. You can use the [Aptos Name Service](https://www.aptosnames.com/) to register human-readable `.apt` domains for key accounts. Unlike blockchains where accounts are implicit (just an address with a balance), Aptos accounts are explicit and backed by on-chain resources that enable features like key rotation and native multisig. However, with [Stateless Accounts (AIP-115)](/build/aips/aip-115), you no longer need to set up an account before using it. Any valid address is treated as an account by default, and you can send transactions as long as you hold the private key. The on-chain `Account` resource is created automatically only when first needed (for example, when rotating keys). See [Creating an account](#creating-an-account) for how addresses are derived. Aptos accounts offer features not available on most other networks: * **Key rotation.** The account's authentication key can be changed to use a different private key, similar to changing a password. * **Native multisig.** Accounts support k-of-n multisig using Ed25519 and Secp256k1 ECDSA signature schemes. There are three types of accounts on Aptos: * _Standard account_ - This is a typical account corresponding to an address with a corresponding pair of public/private keys. * [_Resource account_](/build/smart-contracts/resource-accounts) - An autonomous account without a corresponding private key used by developers to store resources or publish modules on-chain. * [_Object_](/build/smart-contracts/objects) - A complex set of resources stored within a single address representing a single entity. ``` Alice: 0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175bBob: 0x19aadeca9388e009d136245b9a67423f3eee242b03142849eb4f81a4a409e59c ``` ## Account address [](#account-address) Currently, Aptos supports only a single, unified identifier for an account. Accounts on Aptos are universally represented as a 32-byte hex string. A hex string shorter than 32-bytes is also valid; in those scenarios, the hex string can be padded with leading zeroes, e.g., `0x1` => `0x0000000000000...01`. While Aptos standards indicate leading zeroes may be removed from an Address, most applications attempt to eschew that legacy behavior and only support the removal of zeros for special addresses ranging from `0x0` to `0xa`. ## Creating an account [](#creating-an-account) When a user requests to create an account, for example, by using the [Aptos SDK](/build/sdks/ts-sdk/account), the following steps are executed: * Select the authentication scheme for managing the user's account, e.g., Ed25519 or Secp256k1 ECDSA. * Generate a new private key, public key pair. * Combine the public key with the public key's authentication scheme to generate a 32-byte authentication key and the account address. The user should use the private key for signing the transactions associated with this account. ## Account sequence number [](#account-sequence-number) The sequence number for an account indicates the number of transactions that have been submitted and committed on-chain from that account. Committed transactions either execute with the resulting state changes committed to the blockchain or abort wherein state changes are discarded and only the transaction is stored. Every transaction submitted must contain a unique sequence number for the given sender's account. When the Aptos blockchain processes the transaction, it looks at the sequence number in the transaction and compares it with the sequence number in the on-chain account. The transaction is processed only if the sequence number is equal to or larger than the current sequence number. Transactions are only forwarded to other mempools or executed if there is a contiguous series of transactions from the current sequence number. Execution rejects out of order sequence numbers, preventing replay attacks of older transactions and guarantees ordering of future transactions. Aptos also supports [orderless transactions](/build/guides/orderless-transactions) which use a unique nonce instead of a sequence number. This enables parallel transaction submission from multiple machines without coordinating sequence numbers. See [AIP-123](https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-123-orderless-transactions.md) for the full specification. ## Authentication key [](#authentication-key) The initial account address is set to the authentication key derived during account creation. However, the authentication key may subsequently change, for example, when you generate a new public-private key pair, public keys to rotate the keys. An account address never changes. The Aptos blockchain supports the following authentication schemes: 1. [Ed25519](https://ed25519.cr.yp.to/) 2. [Secp256k1 ECDSA](https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-49.md) 3. [K-of-N multi-signatures](https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-55.md) 4. A dedicated, now legacy, MultiEd25519 scheme ### Ed25519 authentication [](#ed25519-authentication) To generate an authentication key and the account address for an Ed25519 signature: 1. **Generate a key-pair**: Generate a fresh key-pair (`privkey_A`, `pubkey_A`). The Aptos blockchain uses the PureEdDSA scheme over the Ed25519 curve, as defined in RFC 8032. 2. **Derive a 32-byte authentication key**: Derive a 32-byte authentication key from the `pubkey_A`: ``` auth_key = sha3-256(pubkey_A | 0x00) ``` where `|` denotes concatenation. The `0x00` is the 1-byte single-signature scheme identifier. 3. Use this initial authentication key as the permanent account address. ### MultiEd25519 authentication [](#multied25519-authentication) With K-of-N multisig authentication, there are a total of N signers for the account, and at least K of those N signatures must be used to authenticate a transaction. To generate a K-of-N multisig account's authentication key and the account address: 1. **Generate key-pairs**: Generate `N` ed25519 public keys `p_1`, ..., `p_n`. 2. Decide on the value of `K`, the threshold number of signatures needed for authenticating the transaction. 3. **Derive a 32-byte authentication key**: Compute the authentication key as described below: ``` auth_key = sha3-256(p_1 | . . . | p_n | K | 0x01) ``` The `0x01` is the 1-byte multisig scheme identifier. 4. Use this initial authentication key as the permanent account address. ### Generalized authentication [](#generalized-authentication) Generalized authentication supports both Ed25519 and Secp256k1 ECDSA. Like the previous authentication schemes, these schemes contain a scheme value, `0x02` and `0x03` for single and multikey respectively, but also each key contains a prefix value to indicate its key type:
Key typePrefix byte
Ed25519 generalized scheme0x00
Secp256k1Ecdsa generalized scheme0x01
Secp256r1Ecdsa WebAuthn scheme0x02
Keyless0x03
For a single key Secp256k1 ECDSA account, using public key `pubkey`, the authentication key would be derived as follows: ``` auth_key = sha3-256(0x01 | pubkey | 0x02) ``` Where * the first entry, `0x01`, represents the use of a Secp256k1 ECDSA key; * the last entry, `0x02`, represents the authentication scheme. For a 1-of-2 multi-key account containing, a single Secp256k1 ECDSA public key, `pubkey_0`, and a single Ed25519 public key, `pubkey_1`, where one signature suffices, the authentication key would be derived as follows: ``` auth_key = sha3-256(0x02 | 0x01 | pubkey_0 | 0x00 | pubkey_1 | 0x01 | 0x03) ``` Where * the first entry, `0x02`, represents the total number of keys as a single byte; * the second-to-last entry, `0x01`, represents the required number of signatures as a single byte; * the last entry, `0x03`, represents the authentication scheme. ## Rotating the keys [](#rotating-the-keys) An Account on Aptos can rotate keys so that potentially compromised keys cannot be used to access the accounts. Keys can be rotated via the `account::rotate_authentication_key` function. Refreshing the keys is generally regarded as good hygiene in the security field. However, this presents a challenge for system integrators who are used to using a mnemonic to represent both a private key and its associated account. To simplify this for the system integrators, Aptos provides an on-chain mapping via aptos account lookup-address. The on-chain data maps an effective account address as defined by the current mnemonic to the actual account address. For more information, see [`account.move`](https://github.com/aptos-labs/aptos-core/blob/a676c1494e246c31c5e96d3363d99e2422e30f49/aptos-move/framework/aptos-framework/sources/account.move#L274). ## State of an account [](#state-of-an-account) The state of each account comprises both the code (Move modules) and the data (Move resources). An account may contain an arbitrary number of Move modules and Move resources: * **Move modules**: Move modules contain code, for example, type and procedure declarations; but they do not contain data. A Move module encodes the rules for updating the Aptos blockchain's global state. * **Move resources**: Move resources contain data but no code. Every resource value has a type declared in a module published on the Aptos blockchain. ## Access control with signers [](#access-control-with-signers) The sender of a transaction is represented by a signer. When a function in a Move module takes `signer` as an argument, the Aptos Move VM translates the identity of the account that signed the transaction into a signer in a Move module entry point. See the below Move example code with `signer` in the `initialize` and `withdraw` functions. When a `signer` is not specified in a function, for example, the below `deposit` function, then no signer-based access controls will be provided for this function: ``` module Test::Coin { struct Coin has key { amount: u64 } public fun initialize(account: &signer) { move_to(account, Coin { amount: 1000 }); } public fun withdraw(account: &signer, amount: u64): Coin acquires Coin { let balance = &mut borrow_global_mut(Signer::address_of(account)).amount; *balance = *balance - amount; Coin { amount } } public fun deposit(account: address, coin: Coin) acquires Coin { let balance = &mut borrow_global_mut(account).amount; *balance = *balance + coin.amount; Coin { amount: _ } = coin; }} ``` # Transactions and States > Understand how transactions modify the blockchain state on Aptos, including transaction types, execution states, and the versioned database model. The Aptos blockchain stores three types of data: * **Transactions**: Transactions represent an intended operation being performed by an account on the blockchain (e.g., transferring assets). * **States**: The (blockchain ledger) state represents the accumulation of the output of execution of transactions, the values stored within all [resources](/network/blockchain/resources). * [**Events**](/network/blockchain/events): Ancillary data published by the execution of a transaction. ## Transactions [](#transactions) Aptos transactions contain information such as the sender’s account address, authentication from the sender, the desired operation to be performed on the Aptos blockchain, and the amount of gas the sender is willing to pay to execute the transaction. ### Transaction states [](#transaction-states) A transaction may end in one of the following states: * Committed on the blockchain and executed. This is considered as a successful transaction. * Committed on the blockchain and aborted. The abort code indicates why the transaction failed to execute. * Discarded during transaction submission due to a validation check such as insufficient gas, invalid transaction format, or incorrect key. * Discarded after transaction submission but before attempted execution. This could be caused by timeouts or insufficient gas due to other transactions affecting the account. The sender’s account will be charged gas for any committed transactions. During transaction submission, the submitter is notified of successful submission or a reason for failing validations otherwise. A transaction that is successfully submitted but ultimately discarded may have no visible state in any accessible Aptos node or within the Aptos network. A user can attempt to resubmit the same transaction to re-validate the transaction. If the submitting node believes that this transaction is still valid, it will return an error stating that an identical transaction has been submitted. The submitter can try to increase the gas cost by a trivial amount to help make progress and adjust for whatever may have been causing the discarding of the transaction further downstream. ### Contents of a Transaction [](#contents-of-a-transaction) A signed transaction on the blockchain contains the following information: * **Signature**: The sender uses a digital signature to verify that they signed the transaction (i.e., authentication). * **Sender address**: The sender's [account address](/network/blockchain/accounts#account-address). * **Sender public key**: The public authentication key that corresponds to the private authentication key used to sign the transaction. * **Payload**: Indicates an action or set of actions Alice's behalf. In the case this is a Move function, it directly calls into Move bytecode on the chain. Alternatively, it may be Move bytecode peer-to-peer [transaction script](/network/glossary#transaction-script). It also contains a list of inputs to the function or script. For this example, it is a function call to transfer an amount of Aptos Coins from Alice account to Bob's account, where Alice's account is implied by sending the transaction and Bob's account and the amount are specified as transaction inputs. * [**Gas unit price**](/network/glossary#gas-unit-price): The amount the sender is willing to pay per unit of gas, to execute the transaction. This is represented in [Octas](/network/glossary#octa). * [**Maximum gas amount**](/network/glossary#maximum-gas-amount): The [maximum gas amount](/network/blockchain/gas-txn-fee#specifying-gas-fees-within-a-transaction) in APT the sender is willing to pay for this transaction. Gas charges are equal to the base gas cost covered by computation and IO multiplied by the gas price. Gas costs also include storage with an APT-fixed priced storage model. This is represented as [Octas](/network/glossary#octa). * **Gas price** (in specified gas units): This is the amount the sender is willing to pay per unit of [gas](/network/blockchain/gas-txn-fee) to execute the transaction. [Gas](/network/blockchain/gas-txn-fee) is a way to pay for computation and storage. A gas unit is an abstract measurement of computation with no inherent real-world value. * **Sequence number**: This is an unsigned integer that must be equal to the sender's account [sequence number](/network/blockchain/accounts#account-sequence-number) at the time of execution. * **Expiration time**: A timestamp after which the transaction ceases to be valid (i.e., expires). ### Types of transaction payloads [](#types-of-transaction-payloads) Within a given transaction, the two most common types of payloads include: * An entry point * [A script (payload)](/build/smart-contracts/scripts) Currently, the SDKs [Python](/build/sdks/python-sdk) and [Typescript](/build/sdks/ts-sdk) support both. This guide points out many of those entry points, such as `coin::transfer` and `aptos_account::create_account`. All operations on the Aptos blockchain should be available via entry point calls. While one could submit multiple transactions calling entry points in series, many such operations may benefit from being called atomically from a single transaction. A script payload transaction can call any entry point or public function defined within any module. ## States [](#states) The Aptos blockchain's ledger state, or global state, represents the state of all accounts in the Aptos blockchain. Each validator node in the blockchain must know the latest version of the global state to execute any transaction. Anyone can submit a transaction to the Aptos blockchain to modify the ledger state. Upon execution of a transaction, a transaction output is generated. A transaction output contains zero or more operations to manipulate the ledger state called **write sets** emitting a vector of resulting events, the amount of gas consumed, and the executed transaction status. ### Proofs [](#proofs) The Aptos blockchain uses proof to verify the authenticity and correctness of the blockchain data. Data within the Aptos blockchain is replicated across the network. Each validator and fullnode's [storage](/network/blockchain/validator-nodes#storage) is responsible for persisting the agreed upon blocks of transactions and their execution results to the database. The blockchain is represented as an ever-growing [Merkle tree](/network/glossary#merkle-trees), where each leaf appended to the tree represents a single transaction executed by the blockchain. All operations executed by the blockchain and all account states can be verified cryptographically. These cryptographic proofs ensure that: * The validator nodes agree on the state. * The client does not need to trust the entity from which it is receiving data. For example, if a client fetches the last **n** transactions from an account, a proof can attest that no transactions were added, omitted or modified in the response. The client may also query for the state of an account, ask whether a specific transaction was processed, and so on. ### Versioned database [](#versioned-database) The ledger state is versioned using an unsigned 64-bit integer corresponding to the number of transactions the system has executed. This versioned database allows the validator nodes to: * Execute a transaction against the ledger state at the latest version. * Respond to client queries about ledger history at both current and previous versions. ## Transactions change ledger state [](#transactions-change-ledger-state) ![Signed Transaction Flow](/_astro/transactions-and-state.DnHxozEu.svg) ![Signed Transaction Flow](/_astro/transactions-and-state-dark.ffwcWBtN.svg) The above figure shows how executing transaction T_i_ changes the state of the Aptos blockchain from S_i-1_ to S_i_. In the figure: * Accounts **A** and **B**: Represent Alice's and Bob's accounts on the Aptos blockchain. * **S_i-1_** : Represents the (_i-1_)-the state of the blockchain. In this state, Alice's account **A** has a balance of 110 APT (Aptos coins), and Bob's account **B** has a balance of 52 APT. * **T_i_** : This is the _i_\-th transaction executed on the blockchain. In this example, it represents Alice sending 10 APT to Bob. * **Apply()**: This is a deterministic function that always returns the same final state for a specific initial state and a specific transaction. If the current state of the blockchain is **S_i-1_**, and transaction **T_i_** is executed on the state **S_i-1_**, then the new state of the blockchain is always **S_i_**. The Aptos blockchain uses the [Move language](https://aptos-labs.github.io/move-book/) to implement the deterministic execution function **Apply()**. * **S_i_** : This is the _i_\-the state of the blockchain. When the transaction **T_i_** is applied to the blockchain, it generates the new state **S_i_** (an outcome of applying **Apply(S_i-1_, T_i_)** to **S_i-1_** and **T_i_**). This causes Alice’s account balance to be reduced by 10 to 100 APT and Bob’s account balance to be increased by 10 to 62 APT. The new state **S_i_** shows these updated balances. ## Size limits [](#size-limits) As part of the gas schedule, there are on-chain configurable limits for the sizes of [the transaction itself](https://github.com/aptos-labs/aptos-core/blob/8074588b5c9c4424fa247c2c9ec5572981ee31cd/aptos-move/aptos-gas-schedule/src/gas_schedule/transaction.rs#L71-L81) and [its outputs](https://github.com/aptos-labs/aptos-core/blob/8074588b5c9c4424fa247c2c9ec5572981ee31cd/aptos-move/aptos-gas-schedule/src/gas_schedule/transaction.rs#L152-L177).
Limit TypeCurrent Per Transaction Limit
transaction64KB
governance transaction1MB
a single write op1MB
all write ops combined10MB
number of write ops8192
a single event1MB
all events combined10MB
# SDKs Overview > Comprehensive software development kits for building on Aptos blockchain in TypeScript, Python, Go, Rust, C#, C++, Unity and more languages ## Official SDKs [](#official-sdks) Use these Aptos software development kits (SDKs), in combination with the [Aptos CLI](/build/cli) for your development on the Aptos blockchain. [Typescript SDK](/build/sdks/ts-sdk) Aptos Typescript SDK (recommended) [Python SDK](/build/sdks/python-sdk) Aptos Python SDK [Go SDK](/build/sdks/go-sdk) Aptos Go SDK [C#/.NET SDK](/build/sdks/dotnet-sdk) Aptos .NET SDK [Rust SDK](/build/sdks/rust-sdk) Aptos Rust SDK [C++ / Unreal SDK](/build/sdks/cpp-sdk) Aptos C++ / Unreal SDK [Unity SDK](/build/sdks/unity-sdk) Aptos Unity SDK [Wallet Adapter](/build/sdks/wallet-adapter) Aptos Wallet Adapter [Forklift](/build/sdks/forklift) Testing & scripting framework for Move contracts with Network Forking support ## [Community SDKs](/build/sdks/community-sdks) [](#community-sdks) SDKs provided by the community for Aptos. These may not be fully vetted by the Aptos team, and may still be in development. They are still provided as a resource for all developers. [Kotlin SDK](/build/sdks/community-sdks/kotlin-sdk) Aptos Kotlin Multiplatform SDK by Kaptos [Swift SDK](/build/sdks/community-sdks/swift-sdk) Aptos Swift SDK by Alcove # Aptos CLI – Install, Setup, and Use the Command-Line Interface > Learn how to install, configure, and use the Aptos CLI to compile Move contracts, interact with the blockchain, run a local network, and manage nodes. The Aptos command line interface (CLI) is a tool to help you compile and test Move contracts. It can also help you quickly play with Aptos features on-chain. For more advanced users, the CLI can also be used to run a private Aptos network (to help test code locally) and can be helpful managing a network node. ## 📥 Install the Aptos CLI [](#-install-the-aptos-cli) [Mac](/build/cli/install-cli/install-cli-mac) Install Aptos CLI via homebrew [Windows](/build/cli/install-cli/install-cli-windows) Install Aptos CLI on Windows via powershell script or pre-compiled binary [Linux](/build/cli/install-cli/install-cli-linux) Install Aptos CLI on Linux via shell script or pre-compiled binary [Advanced (Install Specific Versions)](/build/cli/install-cli/install-cli-specific-version) Build a specific version of the Aptos CLI from source ## ⚙️ Setup the Aptos CLI [](#️-setup-the-aptos-cli) [Setup the CLI](/build/cli/setup-cli) Setup and configure the Aptos CLI [Advanced (Move Prover)](/build/cli/setup-cli/install-move-prover) Setup and install the Move Prover ## 🛠️ Using the Aptos CLI [](#️-using-the-aptos-cli) [Move Contracts](/build/cli/working-with-move-contracts) Compile, Publish, Simulate, and Benchmark Move Contracts [Trying things On-chain](/build/cli/trying-things-on-chain) Interact with Aptos, create accounts, query accounts, use a hardware device like Ledger [Running a Local Network](/build/cli/running-a-local-network) Run a local node / network # Smart Contracts > Learn to write secure, efficient smart contracts on Aptos using the Move programming language with examples, tutorials, and developer resources Aptos contracts are written using Move, a next generation language for secure, sandboxed, and formally verified programming which is used for multiple chains. Move allows developers to write programs that flexibly manage and transfer assets while providing security and protections against attacks on those assets. ## 📖 Learn Move [](#-learn-move) [Why Move?](/build/smart-contracts/why-move) Learn why Aptos uses the Move Language [Create Package](/build/smart-contracts/create-package) Get started by learning how to create a Move package [Objects](/build/smart-contracts/objects) Learn how to use the Object standard on Aptos to create composable and flexible primitives on chain ## 👨‍💻 Move Examples [](#-move-examples) [Aptos Move Examples](https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/move-examples) 30+ examples on how to develop Move on Aptos [Move Tutorial](https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/move-examples/move-tutorial) Covers the basics of programming with Move [Your first Move Module](/build/guides/first-move-module) A example of how to publish your first move module Here is a `hello_blockchain` example of move hello\_blockchain.move ``` module hello_blockchain::message { use std::error; use std::signer; use std::string; use aptos_framework::event; //:!:>resource struct MessageHolder has key { message: string::String, } //<:!:resource #[event] struct MessageChange has drop, store { account: address, from_message: string::String, to_message: string::String, } /// There is no message present const ENO_MESSAGE: u64 = 0; #[view] public fun get_message(addr: address): string::String acquires MessageHolder { assert!(exists(addr), error::not_found(ENO_MESSAGE)); borrow_global(addr).message } public entry fun set_message(account: signer, message: string::String) acquires MessageHolder { let account_addr = signer::address_of(&account); if (!exists(account_addr)) { move_to(&account, MessageHolder { message, }) } else { let old_message_holder = borrow_global_mut(account_addr); let from_message = old_message_holder.message; event::emit(MessageChange { account: account_addr, from_message, to_message: copy message, }); old_message_holder.message = message; } } #[test(account = @0x1)] public entry fun sender_can_set_message(account: signer) acquires MessageHolder { let addr = signer::address_of(&account); aptos_framework::account::create_account_for_test(addr); set_message(account, string::utf8(b"Hello, Blockchain")); assert!( get_message(addr) == string::utf8(b"Hello, Blockchain"), ENO_MESSAGE ); }} ``` ## ⚒️ Developer Resources [](#️-developer-resources) ### FAQ and Discussions [](#faq-and-discussions) * [Aptos Dev Discussions](https://github.com/aptos-labs/aptos-developer-discussions/discussions) for Q&A about Move. ### Move IDE plugins [](#move-ide-plugins) * Move on Aptos extension for [VSCode](https://marketplace.visualstudio.com/items?itemName=AptosLabs.move-on-aptos) and [OpenVSX](https://open-vsx.org/extension/aptoslabs/move-on-aptos). * [Move language plugin for JetBrains IDEs](https://plugins.jetbrains.com/plugin/14721-move-language) ### External Resources [](#external-resources) * [Aptos Move by Example](https://move-developers-dao.gitbook.io/aptos-move-by-example) * [Teach yourself Move on Aptos](https://github.com/econia-labs/teach-yourself-move). * [Formal Verification, the Move Language, and the Move Prover](https://www.certik.com/resources/blog/2wSOZ3mC55AB6CYol6Q2rP-formal-verification-the-move-language-and-the-move-prover) * [Collection of nestable Move resources](https://github.com/taoheorg/taohe) We have a new Move on Aptos compiler that supports Move 2. See [this page](/build/smart-contracts/compiler_v2) for more information. # Building with Objects > Learn about objects for Aptos Objects in smart contract development. In Move, Objects group resources together so they can be treated as a single entity on chain. Objects have their own address and can own resources similar to an account. They are useful for representing more complicated data types on-chain as Objects can be used in entry functions directly, and can be transferred as complete packages instead of one resource at a time. Here's an example of creating an Object and transferring it: ``` module my_addr::object_playground { use std::signer; use std::string::{Self, String}; use aptos_framework::object::{Self, ObjectCore}; struct MyStruct1 has key { message: String, } struct MyStruct2 has key { message: String, } entry fun create_and_transfer(caller: &signer, destination: address) { // Create object let caller_address = signer::address_of(caller); let constructor_ref = object::create_object(caller_address); let object_signer = object::generate_signer(&constructor_ref); // Set up the object by creating 2 resources in it move_to(&object_signer, MyStruct1 { message: string::utf8(b"hello") }); move_to(&object_signer, MyStruct2 { message: string::utf8(b"world") }); // Transfer to destination let object = object::object_from_constructor_ref( &constructor_ref ); object::transfer(caller, object, destination); }} ``` During construction, Objects can be configured to be transferrable and extensible. For example, you could use an Object to represent a soulbound NFT by making it only transferrable once, and have it own resources for an image link and metadata. Objects can also own other Objects, so you could implement your own NFT collection Object by transferring several of the soulbound NFTs to it. ## Learn how to [](#learn-how-to) * [Create and configure a new Object.](/build/smart-contracts/object/creating-objects) * [Use Objects you created.](/build/smart-contracts/object/using-objects) ## Examples with Object contracts [](#examples-with-object-contracts) * [Digital Asset Marketplace Example](https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/move-examples/marketplace) * [Digital Assets Examples](https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/move-examples/token_objects) * [Fungible Asset Examples](https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/move-examples/fungible_asset) # Randomness API > Learn about randomness for Move smart contract development on Aptos blockchain. ## What does it do: a quick example [](#what-does-it-do-a-quick-example) ### How random numbers have been obtained, insecurely/awkwardly [](#how-random-numbers-have-been-obtained-insecurelyawkwardly) Building a lottery system and pick a random winner from `n` participants is trivial, at least in the centralized world with a trusted server: the backend simply calls a random integer sampling function (`random.randint(0, n-1)` in python, or `Math.floor(Math.random() * n)` in JS). Unfortunately, without an equivalent of `random.randint()` in Aptos Move, building a dApp version of it was actually much harder. One may have written a contract where the random numbers are sampled insecurely (e.g., from the blockchain timestamp): ``` module module_owner::lottery { // ... struct LotteryState { players: vector
, winner_idx: std::option::Option, } fun load_lottery_state_mut(): &mut LotteryState { // ... } entry fun decide_winner() { let lottery_state = load_lottery_state_mut(); let n = std::vector::length(&lottery_state.players); let winner_idx = aptos_framework::timestamp::now_microseconds() % n; lottery_state.winner_idx = std::option::some(winner_idx); }} ``` The implementation above is insecure in multiple ways: * a malicious user may bias the result by picking the transaction submission time; * a malicious validator can bias the result easily by selecting which block the `decide_winner` transaction goes to. Other dApps may have chosen to use an external secure randomness source (e.g., [drand](https://drand.love/)), which is typically a complicated flow: 1. The participants agree on using a future randomness seed promised by the randomness source to determine the winner. 2. Once the randomness seed is revealed, the clients fetch it and derive the winner locally. 3. One of the participants submits the seed and the winner on chain. ``` module module_owner::lottery { // ... struct LotteryState { players: vector
, /// public info about the "future randomness", tyipcally a VRF public key and an input. seed_verifier: vector, winner_idx: std::option::Option, } fun load_lottery_state_mut(): &mut LotteryState { // ... } fun is_valid_seed(seed_verifier: vector, seed: vector): bool { // ... } fun derive_winner(n: u64, seed: vector): u64 { // ... } entry fun update_winner(winner_idx: u64, seed: vector) { let lottery_state = load_lottery_state_mut(); assert!(is_valid_seed(lottery_state.seed_verifier, seed), ERR_INVALID_SEED); let n = std::vector::length(players); let expected_winner_idx = derive_winner(n, seed); assert!(expected_winner_idx == winner_idx, ERR_INCORRECT_DERIVATION); lottery_state.winner_idx = std::option::some(winner_idx); }} ``` ### Achieve simplicity + security with Aptos randomness API [](#achieve-simplicity--security-with-aptos-randomness-api) Using Aptos randomness API, the implementation will look like this: ``` module module_owner::lottery { // ... use aptos_framework::randomness; struct LotteryState { players: vector
, winner_idx: std::option::Option, } fun load_lottery_state_mut(): &mut Lottery { // ... } #[randomness] entry fun decide_winner() { let lottery_state = load_lottery_state_mut(); let n = vector::length(&lottery_state.players); let winner_idx = aptos_framework::randomness::u64_range(0, n); lottery_state.winner_idx = std::option::some(winner_idx); }} ``` where: * `let winner_idx = aptos_framework::randomness::u64_range(0, n);` is the randomness API call that returns an u64 integer in range `[0, n)` uniformly at random. * `#[randomness]` is a required attribute to enable the API call at runtime. ## How to use Aptos randomness API [](#how-to-use-aptos-randomness-api) ### Prerequisites [](#prerequisites) Ensure you have the latest [aptos-cli](/build/cli) installed. ### Keep undergasing attacks in mind [](#keep-undergasing-attacks-in-mind) Caution **The randomness API currently does not prevent undergasing attacks.** Carefully read the undergasing section to understand about undergasing attacks and how to prevent them. As a dApp developer, you will need to design applications using randomness with safety in mind. ### Identify randomness-dependent entry functions and make them compliant [](#identify-randomness-dependent-entry-functions-and-make-them-compliant) For safety (discussed with more details later), randomness API calls are only allowed from an entry function that is: * private, and * annotated with `#[randomness]`. It's now a good time to think about what user actions need randomness API, write them down, and make sure they are private and have the right attribute, as shown in the example below. ``` module module_owner::lottery { // ... #[randomness] entry fun decide_winner() { // ... }} ``` At runtime, when randomness API is called, the VM checks whether the outermost of the callstack is a private entry function with `#[randomness]` attribute. **If not, the entire transaction is aborted.** ### Call the API [](#call-the-api) The APIs are public functions under `0x1::randomness` and can be referenced directly, as demonstrated in the lottery example above. ``` module module_owner::lottery { // ... use aptos_framework::randomness; #[randomness] entry fun decide_winner() { // ... let winner_idx = aptos_framework::randomness::u64_range(0, n); lottery_state.winner_idx = std::option::some(winner_idx); }} ``` The above example uses function `u64_range()` but many other basic types are also supported. Here's a quick overview of all the API, where `T` can be one of `u8, u16, u32, u64, u128, u256`. ``` module aptos_framework::randomness { /// Generates an number uniformly at random. fun u8_integer(): u8 {} /// Generates an number uniformly at random. fun u16_integer(): u16 {} // fun u32_integer(), fun u64_integer() ... /// Generates a number `[min_incl, max_excl)` uniformly at random. fun u8_range(min_incl: u8, max_excl: u8): u8 {} /// Generates a number `[min_incl, max_excl)` uniformly at random. fun u16_range(min_incl: u16, max_excl: u16): u16 {} // fun u32_range(), fun u64_range() ... /// Generates a sequence of bytes uniformly at random /// n is the number of bytes /// If n is 0, returns the empty vector. fun bytes(n: u64): vector {} /// Generate a permutation of `[0, 1, ..., n-1]` uniformly at random. /// n is the number of bytes /// If n is 0, returns the empty vector. fun permutation(n: u64): vector {}} ``` The full API function list and documentation can be found [here](https://github.com/aptos-labs/aptos-core/blob/main/aptos-move/framework/aptos-framework/doc/randomness.md). ## Security considerations [](#security-considerations-1) Randomness API is powerful in many ways: it unlocks new dApp designs; but if used incorrectly, it may leave your dApps open to attacks! Below are some common mistakes you should avoid. ### Randomness API calls in public functions [](#randomness-api-calls-in-public-functions) As your dApp gets more complicated, you may have multiple entry functions that need to share the same randomness-dependent logic, and want to pull the logic out as a separate helper function. While this is supported as shown below, extra care must be taken. ``` module module_owner::lottery { // ... use aptos_framework::randomness; #[randomness] entry fun decide_winner_v0() { // ... decide_winner_internal(lottery_state); } #[randomness] entry fun decide_winner_v1() { // ... decide_winner_internal(lottery_state); } // A private helper function fun decide_winner_internal(lottery_state: &mut lottery_state) { let n = std::vector::length(&lottery_state.players); let winner_idx = aptos_framework::randomness::u64_range(0, n); lottery_state.winner_idx = std::option::some(winner_idx); }} ``` If `decide_winner_internal()` were accidentally marked public, malicious players can deploy their own contract to: 1. call`decide_winner_internal()`; 2. read the lottery result (assuming the `lottery` module has some getter functions for the result); 3. abort if the result is not in their favor. By repeatedly calling their own contract until a txn succeeds, malicious users can bias the uniform distribution of the winner (dApp developer's initial design). This is referred to as a [test-and-abort attack](https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-41.md#test-and-abort-attacks). The Aptos Move compiler has been updated to prevent this attack for your contract safety: a randomness-dependent public function is treated as a compile error. If you have finished the steps in the ["build Aptos CLI"](/build/cli) section, then your Aptos CLI are equipped with the updated compiler. ``` module module_owner::lottery { // Compile error! public fun decide_winner_internal(lottery_state: &mut lottery_state) { let n = std::vector::length(&lottery_state.players); let winner_idx = aptos_framework::randomness::u64_range(0, n); lottery_state.winner_idx = std::option::some(winner_idx); }} ``` Not recommended, but if you intend to expose such a randomness-dependent function to the public, you can bypass the compiler check by annotating your function with `#[lint::allow_unsafe_randomness]`. ``` module module_owner::lottery { // Can compile, but use it at your own risk! #[lint::allow_unsafe_randomness] public fun decide_winner_internal(lottery_state: &mut lottery_state) { let n = std::vector::length(&lottery_state.players); let winner_idx = aptos_framework::randomness::u64_range(0, n); lottery_state.winner_idx = std::option::some(winner_idx); }} ``` ### Undergasing attacks, and how to prevent [](#undergasing-attacks-and-how-to-prevent) Imagine such a dApp. It defines a private entry function for a user to: 1. toss a coin (gas cost: 9), then 2. get a reward (gas cost: 10) if coin=1, or do some cleanup (gas cost: 100) otherwise. A malicious user can control its account balance, so it covers at most 108 gas units (or set transaction parameter `max_gas=108`), and the cleanup branch (total gas cost: 110) will always abort with an out-of-gas error. The user then repeatedly call the entry function until it gets the reward. Formally, this is referred to as an [undergasing attack](https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-41.md#undergasing-attacks), where an attacker can control how much gas is left for the entry function to execute, and so can arbitrarily decide to abort paths that cost more gas, biasing the outcome (i.e., effectively changing the distribution of random numbers). Caution **WARNING: randomness API currently does not prevent undergasing attacks.** As a dApp developer, you need to be very careful in your design to avoid this type of attack. Here are some ideas of how to prevent undergasing attack generally. * Make your entry function gas independent from the randomness outcome. The simplest example is to not "act" on the randomness outcome, i.e., read it and store it for later. Note that calling any other functions can have variable gas costs. For example, when calling randomness to decide which player should win, and then depositing the winnings to the winner might seem like a fixed gas cost. But, `0x1::coin::transfer` / `0x1::fungible_asset::transfer` can have a variable cost based on the user's on-chain state. * If your dApp involves a trusted admin/admin group, only allow the trusted to execute randomness transaction (i.e., require an admin signer). * Make the path that is most beneficial have the highest gas (as attacker can only abort paths with gas above a threshold he chooses. NOTE: that this can be tricky to get right, and gas schedule can change, and is even harder to get right when there are more than 2 possible outcomes. Note that everything that does not fall in above categories can be susceptible to undergasing attack in a subtle ways. Reach out if you need help. We will be providing more functionality in the future, to allow for more complex code to be able to be safe against undergasing attacks. ### It's random, but not a secret [](#its-random-but-not-a-secret) While the randomness API mimics the standard libraries you use to implement a private centralized server, keep in mind that **the seed is public, and so is your transaction execution**, and not every randomness-dependent logic in your private centralized server can be transferred on chain safely, **especially when it involves a secret that only the server should see**. For example, in your contract, DO NOT try to do the following. * Use randomness API to generate an asymmetric key pair, discard the private key, then think the public key is safe. * Use randomness API to shuffle some opened cards, veil them, and think no one knows the permutation. ## Read more [](#read-more) [Aptogotchi Random Mint](https://github.com/aptos-labs/aptogotchi-random-mint/tree/main) is an official demo dApp built to demonstrate the use of randomness API. The full API function list and documentation can be found [here](https://github.com/aptos-labs/aptos-core/blob/main/aptos-move/framework/aptos-framework/doc/randomness.md). You can also find the partial implementation of the API functions and example unit tests [here](https://github.com/aptos-labs/aptos-core/blob/main/aptos-move/framework/aptos-framework/sources/randomness.move). See [AIP-41](https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-41.md) for the API design, and [AIP-79](https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-79.md) if you are interested in system-level/cryptography details. # Aptos APIs > Access the Aptos blockchain through various APIs including REST API, GraphQL, and specialized endpoints for different use cases The Aptos Blockchain network can be accessed by several APIs, depending on your use-case. ## Aptos Fullnode [](#aptos-fullnode) This API - embedded into Fullnodes - provides a simple, low latency, yet low-level way of _reading_ state and _submitting_ transactions to the Aptos Blockchain. It also supports transaction simulation. [Aptos Fullnode REST API (Mainnet)](/build/apis/fullnode-rest-api?network=mainnet) Mainnet API playground for Aptos Fullnode REST API [Aptos Fullnode REST API (Testnet)](/build/apis/fullnode-rest-api?network=testnet) Testnet API playground for Aptos Fullnode REST API [Aptos Fullnode REST API (Devnet)](/build/apis/fullnode-rest-api?network=devnet) Devnet API playground for Aptos Fullnode REST API ## Indexer [](#indexer) [Indexer GraphQL API](/build/indexer) This GraphQL API offers a high-level, opinionated GraphQL interface to read state from the Aptos Blockchain. It's ideal for interacting with NFTs, Aptos Objects, or custom Move contracts. Learn more about the Indexer-powered GraphQL API here. [Transaction Stream API](/build/indexer/txn-stream) This GRPC API streams historical and real-time transaction data to an indexing processor. It's used by Aptos Core Indexing and can also support custom app-specific indexing processors for real-time blockchain data processing. Learn more here. ## Faucet (Only Testnet/Devnet) [](#faucet-only-testnetdevnet) [Faucet API](/build/apis/faucet-api) This API provides the ability to receive test tokens on devnet. Its primary purpose is the development and testing of applications and Move contracts before deploying them to mainnet. On testnet you can mint at the mint page. The code of each of the above-mentioned APIs is open-sourced on [GitHub](https://github.com/aptos-labs/aptos-core). As such anyone can operate these APIs and many independent operators and builders worldwide choose to do so. ### Aptos Labs operated API Deployments [](#aptos-labs-operated-api-deployments) [Aptos Labs](https://aptoslabs.com) operates a deployment of these APIs on behalf of [Aptos Foundation](https://aptosnetwork.com/foundation) for each [Aptos Network](/network/nodes/networks) and makes them available for public consumption. These APIs allow for limited access on a per-IP basis without an API key (anonymous access). To get much higher rate limits you can sign up for an [Geomi](https://geomi.dev/) account. # Fullnode REST API > Low-level REST API for reading state, submitting transactions, and simulating operations on the Aptos blockchain This API - embedded into Fullnodes - provides a simple, low latency, yet low-level way of reading state and submitting transactions to the Aptos Blockchain. It also supports transaction simulation. For more advanced queries, we recommend using the [Indexer GraphQL API](/build/indexer). ## Fullnode REST API Explorer [](#fullnode-rest-api-explorer) [Mainnet Fullnode REST API](https://fullnode.mainnet.aptoslabs.com/v1/spec#/) REST API Explorer for Mainnet [Testnet Fullnode REST API](https://fullnode.testnet.aptoslabs.com/v1/spec#/) REST API Explorer for Testnet [Devnet Fullnode REST API](https://fullnode.devnet.aptoslabs.com/v1/spec#/) REST API Explorer for Devnet ## Understanding rate limits [](#understanding-rate-limits) As with the [Aptos Indexer](/build/indexer/indexer-api), the Aptos REST API has rate limits based on compute units. You can learn more about how the ratelimiting works by reading the [Geomi docs](https://geomi.dev/docs/admin/billing). ## Viewing current and historical state [](#viewing-current-and-historical-state) Most integrations into the Aptos blockchain benefit from a holistic and comprehensive overview of the current and historical state of the blockchain. Aptos provides historical transactions, state, and events, all the result of transaction execution. * Historical transactions specify the execution status, output, and tie to related events. Each transaction has a unique version number associated with it that dictates its global sequential ordering in the history of the blockchain ledger. * The state is the representation of all transaction outputs up to a specific version. In other words, a state version is the accumulation of all transactions inclusive of that transaction version. * As transactions execute, they may emit events. [Events](/network/blockchain/events) are hints about changes in on-chain data. The storage service on a node employs two forms of pruning that erase data from nodes: * state * events, transactions, and everything else While either of these may be disabled, storing the state versions is not particularly sustainable. Events and transactions pruning can be disabled via setting the [`enable_ledger_pruner`](https://github.com/aptos-labs/aptos-core/blob/cf0bc2e4031a843cdc0c04e70b3f7cd92666afcf/config/src/config/storage_config.rs#L141) to `false` in `storage_config.rs`. This is default behavior in Mainnet. In the near future, Aptos will provide indexers that mitigate the need to directly query from a node. The REST API offers querying transactions and events in these ways: * [Transactions for an account](https://api.devnet.aptoslabs.com/v1/spec#/operations/get_account_transactions) * [Transactions by version](https://api.devnet.aptoslabs.com/v1/spec#/operations/get_transaction_by_version) * [Events by event handle](https://api.devnet.aptoslabs.com/v1/spec#/operations/get_events_by_event_handle) ## Reading state with the View function [](#reading-state-with-the-view-function) View functions do not modify blockchain state when called from the API. A [View function](https://github.com/aptos-labs/aptos-core/blob/main/api/src/view_function.rs) and its [input](https://github.com/aptos-labs/aptos-core/blob/main/api/types/src/view.rs) can be used to read potentially complex on-chain state using Move. For example, you can evaluate who has the highest bid in an auction contract. Here are related files: * [`view_function.rs`](https://github.com/aptos-labs/aptos-core/blob/main/api/src/tests/view_function.rs) for an example * related [Move](https://github.com/aptos-labs/aptos-core/blob/90c33dc7a18662839cd50f3b70baece0e2dbfc71/aptos-move/framework/aptos-framework/sources/coin.move#L226) code * [specification](https://github.com/aptos-labs/aptos-core/blob/90c33dc7a18662839cd50f3b70baece0e2dbfc71/api/doc/spec.yaml#L8513). The view function operates like the Aptos simulation API, though with no side effects and an accessible output path. View functions can be called via the `/view` endpoint. Calls to view functions require the module and function names along with input type parameters and values. A function does not have to be immutable to be tagged as `#[view]`, but if the function is mutable it will not result in state mutation when called from the API. If you want to tag a mutable function as `#[view]`, consider making it private so that it cannot be maliciously called during runtime. In order to use the View functions, you need to [publish the module](/build/cli/working-with-move-contracts) through the [Aptos CLI](/build/cli). In the Aptos CLI, a view function request would look like this: ``` aptos move view --function-id devnet::message::get_message --profile devnet --args address:devnet{ "Result": [ "View functions rock!" ]} ``` In the TypeScript SDK, a view function request would look like this: ``` import { Aptos } from "@aptos-labs/ts-sdk"; const aptos = new Aptos();const [balance] = aptos.view<[string]>({ function: "0x1::coin::balance", typeArguments: ["0x1::aptos_coin::AptosCoin"], functionArguments: [alice.accountAddress]}); expect(balance).toBe("100000000"); ``` The view function returns a list of values as a vector. By default, the results are returned in JSON format; however, they can be optionally returned in Binary Canonical Serialization (BCS) encoded format. # Exchange Integration Guide > Comprehensive guide for integrating Aptos and its assets into cryptocurrency exchanges with balance tracking and testing. This describes how to integrate Aptos and Aptos assets into an exchange. It provides generic information for tracking balances, transferring assets, and testing the integration. ## Overview [](#overview) This document will guide you through the following tasks to integrate with Aptos: * Infrastructure * Address standards * Asset standards * Retrieving balances * Tracking balance changes * Transferring assets * Testing the integration ## Infrastructure [](#infrastructure) It's suggested that you run your own [full node](/network/nodes/full-node) to interact with the Aptos blockchain. This will allow you to query the blockchain for the latest state and submit transactions. You can also use the [Indexer](/build/indexer) to query for on-chain data efficiently. ## Address Standards [](#address-standards) ### Addresses [](#addresses) A single address can be represented in three ways. We recommend you show all leading zeros, and the `0x`. Here is an example of all three representations for the framework address `0x1`: * `0x00000000000000000000000000000001` - A full representation of 32-bytes in hex with a leading `0x`. This is preferred. * `0x1` - The short representation of the address with a leading `0x`. This is kept around for compatibility, but preferred with all leading 0s. * `00000000000000000000000000000001` - A full representation of 32-bytes in hex without a leading `0x`. This is kept around for compatibility, but preferred with leading 0x. For example SDKs will handle this parsing automatically, and we suggest you use the SDKs directly to handle it for you. ``` import { AccountAddress } from "@aptos-labs/ts-sdk";const address = AccountAddress.from("0x1");address.toStringLong(); // 0x00000000000000000000000000000001 ``` There is additionally, Aptos Name Service (ANS) for friendly .apt names. For more information about addresses and Aptos Names, see our page on [Accounts](/network/blockchain/accounts). ## Account Standards [](#account-standards) Accounts must exist prior to sending a transaction to the blockchain. This is done by creating an account resource, which can be created by simply calling `0x1::aptos_account::transfer` with a zero amount to the account you want to create. Optionally, `0x1::aptos_account::create_account` can be used to create an account with a zero balance. ``` import { Aptos, Ed25519Account, Ed25519PrivateKey } from "@aptos-labs/ts-sdk"; const aptos = new Aptos();const account = new Ed25519Account({privateKey: new Ed25519PrivateKey("private key")})const transaction = await aptos.transferCoinTransaction({sender: account.accountAddress, recipient: "receiver address", amount: 100000000})const pendingTransaction = await aptos.transaction.signAndSubmitTransaction({signer: account, transaction})const committedTransaction = await aptos.waitForTransaction({transactionHash: pendingTransaction.hash}); ``` ## Asset Standards [](#asset-standards) Aptos provides two standards for fungible tokens, similar to ERC-20 tokens on Ethereum: * An earlier [Coin standard](/build/smart-contracts/aptos-coin) used by assets on Aptos. * A newer [Fungible Asset Standard](/build/smart-contracts/fungible-asset) which is more featured. Additionally, there is a migratory period for assets from Coin to Fungible Asset standards. We will call this from now on **migrated coins**. Migrated coins may have two forms, but either can be used interchangeably with Coin standards. This is important to note when querying balances, to use coin functions and not fungible asset functions. The FA standard can only deal with the FA form. ### Coin Standard (tl;dr) [](#coin-standard-tldr) A **coin** has an associated contract that holds the on-chain struct that represents the coin. The coin is represented as a struct name e.g. `0x1::aptos_coin::AptosCoin` for `APT`. All coins are stored in an account resource called `0x1::coin::CoinStore`. Coins must be registered prior to using the `CoinStore`, but if using the proper functions e.g. `0x1::aptos_account::transfer` or `0x1::aptos_account::transfer_coins`, this will be done automatically. Coins can be _migrated_ to a fungible asset. In order to support a migrated asset, continue calling the coin functions as will be mentioned later. More info can be found at: [Coin Standard](/build/smart-contracts/aptos-coin) ### Fungible Asset Standard (tl;dr) [](#fungible-asset-standard-tldr) A **fungible asset** has an associated metadata address that holds the metadata for the fungible asset. This is commonly called the fa metadata address. The asset is represented as an address e.g. `0xA` for `APT`. All fungible assets are stored in an `object`, which is called a `fungible asset store`. For exchanges, the most important store is `primary_fungible_store`, which is the default store for fungible assets. This is directly connected to an owner. From this point on in this guide, we will only talk about supporting `primary_fungible_store` for fungible assets. More info can be found at: [Fungible Asset Standard](/build/smart-contracts/fungible-asset) ## Retrieving Balances [](#retrieving-balances) Retrieving current balances for assets are different for each standard. Integration is considered complete when it can handle both. Balances are always returned in their subunits. For example, `APT` is returned in `octas` (1e-8 APT). So, when an API returns a balance of `100000000`, this is `1 APT`. If it returns `100`, this is `0.000001 APT`. ### Coin (and migrated coins) Balances [](#coin-and-migrated-coins-balances) To retrieve the balance of a coin, or a coin that was migrated to a fungible asset, you can use the `0x1::coin::balance(account address)` view function. This will combine the coin and coin migrated to fungible asset balances. ``` import { Aptos, AptosConfig, Network } from "@aptos-labs/ts-sdk"; const config = new AptosConfig({ network: Network.DEVNET });const aptos = new Aptos(config); const coinType = "0x1::aptos_coin::AptosCoin";const account = "0x00000000000000000000000000000001";const [balanceStr] = await aptos.view<[string]>({ payload: { function: "0x1::coin::balance", typeArguments: [coinType], functionArguments: [account] }});const balance = parseInt(balanceStr, 10); ``` A specific ledger version (transaction height) can be provided to get the balance at that point in time. The below example shows for ledger version `1,000,000`. ``` import { Aptos, AptosConfig, Network } from "@aptos-labs/ts-sdk"; const config = new AptosConfig({ network: Network.DEVNET });const aptos = new Aptos(config); const coinType = "0x1::aptos_coin::AptosCoin";const account = "0x00000000000000000000000000000001";const [balanceStr] = await aptos.view<[string]>({ payload: { function: "0x1::coin::balance", typeArguments: [coinType], functionArguments: [account], options: { ledgerVersion: 1_000_000 } }});const balance = parseInt(balanceStr, 10); ``` ### Fungible Asset Balances [](#fungible-asset-balances) To retrieve the balance of a fungible asset, you can use the `0x1::primary_fungible_store::balance<0x1::object::ObjectCore>(account address, fungible asset metadata address)` view function. Note, that this will not include the balance of coins if it's a migrated coin. ``` import { Aptos, AptosConfig, Network } from "@aptos-labs/ts-sdk"; const config = new AptosConfig({ network: Network.DEVNET });const aptos = new Aptos(config); const faMetadataAddress = "0xA";const account = "0x00000000000000000000000000000001";const [balanceStr] = await aptos.view<[string]>({ payload: { function: "0x1::primary_fungible_store::balance", typeArguments: ["0x1::object::ObjectCore"], functionArguments: [account, faMetadataAddress] }});const balance = parseInt(balanceStr, 10); ``` A specific ledger version (transaction height) can be provided to get the balance at that point in time. The below example shows for ledger version `1,000,000`. ``` import { Aptos, AptosConfig, Network } from "@aptos-labs/ts-sdk"; const config = new AptosConfig({ network: Network.DEVNET });const aptos = new Aptos(config); const faMetadataAddress = "0xA";const account = "0x00000000000000000000000000000001";const [balanceStr] = await aptos.view<[string]>({ payload: { function: "0x1::primary_fungible_store::balance", typeArguments: ["0x1::object::ObjectCore"], functionArguments: [account, faMetadataAddress] }, options: { ledgerVersion: 1_000_000 }});const balance = parseInt(balanceStr, 10); ``` Besides SDK, you can also directly use aptos node's [balance API endpoint](/build/apis/fullnode-rest-api#tag/accounts/GET/accounts/%7Baddress%7D/balance/%7Basset_type%7D) to get the balance of a migrated coin or fungible asset. ## Tracking Balance Changes [](#tracking-balance-changes) Balance changes can be queried in one of two ways: 1. By watching for events that change the balance for each transaction. 2. By querying the indexer for indexed balance change events. In the past, it was able to use the `events` endpoint for an account to get the transactions that changed the balance. This is still possible, but will be deprecated in the future, and is not recommended for new integrations. ### Coin Balance Changes [](#coin-balance-changes) Coin balances are tracked as two items, write set changes, and events. Write set changes are end state of the coin balance, and events are the events that are emitted when a coin is withdrawn or deposited. Here is an [example of a coin transfer](https://explorer.aptoslabs.com/txn/1747361321?network=mainnet). The coin transfer can be tracked as an individual transaction [here](https://fullnode.mainnet.aptoslabs.com/v1/transactions/by_version/1747361321) from the REST API. We'll break it down into a few parts: 1. The general transaction details tell information about the transaction. The most important thing here is the transaction version is `1747361321`. This gives us total order of all transactions on the blockchain. Think of it like block height, but for transactions. 2. The Write set `changes` are the end state of the transaction. It shows all resources that were modified by the transaction, and what it's final state was. In this case, we only care about coin store changes. 3. Events are the events that were emitted by the transaction. In this case, we only care about the `0x1::coin::Withdraw` and `0x1::coin::Deposit` events. The Coin withdraw event is emitted when coins are withdrawn from an account. The account's balance will decrease by that amount in the field `data.amount`. To determine the matching asset, you must match the `guid` in the `withdraw_events` to the `guid` in the `changes` section for a `CoinStore`. But if the `CoinStore` is not found in the `changes`, it means it got deleted, and a `CoinStoreDeleteEvent` must be present instead. Then you can match the `guid` with `deleted_withdraw_event_handle_creation_number` and `event_handle_creation_address`. The Coin deposit event is emitted when coins are deposited into an account. The account's balance will increase by that amount in the field `data.amoount`. To determine the matching asset, you must match the `guid` in the `deposit_events` to the `guid` in the `changes` section for a `CoinStore`. Similarly, if the `CoinStore` is not found in the `changes`, it means it got deleted, and a `CoinStoreDeleteEvent` must be present instead. Then you can match the `guid` with `deleted_deposit_event_handle_creation_number` and `event_handle_creation_address`. 4. Gas usage only is tracked for APT. There is no direct event for tracking gas, but it can be calculated from the transaction. Using the `gas_used` field, and the `gas_unit_price` field, you can calculate the total gas used. In this case, the `gas_used` is `999` and the `gas_unit_price` is `100`, so the total gas deducted from the sender(`0x559d4f690c683fca7c539237aa8dc4c6ec09886b7016bf66f2cdeffef55468f0`) is `999 * 100 = 99900 subunits` Remember that the subunits are used here. The value in the gas token `APT` is `0.00099900 APT`. 5. Overall, you need both the events and the changes to determine the amount transferred of the account. The final balances will show in the changes alone. If you watch all of these events, you will be able to handle all possible transactions. Below is the full example of the transaction response. ### Fungible Asset Balance Changes [](#fungible-asset-balance-changes) For fungible assets, the balance changes are tracked in the `primary_fungible_store`. The primary fungible store address is deterministic, and will always be tracked by the owner of the store. An example: [https://api.mainnet.aptoslabs.com/v1/transactions/by\\\_version/1750174030](https://api.mainnet.aptoslabs.com/v1/transactions/by%5C_version/1750174030) There are a few steps when tracking fungible assets: 1. There will be two types of events for fungible assets. `0x1::fungible_asset::Deposit` and `0x1::fungible_asset::Withdraw`. `Withdraw` events are similar to the coin events, where the balance will decrease by the amount in the `data.amount` field. And similarly `Deposit` events will increase the balance by the amount in the `data.amount` field. Note that, I've omitted the sequence number, and GUID fields, as they do not apply to module events. Each event has a `store` field, which in this case is `0x8a9d57692a9d4deb1680eaf107b83c152436e10f7bb521143fa403fa95ef76a`. This is the address of the `FungibleStore` for the asset, where the balance is stored. Note this, for the next step. 2. Next, we take a look at the `0x1::fungible_asset::FungibleStore` changes. This will show the end state of the balance for the fungible asset. The balance is in the `data.balance` field. The `address` field will match the `store` field from the events. The identifier of the fungible asset, is the `metadata` field. It is the address of the `metadata` for the fungible asset. Additionally, to figure out the actual owner of the assets, you will need to look at the owner of the store. In this case, you will need the `0x1::object::ObjectCore`, where the `address` field matches the `store` field from the events. The `owner` field will show the asset owner's address. similar to the coin events, if the `ObjectCore` is not found in the `changes`, it means it got deleted, and a `FungibleStoreDeletion` event must be present instead. Then you can match the `store` fields between the `Withdraw`/`Deposit` events and the `FungibleStoreDeletion` event. ### Coins migrated to Fungible Asset Balance Changes [](#coins-migrated-to-fungible-asset-balance-changes) For coins migrated to fungible assets, it is just simply tracking of the two above. A coin migrated to a fungible asset will have both the coin store changes and the primary fungible asset store changes. The amounts would need to be aggregated together, and otherwise, handled as a coin. The Fungible asset metadata address is the hash of the coin type and 0xA ``` address = sha3_256(0xA | coin_type | 0xFE) ``` Here is an example of a migrated coin with APT: [https://api.mainnet.aptoslabs.com/v1/transactions/by\\\_version/1642580695](https://api.mainnet.aptoslabs.com/v1/transactions/by%5C_version/1642580695) ## Transferring Assets [](#transferring-assets) ### Coin (or migrated coin) Transfers [](#coin-or-migrated-coin-transfers) We suggest you use `0x1::aptos_account::transfer_coins(receiver address, amount)` for transferring coins. It will register the coin if it hasn't been registered yet, and create the associated account if it hasn't been created yet. This will continue to work with any coins that were migrated to a fungible asset, including APT. Coins can be transferred in the following ways: * [`0x1::aptos_account::transfer_coins(receiver address, amount)`](https://github.com/aptos-labs/aptos-core/blob/main/aptos-move/framework/aptos-framework/sources/aptos_account.move#L108-L112) - Transfer a coin to another account. * [`0x1::aptos_account::batch_transfer_coins(receiver addresses, amounts)`](https://github.com/aptos-labs/aptos-core/blob/main/aptos-move/framework/aptos-framework/sources/aptos_account.move#L93-L106) - Transfer a coin to multiple accounts. * [`0x1::aptos_account::transfer(receiver address, amount)`](https://github.com/aptos-labs/aptos-core/blob/main/aptos-move/framework/aptos-framework/sources/aptos_account.move#L74-L91) - Transfer specifically APT to another account. ### Fungible Asset Transfers [](#fungible-asset-transfers) We suggest you use `0x1::primary_fungible_store::transfer<0x1::object::ObjectCore>(receiver address, amount)` for transferring fungible assets. It will send the associated fungible asset, and create a primary store for the asset if it hasn't been created yet. Caution Note: This will not create an account for the user if it hasn't been created yet. You will need to call `0x1::aptos_account::create_account(account address)` to create the account before the user can submit transactions. ## Testing [](#testing) In order to check that everything is working correctly, we've provided these checks. ### Balance Checks [](#balance-checks) To test balance checks, you can check the balance for the account `0x5` for the asset `0x1::aptos_coin::AptosCoin`. The balance should show `0.002 APT`, where 0.001 APT is a coin, and 0.001 APT is a migrated coin (fungible asset). If your balance is not correct, see [Coin and Migrated Coin Balances](#coin-and-migrated-coins-balances) for more information. ### Balance Change / Transfer Checks [](#balance-change--transfer-checks) #### Check Coin Transfer [](#check-coin-transfer) To test a transfer, create a transaction to transfer 0.001 APT to another account. The transaction should be successful, and the balance should be updated, where the balance is 0.001 APT smaller and minus the gas cost associated. #### Check Fungible Asset Transfer [](#check-fungible-asset-transfer) To test a transfer, you can fund an account with the fungible asset here [https://test-token-faucet.vercel.app/](https://test-token-faucet.vercel.app/) and then transfer the fungible asset to another account. The balance should be updated according to the change, and you should be able to track the mint on the website. ## Stablecoin Addresses [](#stablecoin-addresses)
Token NameToken SymbolToken AddressSource of Address
Tether USDUSDt0x357b0b74bc833e95a115ad22604854d6b0fca151cecd94111770e5d6ffc9dc2bAptos Foundation
USDCUSDC0xbae207659db88bea0cbead6da0ed00aac12edcdda169e591cd41c94180b46f3bCircle
Ondo US Dollar YieldUSDY0xcfea864b32833f157f042618bd845145256b1bf4c0da34a7013b76e42daa53cc::usdy::USDYOndo Finance
## FAQ [](#faq) ### What is the finality of a transaction? [](#what-is-the-finality-of-a-transaction) Aptos uses a BFT consensus algorithm, so transactions are finalized immediately after committing to the blockchain. ### What is the transaction fee on a transaction? [](#what-is-the-transaction-fee-on-a-transaction) Transaction fees are variable, but for most cases here are fixed. Check out [simulating transactions](/network/blockchain/gas-txn-fee#estimating-gas-consumption-via-simulation) to get an idea of the fee. # Indexer API Access > Access Aptos Indexer GraphQL API for historical data, transactions, fungible assets, and tokens with SDK integration and direct endpoints Aptos Labs hosts a public version of the Indexer GraphQL API that anyone can use to get basic historical and aggregate data about transactions, fungible assets, and tokens from on-chain. You can explore it by hand by viewing the Hasura Explorer below for the network you are interested in. You can also access the API via the GraphQL endpoints below. For more information on the format of data in each field / table, please see the [table reference page](/build/indexer/indexer-api/indexer-reference). ## SDK Access (Primary Method) [](#sdk-access-primary-method) The primary way to use the Indexer is to access it through the [TypeScript SDK](/build/sdks/ts-sdk/fetch-data-via-sdk). The TypeScript SDK will automatically handle rate limits, and can seamlessly allow for both [Fullnode REST API](/build/apis/fullnode-rest-api) access and Indexer access depending on what data is needed. ## Hasura Explorer (Manual Queries) [](#hasura-explorer-manual-queries) Choose a network to explore the free Aptos-Hosted Indexer API using the Hasura Explorer: [Mainnet](https://cloud.hasura.io/public/graphiql?endpoint=https://api.mainnet.aptoslabs.com/v1/graphql) Hasura GraphQL Explorer for Aptos Mainnet [Testnet](https://cloud.hasura.io/public/graphiql?endpoint=https://api.testnet.aptoslabs.com/v1/graphql) Hasura GraphQL Explorer for Aptos Testnet [Devnet](https://cloud.hasura.io/public/graphiql?endpoint=https://api.devnet.aptoslabs.com/v1/graphql) Hasura GraphQL Explorer for Aptos Devnet ## GraphQL API Endpoints (Direct Access) [](#graphql-api-endpoints-direct-access) If you need to directly make GraphQL queries to the Aptos-Labs hosted Indexer API, then use the following endpoints: * **Mainnet:** `https://api.mainnet.aptoslabs.com/v1/graphql` * **Testnet:** `https://api.testnet.aptoslabs.com/v1/graphql` * **Devnet:** `https://api.devnet.aptoslabs.com/v1/graphql` ### Rate limits [](#rate-limits) Learn more about the rate limits that apply to the Aptos Labs hosted indexer API by reading the [Geomi docs](https://geomi.dev/docs/admin/billing). If you need a higher rate limit, consider the following solutions: 1. Get an API Key from [Geomi](https://geomi.dev/). Learn more about API keys at the [Geomi docs site](https://geomi.dev/docs/api-keys). 2. Run the Aptos Indexer API yourself. See the guide to self-hosting [here](/build/indexer/txn-stream/self-hosted). # Indexer API Reference > Complete GraphQL API reference for Aptos Indexer with table schemas, query examples, and field documentation for tokens and accounts The Indexer API allows you to access rich data about tokens, fungible assets, and accounts on-chain using GraphQL queries. **You can access it [here](/build/indexer/indexer-api).** For common queries, check out the sidebar for examples to work from. When building your own, this reference guide should help you determine which tables are most relevant, and how to format your queries. Caution Before relying on a table for production services, check the bottom of this page to see if that table is deprecated. If so, use the note section for guidance on what to do to migrate to a non-deprecated table. # Indexer Table Reference [](#indexer-table-reference) ## Filtering (with `where` clauses) [](#filtering-with-where-clauses) To ensure your queries filter data efficiently, check out the available indexes for each table. Some indexes are composite B-tree indexes, meaning they consist of multiple columns. B-tree indexes are ordered and perform optimally when queries utilize a left-most prefix of the indexed columns. ## General [](#general) ### `user_transactions` [](#user_transactions) Transactions filtered to user\_transactions (not system).
Index NameIndexed Columns
user_transactions_pkeyversion
user_transactions_sender_sequence_number_keysender, sequence_number
ut_epoch_indexepoch
ut_insat_indexinserted_at
ut_sender_seq_indexsender, sequence_number
user_transactions_contract_info_indexentry_function_contract_address, entry_function_module_name, entry_function_function_name
### `block_metadata_transactions` [](#block_metadata_transactions) A type of system transaction emitted once per block, useful for mapping to timestamp or epoch.
Index NameIndexed Columns
block_metadata_transactions_block_height_keyblock_height
block_metadata_transactions_pkeyversion
bmt_insat_indexinserted_at
### `account_transactions` [](#account_transactions) _Has an aggregate view for summary data called `account_transactions_aggregate`_
Index NameIndexed Columns
account_transactions_pkeyaccount_address, transaction_version
at_insat_indexinserted_at
at_version_indextransaction_version DESC
This table maps accounts and transactions that interact with that account.
FieldTypePrimary KeyDescription
delegated_staking_activitiesJoinUse the Hasura explorer to see these sub-fields.
fungible_asset_activitiesJoinReferences fungible_asset_activities.
token_activitiesJoinUse the Hasura explorer to see these sub-fields.
token_activities_aggregateJoinUse the Hasura explorer to see these sub-fields.
token_activities_v2JoinReferences token_activities_v2.
token_activities_v2_aggregateJoinReferences token_activities_v2.
account_addressString!YesThis is an Aptos account address. Ex. "0x50bc83f01d48ab3b9c00048542332201ab9cbbea61bda5f48bf81dc506caa78a"
transaction_versionbigint!YesBlockchain version of the transaction. Ex. 10000000
### `ledger_infos` [](#ledger_infos) This table shares what chain is currently being queried.
FieldTypePrimary KeyDescription
chain_idintYesThe unique identifier for the chain you are accessing. Ex. 1 (for Mainnet), 2 (for Testnet), etc.
### `processor_status` [](#processor_status) This table shares how current this processor's data is. gives you latest version processed per “processor”
FieldTypePrimary KeyDescription
last_success_versionbigintYesThe version number of the last successful processor run. Ex. 5000000
last_transaction_timestampStringTimestamp of the last processed transaction. Ex. "2024-04-17T02:14:25.68771"
last_updatedStringTimestamp of the last update to this processor's status. Ex. "2024-04-17T02:14:25.68771"
processorStringYesName of the processor. Ex. "transaction_processor"
## NFT [](#nft) ### `token_activities_v2` [](#token_activities_v2) _Has an aggregate view for summary data called `token_activities_v2_aggregate`_
Index NameIndexed Columns
ta2_from_type_indexfrom_address, type
ta2_insat_indexinserted_at
ta2_owner_type_indexevent_account_address, type
ta2_tid_indextoken_data_id
ta2_to_type_indexto_address, type
token_activities_v2_pkeytransaction_version, event_index
This table tracks token activities and is especially useful for tracking NFT activity. This includes both v1 and v2 data.
FieldTypePrimary KeyDescription
aptos_names_fromJoinUse the Hasura explorer to see these sub-fields.
aptos_names_from_aggregateJoinUse the Hasura explorer to see these sub-fields.
aptos_names_toJoinUse the Hasura explorer to see these sub-fields.
aptos_names_to_aggregateJoinUse the Hasura explorer to see these sub-fields.
current_token_dataJoinUse the Hasura explorer to see these sub-fields.
after_valueStringThe value of a token property after the transaction. Ex. "100"
before_valueStringThe value of a token property before the transaction. Ex. "50"
entry_function_id_strStringThe identifier of the function called in this transaction. Ex. "0x1::aptos_account::transfer"
event_account_addressStringThis is an Aptos account address related to the event. This address must be 66 characters long, and so may be 0 padded. Ex. "0x50bc83f01d48ab3b9c00048542332201ab9cbbea61bda5f48bf81dc506caa78a"
event_indexbigintYesIndex of the event within the transaction. Ex. 1
from_addressStringThis is an Aptos account address from which the token was sent. This address must be 66 characters long, and so may be 0 padded. Ex. "0x50bc83f01d48ab3b9c00048542332201ab9cbbea61bda5f48bf81dc506caa78a"
is_fungible_v2BooleanIndicates whether the token is fungible. Soon to be deprecated. Ex. False for NFTs.
property_version_v1bigintThe version of the token's properties under schema version 1. This field is only for token standard v1. It is always 0 for v2. Ex. 0
to_addressStringThis is an Aptos account address to which the token was sent. This address must be 66 characters long, and so may be 0 padded. Ex. "0x50bc83f01d48ab3b9c00048542332201ab9cbbea61bda5f48bf81dc506caa78a"
token_amountbigintThe amount of the token transferred in this activity. Ex. 3
token_data_idStringUnique identifier for this particular token's data. For token standard v1, this is derived from a combination of creator_address, collection_name, and token_name. This ID must be 66 characters long, and so may be 0 padded. Ex. "0x50bc83f01d48ab3b9c00048542332201ab9cbbea61bda5f48bf81dc506caa78a"
token_standardStringAptos standard that the collection adheres to. Ex. "v1"
transaction_timestampStringTimestamp when the transaction occurred. Ex. "2024-04-17T02:14:25.68771"
transaction_versionbigintYesBlockchain version of the transaction. Ex. 10000000
typeStringType of transfer - like "deposit" or "withdrawal". Ex. "0x3::token::DepositEvent"
### `nft_metadata_crawler_parsed_asset_uris` [](#nft_metadata_crawler_parsed_asset_uris) This table allows you to look up the cdn and uris for NFT images / content.
FieldTypePrimary KeyDescription
animation_optimizer_retry_countIntNumber of retries to optimize animation. Ex. 3
asset_uriStringYesURI of the asset. Ex. "https://example.com/nft/123"
cdn_animation_uriStringContent Delivery Network URI for animation. Ex. "https://cdn.example.com/animations/123"
cdn_image_uriStringContent Delivery Network URI for image. Ex. "https://cdn.example.com/images/123"
cdn_json_uriStringContent Delivery Network URI for JSON metadata. Ex. "https://cdn.example.com/metadata/123.json"
raw_animation_uriStringOriginal URI for animation before CDN optimization. Ex. "https://example.com/raw/animations/123"
raw_image_uriStringOriginal URI for image before CDN optimization. Ex. "https://example.com/raw/images/123"
Index NameIndexed Columns
nft_inserted_atinserted_at
nft_raw_animation_uriraw_animation_uri
nft_raw_image_uriraw_image_uri
parsed_asset_uris_pkeyasset_uri
### `current_token_ownerships_v2` [](#current_token_ownerships_v2) _Has an aggregate view for summary data called `current_token_ownerships_v2_aggregate`_ This table tracks who owns which NFTs. This includes both v1 and v2 tokens. Fungible tokens are not tracked as consistently.
FieldTypePrimary KeyDescription
composed_nfts_aggregateJoinAggregate information about the composed NFTs, such as count or other statistics.
current_token_dataJoinDetailed information about the token's current data; structure is defined in a related table.
amountbigintThe amount of the token owned. Example: 1 for an NFT.
composed_nftsArrayAn array containing the IDs of NFTs that compose this token, if applicable.
is_fungible_v2BooleanIndicates whether the token is fungible. Example: true or null
is_soulbound_v2BooleanIndicates whether the token is soulbound (non-transferable once owned). Example: true or null
last_transaction_timestampStringTimestamp of the last transaction involving the token. Example: "2024-04-17T02:14:25.68771"
last_transaction_versionbigintThe version number of the last transaction involving the token. Example: 20747031
non_transferrable_by_ownerBooleanIndicates whether the token is non-transferrable by the owner. Example: true or null
owner_addressStringYesThe Aptos account address that currently owns the token. Addresses must be 66 characters so may be 0 padded. Example: "0xa815a9a09105973084bfc31530e7c8f002846787c2f0521e1e34dc144ad83b89"
property_version_v1bigintYesThe version number of the token's properties as of the last update. This field is only for token standard v1. It is always 0 for v2. Example: 0
storage_idStringYesA unique identifier used for storage purposes. IDs must be 66 characters long, so may be 0 padded. Ex. "0xd8d41ff9f67d17d7dee061b5b683b92013b420cb6a30c21fc7c287454792d7a8"
table_type_v1StringThe Move function type. Example: "0x3::token::TokenStore"
token_data_idStringYesA unique identifier for the token data, typically a hash or a numeric ID. Ex. "0x3d911af2dc3e47848fbba17b8694cf526942be183b84f8393a6c048232fb976d"
token_properties_mutated_v1ObjectProperties of the token that have been mutated from the original. Often in JSON or similar format. Example:
token_standardStringThe standard used to generate this token. Ex. "v1" or "v2"
Index NameIndexed Columns
curr_to2_insat_indexinserted_at
curr_to2_owner_indexowner_address
curr_to2_wa_indexstorage_id
current_token_ownerships_v2_pkeytoken_data_id, property_version_v1, owner_address, storage_id
### `current_token_datas_v2` [](#current_token_datas_v2) This table tracks the metadata associated with each NFT (Ex. URI, supply, etc.). This tracks both v1 and v2 tokens.
FieldTypePrimary KeyDescription
cdn_asset_urisJoinUse the Hasura explorer to see these sub-fields.
current_collectionJoinUse the Hasura explorer to see these sub-fields.
current_token_ownershipsJoinUse the Hasura explorer to see these sub-fields.
current_token_ownerships_aggregateJoinUse the Hasura explorer to see these sub-fields.
aptos_nameStringThis is a name tied to this token using the Aptos Name Service (ANS). Ex. "EpicDragon"
collection_idStringYesIdentifier for the collection that includes this token. Ex. "0x360f6eeabb4d7a9d2fab1f35b01e02831e3b5c4b73c7fd6c98dcc1c301c817c8"
decimalsbigintNumber of decimal places for token value, typically for fungible tokens. Ex. 18
descriptionStringDescription of the token. Ex. "A legendary dragon from the mystical lands."
is_fungible_v2BooleanWhether the token is fungible. Ex. False for NFTs
largest_property_version_v1bigintThe largest version number of the token's properties under the first schema. Ex. 1
last_transaction_timestampbigintUnix timestamp of the last transaction involving this token. Ex. 2024-03-27T07:41:58.800893
last_transaction_versionbigintBlockchain version of the last transaction involving this token. Ex. 30000000
maximumbigintMaximum possible quantity of this token, relevant for fungibles. Ex. 1000000
supplybigintCurrent supply of the token in circulation. Ex. 500000
token_data_idStringUnique identifier for the token's data. Ex. "0xa815a9a09105973084bfc31530e7c8f002846787c2f0521e1e34dc144ad83b89"
token_nameStringThe formal name of the token. Ex. "Mystic Dragon"
token_propertiesObjectUse the Hasura explorer to see these sub-fields.
token_standardStringAptos standard that the collection adheres to. Ex. "v1"
token_uriStringURI linking to further information about the token. Ex. "https://example.com/tokens/987654321"
Index NameIndexed Columns
cur_td2_cid_name_indexcollection_id, token_name
cur_td2_insat_indexinserted_at
current_token_datas_v2_pkeytoken_data_id
### `current_collections_v2` [](#current_collections_v2) This table tracks the metadata associated with each NFT collection (Ex. collection\_id, creator\_address, etc.). This tracks both v1 and v2 tokens.
FieldTypePrimary KeyDescription
cdn_asset_urisJoinUse the Hasura explorer to see these sub-fields.
collection_idStringYesUnique identifier for the collection. IDs must be 66 characters long, and so may be 0 padded. Ex. "0xa815a9a09105973084bfc31530e7c8f002846787c2f0521e1e34dc144ad83b88"
collection_nameStringThe formal name of the collection. Ex. "Mythic Dragons"
creator_addressStringThis is an Aptos account address that created the collection. Addresses must be 66 characters long, and so may be 0 padded. Ex. "0x50bc83f01d48ab3b9c00048542332201ab9cbbea61bda5f48bf81dc506caa78a"
current_supplybigintCurrent supply of tokens in this collection. Ex. 500
descriptionStringDescription of the collection. Ex. "A collection of rare digital dragons."
last_transaction_timestampStringTimestamp of the last transaction involving this collection. Ex. "2024-04-17T02:14:25.68771"
last_transaction_versionbigintBlockchain version of the last transaction involving this collection. Ex. 3000000002
max_supplybigintMaximum possible quantity of tokens in this collection. If the max supply is 0, there is no limit on the supply. Ex. 1000
mutable_descriptionStringChangeable description of the collection. Ex. "Updated collection description."
mutable_uriBooleanTrue if the uri is changeable by the creator. Ex. True
table_handle_v1StringLegacy identifier handle for the collection in earlier schema versions. Ex. "handle_12345"
token_standardStringAptos standard that the collection adheres to. Ex. "v1"
total_minted_v2bigintTotal number of tokens minted in this collection under schema version 2. Ex. 800
uriStringThis is a URI to where the image live. This can also be JSON data. Ex. "https://example.com/collections/9876543210"
Index NameIndexed Columns
cur_col2_crea_cn_indexcreator_address, collection_name
cur_col2_insat_indexinserted_at
current_collections_v2_pkeycollection_id
### `current_collection_ownership_v2_view` [](#current_collection_ownership_v2_view) _Has an aggregate view for summary data called `current_collection_ownership_v2_view_aggregate`_ This table maps collections to who owns them and helps count how much of a collection is owned by other accounts.
FieldTypePrimary KeyDescription
current_collectionJoinUse the Hasura explorer to see these sub-fields.
collection_idStringYesUnique identifier for the collection. IDs must be 66 characters long, and so may be 0 padded. Ex. "0xa815a9a09105973084bfc31530e7c8f002846787c2f0521e1e34dc144ad83b89"
collection_nameStringThe formal name of the collection. Ex. "Mythic Dragons"
collection_uriStringURI linking to further information about the collection. Ex. "https://example.com/collections/9876543210"
creator_addressStringThis is an Aptos account address that created the collection. Addresses must be 66 characters long, and so may be 0 padded. Ex. "0x50bc83f01d48ab3b9c00048542332201ab9cbbea61bda5f48bf81dc506caa78a"
distinct_tokensbigintThe count of distinct tokens owned within this collection. Ex. 150
last_transaction_versionbigintThe version number of the last transaction involving this collection. Ex. 3000000002
owner_addressStringYesThis is an Aptos account address that currently owns the token. Addresses must be 66 characters long, and so may be 0 padded. Ex. "0x50bc83f01d48ab3b9c00048542332201ab9cbbea61bda5f48bf81dc506caa78a"
single_token_uriStringURI linking to information about a specific token within the collection. Ex. "https://example.com/tokens/9876543210"
token_standardStringAptos standard that the collection adheres to. Ex. "v1"
## Fungible Assets [](#fungible-assets) ### `fungible_asset_metadata` [](#fungible_asset_metadata) This tracks the metadata tied to each fungible asset (ex. decimals of precision). It includes v1 token data. This is a current\_ table.
FieldTypePrimary KeyDescription
asset_typeStringYesThe type of the asset, described by a Move resource. Ex. "0x1::aptos_coin::AptosCoin"
creator_addressStringThis is an Aptos account address that created the asset. Addresses must be 66 characters long, and so may be 0 padded. Ex. "0x50bc83f01d48ab3b9c00048542332201ab9cbbea61bda5f48bf81dc506caa78a"
decimalsbigintNumber of decimal places for token value, typically for fungible tokens. Ex. 18
icon_uriStringURI for the icon of the asset. Ex. "https://cdn.example.com/icons/123"
last_transaction_timestampStringTimestamp of the last transaction involving this asset. Ex. "2024-04-17T02:14:25.68771"
last_transaction_versionbigintBlockchain version of the last transaction involving this asset. Ex. 10000000
nameStringThe formal name of the asset. Ex. "Digital Gold"
project_uriStringURI linking to the project information associated with this asset. Ex. "https://www.example.com/project\_name/"
supply_aggregator_table_handle_v1StringLegacy handle for the supply aggregator table from an earlier schema version. Ex. "handle_67890"
supply_aggregator_table_key_v1StringLegacy key for accessing the supply aggregator table in earlier schema versions. Ex. "key_12345"
symbolStringThe trading symbol of the asset. Ex. "DGOLD"
token_standardStringStandard that the asset adheres to. Ex. "v1"
Index NameIndexed Columns
fam_creator_indexcreator_address
fam_insat_indexinserted_at
fungible_asset_metadata_pkeyasset_type
### `fungible_asset_activities` [](#fungible_asset_activities) This tracks the activity of fungible assets. It includes v1 token data.
FieldTypePrimary KeyDescription
owner_aptos_namesJoinReferences owner_aptos_names.
owner_aptos_names_aggregateJoinReferences owner_aptos_names.
amountbigintThe amount of the asset involved in the activity. Ex. 1000
asset_typeStringYesThe type of the asset, described by a Move resource. For fungible assets, this will be the address of the metadata object. Ex. "0x1::aptos_coin::AptosCoin"
block_heightbigintThe blockchain id at which this activity occurred. Ex. 1500000
entry_function_id_strStringThe identifier of the function called in this transaction. Ex. "0x1::aptos_account::transfer"
event_indexbigintIndex of the event within the transaction. Ex. 1
gas_fee_payer_addressStringThis is an Aptos account address that paid the gas fee for the transaction. Addresses must be 66 characters long, and so may be 0 padded. Ex. "0x50bc83f01d48ab3b9c00048542332201ab9cbbea61bda5f48bf81dc506caa78a"
is_frozenBooleanTrue if this activity is a freeze asset activity. Ex. null
is_gas_feeBooleanIndicates whether this activity involved a gas fee. Ex. True
is_transaction_successBooleanIndicates whether the transaction was successful. Ex. True
metadataObjectUse the Hasura explorer to see fields for metadata in this table.
owner_addressStringThis is an Aptos account address that owns the asset. Addresses must be 66 characters long, and so may be 0 padded. Ex. "0xa815a9a09105973084bfc31530e7c8f002846787c2f0521e1e34dc144ad83b89"
storage_idStringIdentifier for the storage used in the transaction. IDs must be 66 characters long, and so may be 0 padded. Ex. "0xa815a9a09105973084bfc31530e7c8f002846787c2f0521e1e34dc144ad83b89"
storage_refund_amountbigintAmount refunded for storage after the transaction. This is always in APT octas. Ex. 50
token_standardStringAptos standard that the collection adheres to. Ex. "v1"
transaction_timestampStringTimestamp when the transaction occurred. Ex. "2024-04-17T02:14:25.68771"
transaction_versionbigintBlockchain version of the transaction. Ex. 2
typeStringType of the transaction, described by a Move entry function. Ex. "0x1::coin::WithdrawEvent"
Index NameIndexed Columns
faa_at_indexasset_type
faa_gfpa_indexgas_fee_payer_address
faa_insat_idxinserted_at
faa_owner_type_indexowner_address, type
faa_si_indexstorage_id
fungible_asset_activities_pkeytransaction_version, event_index
### `current_fungible_asset_balances` [](#current_fungible_asset_balances) _Has an aggregate view for summary data called `current_fungible_asset_balances_aggregate`_ This tracks the asset balances of each account on-chain. It includes v1 token data.
FieldTypePrimary KeyDescription
amountbigintThe amount of the asset owned. Ex. 2000
asset_typeStringThe type of the asset, described by a Move resource. For v2 tokens this is the address of the fungible asset metadata object. For v1 it's the fully qualified path of the move resource. Ex. "0x1::aptos_coin::AptosCoin"
is_frozenBooleanIndicates whether the account is frozen. Ex. False
is_primaryBooleanIndicates whether this is the primary balance of the owner. Ex. True
last_transaction_timestampStringTimestamp of the last transaction involving this balance. Ex. "2024-04-17T02:14:25.68771"
last_transaction_versionbigintBlockchain version of the last transaction involving this balance. Ex. 30000000
metadataObjectUse the Hasura explorer to see fields for metadata in current_fungible_asset_balances.
owner_addressStringThis is an Aptos account address that owns the asset. Addresses must be 66 characters long, and so may be 0 padded. Ex. "0xa815a9a09105973084bfc31530e7c8f002846787c2f0521e1e34dc144ad83b89"
storage_idStringYesIdentifier for the storage associated with this balance. IDs must be 66 characters long, and so may be 0 padded. Ex. "0xa815a9a09105973084bfc31530e7c8f002846787c2f0521e1e34dc144ad83b89"
token_standardStringAptos standard that the collection adheres to. Ex. "v1"
Index NameIndexed Columns
cufab_insat_indexinserted_at
cufab_owner_at_indexowner_address, asset_type
current_unified_fungible_asset_balances_pkeystorage_id
## Delegated Staking [](#delegated-staking) With [AIP-6](https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-6.md) we added the ability for staking pools to be formed with delegated funds (delegation pools). Once these pools hold over 1M APT, they can become a staking pool (validator node). ### `current_delegated_staking_pool_balances` [](#current_delegated_staking_pool_balances) This table tracks the current balances of each account in a delegated staking pool.
FieldTypePrimary KeyDescription
staking_pool_addressStringYesThe address of the delegation pool.
total_coinsbigintAmount of APT in the staking pool.
total_sharesbigintThe total number of shares in the delegation pool.
operator_commission_percentagebigintThe commission percentage taken by the staking pool operator.
inactive_table_handleStringThe table handle for the inactive table.
active_table_handleStringThe table handle for the active table.
last_transaction_versionint8Transaction version (identifier) for the last transaction involving this staking pool.
inserted_atStringThe timestamp when the record was inserted.
Index NameIndexed Columns
current_delegated_staking_pool_balances_pkeystaking_pool_address
### `current_delegated_voter` [](#current_delegated_voter) This table tracks the current delegated voters of a delegation pool.
FieldTypePrimary KeyDescription
delegation_pool_addressStringYesThe address of the delegation pool.
delegator_addressStringYesThe address of the delegator.
table_handleStringThe table handle tracking this position.
voterStringThe address of the current voter in the delegation pool.
pending_voterStringThe address of the pending voter awaiting confirmation.
last_transaction_versionbigintThe transaction version (identifier) of the last transaction involving this delegation.
last_transaction_timestampTimestampThe block timestamp of the last transaction involving this delegation.
inserted_atTimestampThe timestamp when the record was inserted into the database.
Index NameIndexed Columns
current_delegated_voter_pkeydelegation_pool_address, delegator_address
cdv_da_indexdelegator_address
### `current_delegator_balances` [](#current_delegator_balances) This table tracks the current balances of each account in a delegated staking pool.
FieldTypePrimary KeyDescription
delegator_addressStringYesThe address of the delegator.
pool_addressStringThe address of the delegator pool.
pool_typeStringIf the shares are active or inactive
table_handleStringThe table handle for the pool.
sharesbigintThe number of shares in the pool.
parent_table_handleStringThe table handle for the parent table.
last_transaction_versionbigintTransaction version (identifier) for the last transaction involving this staking pool.
inserted_atTimestampThe timestamp when the record was inserted.
Index NameIndexed Columns
current_delegator_balances_pkeydelegator_address, pool_address, pool_type, table_handle
### `current_staking_pool_voter` [](#current_staking_pool_voter) This table tracks the current voters of a staking pool.
FieldTypePrimary KeyDescription
staking_pool_addressStringYesThe address of the staking pool.
voter_addressStringThe address of the voter.
operator_addressStringThe address of the operator.
last_transaction_versionbigintTransaction version (identifier) for the last transaction involving this staking pool.
inserted_atTimestampThe timestamp when the record was inserted.
Index NameIndexed Columns
current_staking_pool_voter_pkeystaking_pool_address
ctpv_va_indexvoter_address
ctpv_insat_indexinserted_at
### `delegated_staking_activities` [](#delegated_staking_activities) This table tracks delegated staking events.
FieldTypePrimary KeyDescription
transaction_versionbigintTransaction version (identifier) for activity
event_indexbigintThe index of the event. Ex. 1
delegator_addressStringThe address of the delegator.
pool_addressStringThe address of the pool.
event_typeStringDistributeRewards, AddStake, UnlikeStake, ReactiveStake, WithdrawStake
amountbigintThe amount being staked. Ex. 1000
inserted_atTimestampThe timestamp when the record was inserted.
Index NameIndexed Columns
delegated_staking_activities_pkeytransaction_version, event_index
dsa_pa_da_indexpool_address, delegator_address, transaction_version, event_index
dsa_insat_indexinserted_at
### `delegated_staking_pool_balances` [](#delegated_staking_pool_balances) This table tracks the historical balances of each account in a delegated staking pool.
FieldTypePrimary KeyDescription
transaction_versionbigintTransaction version (identifier) for activity
staking_pool_addressStringThe address of the delegation pool.
total_coinsbigintAmount of APT in the staking pool.
total_sharesbigintThe total number of shares in the delegation pool.
operator_commission_percentagebigintThe commission percentage taken by the staking pool operator.
inactive_table_handleStringThe table handle for the inactive table.
active_table_handleStringThe table handle for the active table.
inserted_atStringThe timestamp when the record was inserted.
Index NameIndexed Columns
delegated_staking_pool_balances_pkeytransaction_version, staking_pool_address
### `delegated_staking_pools` [](#delegated_staking_pools) This table tracks when a delegated pool was created.
FieldTypePrimary KeyDescription
staking_pool_addressStringThe address of the staking pool.
first_transaction_versionbigintThe version number of the first transaction involving this pool. Ex. 5000000
inserted_atTimestampThe timestamp when the record was inserted.
Index NameIndexed Columns
delegated_staking_pools_pkeystaking_pool_address
### `delegator_balances` [](#delegator_balances) This table tracks the historical balances of each account in a delegation pool.
FieldTypePrimary KeyDescription
transaction_versionbigintThe version number of the transaction. Ex. 5000000
write_set_change_indexbigintThe index of the write set change. Ex. 1
delegator_addressStringThe address of the delegator.
pool_addressStringThe address of the delegator pool.
pool_typeStringThe type of the pool. Ex. "delegated"
table_handleStringThe table handle for the pool.
sharesbigintThe number of shares in the pool.
parent_table_handleStringThe table handle for the parent table.
inserted_atTimestampThe timestamp when the record was inserted.
Index NameIndexed Columns
delegator_balances_pkeytransaction_version, write_set_change_index
## Aptos Naming Service (ANS) [](#aptos-naming-service-ans) ### `current_aptos_names` [](#current_aptos_names) _Has an aggregate view for summary data called `current_aptos_names_aggregate`_ This view of [`current_ans_lookup_v2`](#current_ans_lookup_v2) helps query by name instead of account.
FieldTypePrimary KeyDescription
domainStringThe domain associated with this Aptos name. Ex. "example.crypto"
domain_with_suffixStringThe full domain name including any suffix. Ex. "example.crypto.aptos"
expiration_timestampStringTimestamp when the domain registration expires. Ex. "2024-04-17T02:14:25.68771"
is_activeBooleanIndicates whether the domain is currently active. Ex. True
is_domain_ownerBooleanIndicates whether the registered address is the owner of the domain. Ex. False
is_primaryBooleanIndicates whether this is the primary domain for the registered address. Ex. True
last_transaction_versionbigintThe version number of the last transaction involving this domain. Ex. 5000000
owner_addressStringThis is an Aptos account address that owns the domain. Addresses must be 66 characters long, and so may be 0 padded. Ex. "0x123abc456def7890abcdef1234567890abcdef1234"
registered_addressStringThis is an Aptos account address registered to the domain. Addresses must be 66 characters long, and so may be 0 padded. Ex. "0x50bc83f01d48ab3b9c00048542332201ab9cbbea61bda5f48bf81dc506caa78a"
subdomainStringAny subdomain part of the domain name. Ex. "sub.example"
token_nameStringThe name of the token associated with this domain. Ex. "ExampleToken"
token_standardStringAptos standard that the collection adheres to. Ex. "v1"
### `current_ans_lookup_v2` [](#current_ans_lookup_v2) This table maps tokens, standards, and addresses to human readable names.
FieldTypePrimary KeyDescription
domainStringYesThe domain associated with this Aptos name. Ex. "example.crypto"
expiration_timestampStringTimestamp when the domain registration expires. Ex. "2024-04-17T02:14:25.68771"
is_deletedBooleanIndicates whether the domain registration has been deleted. Ex. False
last_transaction_versionbigintThe version number of the last transaction involving this domain. Ex. 5000000
registered_addressStringThis is an Aptos account address registered to the domain. Addresses must be 66 characters long, and so may be 0 padded. Ex. "0x50bc83f01d48ab3b9c00048542332201ab9cbbea61bda5f48bf81dc506caa78a"
subdomainStringYesAny subdomain part of the domain name. Ex. "sub.example"
token_nameStringThe name of the token associated with this domain. Ex. "ExampleToken"
token_standardStringYesAptos standard that the collection adheres to. Ex. "v1"
Index NameIndexed Columns
ans_v2_et_indexexpiration_timestamp
ans_v2_insat_indexinserted_at
ans_v2_ra_indexregistered_address
ans_v2_tn_indextoken_name, token_standard
current_ans_lookup_v2_pkeydomain, subdomain, token_standard
## Deprecated Tables [](#deprecated-tables) The following tables are planned for deprecation, or are already deprecated. See the notes section for any direct replacements or notes on how to migrate if you currently depend on one of these tables. Please do not use any of the below tables for production services.
TableNotes
address_version_from_move_resourcesReplace with account_transactions
address_events_summaryTo query custom events, you should create a No-Code Indexer
address_version_from_eventsTo query custom events, you should create a No-Code Indexer
coin_activitiesReplace with fungible_asset_activities
coin_balancesReplace with current_fungible_asset_balances
coin_infosReplace with fungible_asset_metadata
coin_supplyNo replacement; non-realtime APT coin supply is available with this query
collection_datasReplace with current_collections_v2
current_ans_lookupReplace with current_ans_lookup_v2
current_coin_balancesReplace with current_fungible_asset_balances
current_collection_datasReplace with current_collections_v2
current_token_datasReplace with current_token_datas_v2
current_token_ownershipsReplace with current_token_ownerships_v2
events_viewTo query custom events, you should create a No-Code Indexer
move_resourcesReplace with account_transactions
move_resources_viewReplace with account_transactions
nft_marketplace_v2_*No direct replacement; query marketplace activity via the Indexer API
token_activitiesReplace with token_activities_v2
token_datasReplace with current_token_datas_v2
token_ownershipsReplace with current_token_ownerships_v2
tokensReplace with current_token_datas_v2
transactionsNo replacement; non-realtime data is available in BigQuery
transactions_viewNo replacement; non-realtime data is available in BigQuery
# Transaction Stream Service > Real-time transaction streaming service for Aptos blockchain data, supporting both Aptos-hosted and self-hosted deployment options The Transaction Stream Service is a service that listens to the Aptos blockchain and emits transactions as they are processed. These docs explain how this system works, how to use the Labs-Hosted instance of the service, and how to deploy it yourself. You can get API access to a transaction stream hosted by Aptos Labs [here](/build/indexer/txn-stream/aptos-hosted-txn-stream). # TypeScript SDK > Official TypeScript SDK for building applications on Aptos - the most comprehensive and up-to-date SDK [ ![Github Repo Stars](https://img.shields.io/github/stars/aptos-labs/aptos-ts-sdk) ](https://github.com/aptos-labs/aptos-ts-sdk)[ ![NPM Version](https://img.shields.io/npm/v/%40aptos-labs%2Fts-sdk) ](https://www.npmjs.com/package/@aptos-labs/ts-sdk)[ ![Node Version](https://img.shields.io/node/v/%40aptos-labs%2Fts-sdk) ](https://www.npmjs.com/package/@aptos-labs/ts-sdk)[ ![NPM bundle size](https://img.shields.io/bundlephobia/min/%40aptos-labs/ts-sdk) ](https://www.npmjs.com/package/@aptos-labs/ts-sdk)[ ![Static Badge](https://img.shields.io/badge/SDK_Reference-Docs) ](https://aptos-labs.github.io/aptos-ts-sdk/@aptos-labs/ts-sdk-latest) The TypeScript SDK allows you to connect, explore, and interact on the Aptos blockchain. You can use it to request data, send transactions, set up test environments, and more! ``` npm i @aptos-labs/ts-sdk ``` ## Examples [](#examples) [Quickstart](/build/sdks/ts-sdk/quickstart) See the quickstart to get a working demo in < 5 minutes [20+ Examples](https://github.com/aptos-labs/aptos-ts-sdk/tree/main/examples/typescript) Explore all of the TypeScript examples provided in the SDK repository [Comprehensive Tests](https://github.com/aptos-labs/aptos-ts-sdk/tree/main/tests/e2e) See end to end tests which demonstrate how to use each feature of the SDK ### Transfer APT in 10 lines or less [](#transfer-apt-in-10-lines-or-less) simple\_transfer.ts ``` /* eslint-disable no-console */ /** * This example shows how to use the Aptos client to create accounts, fund them, and transfer between them. */ import { Account, AccountAddress, Aptos, AptosConfig, Network, NetworkToNetworkName } from "@aptos-labs/ts-sdk"; // TODO: There currently isn't a way to use the APTOS_COIN in the COIN_STORE due to a regexconst APTOS_COIN = "0x1::aptos_coin::AptosCoin";const COIN_STORE = "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>";const ALICE_INITIAL_BALANCE = 100_000_000;const BOB_INITIAL_BALANCE = 100;const TRANSFER_AMOUNT = 100; // Default to devnet, but allow for overridingconst APTOS_NETWORK: Network = NetworkToNetworkName[process.env.APTOS_NETWORK ?? Network.DEVNET]; /** * Prints the balance of an account * @param aptos * @param name * @param address * @returns {Promise<*>} * */const balance = async (aptos: Aptos, name: string, address: AccountAddress) => { type Coin = { coin: { value: string } }; const resource = await aptos.getAccountResource({ accountAddress: address, resourceType: COIN_STORE, }); const amount = Number(resource.coin.value); console.log(`${name}'s balance is: ${amount}`); return amount;}; const example = async () => { console.log("This example will create two accounts (Alice and Bob), fund them, and transfer between them."); // Setup the client const config = new AptosConfig({ network: APTOS_NETWORK }); const aptos = new Aptos(config); // Create two accounts const alice = Account.generate(); const bob = Account.generate(); console.log("=== Addresses ===\n"); console.log(`Alice's address is: ${alice.accountAddress}`); console.log(`Bob's address is: ${bob.accountAddress}`); // Fund the accounts console.log("\n=== Funding accounts ===\n"); const aliceFundTxn = await aptos.faucet.fundAccount({ accountAddress: alice.accountAddress, amount: ALICE_INITIAL_BALANCE, }); console.log("Alice's fund transaction: ", aliceFundTxn); const bobFundTxn = await aptos.faucet.fundAccount({ accountAddress: bob.accountAddress, amount: BOB_INITIAL_BALANCE, }); console.log("Bob's fund transaction: ", bobFundTxn); // Show the balances console.log("\n=== Balances ===\n"); const aliceBalance = await balance(aptos, "Alice", alice.accountAddress); const bobBalance = await balance(aptos, "Bob", bob.accountAddress); if (aliceBalance !== ALICE_INITIAL_BALANCE) throw new Error("Alice's balance is incorrect"); if (bobBalance !== BOB_INITIAL_BALANCE) throw new Error("Bob's balance is incorrect"); // Transfer between users const txn = await aptos.transaction.build.simple({ sender: alice.accountAddress, data: { function: "0x1::coin::transfer", typeArguments: [APTOS_COIN], functionArguments: [bob.accountAddress, TRANSFER_AMOUNT], }, }); console.log("\n=== Transfer transaction ===\n"); const committedTxn = await aptos.signAndSubmitTransaction({ signer: alice, transaction: txn }); await aptos.waitForTransaction({ transactionHash: committedTxn.hash }); console.log(`Committed transaction: ${committedTxn.hash}`); console.log("\n=== Balances after transfer ===\n"); const newAliceBalance = await balance(aptos, "Alice", alice.accountAddress); const newBobBalance = await balance(aptos, "Bob", bob.accountAddress); // Bob should have the transfer amount if (newBobBalance !== TRANSFER_AMOUNT + BOB_INITIAL_BALANCE) throw new Error("Bob's balance after transfer is incorrect"); // Alice should have the remainder minus gas if (newAliceBalance >= ALICE_INITIAL_BALANCE - TRANSFER_AMOUNT) throw new Error("Alice's balance after transfer is incorrect");}; example(); ``` # Sponsored Transactions > Allow applications to pay transaction fees for users, simplifying onboarding by removing the need for gas tokens. As outlined in [AIP-39](https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-39.md), sponsored transactions allow one account to pay the fees associated with executing a transaction for another account, essentially setting up a fee payer. Sponsored transactions simplify the process for onboarding users into applications by allowing the application to cover all associated fees for interacting with the Aptos blockchain. Here are two examples: * [MerkleTrade](https://merkle.trade/) offers low cost trading to those with Ethereum wallets by creating an Aptos wallet for users and covering all transaction fees so that the user does not need to acquire utility tokens for Aptos. * Community engagement applications like [Graffio](https://medium.com/aptoslabs/graffio-web3s-overnight-sensation-81a6cf18b626) offered to cover transaction fees for custodial accounts to support the collaborative drawing application for those without wallets. ## Process Overview [](#process-overview) The process for sending a sponsored transaction follows: * The sender of the transaction determines upon an operation, as defined by a `RawTransaction`. * The sender generates a `RawTransactionWithData::MultiAgentWithFeePayer` structure * Prior to the framework 1.8 release, this must contain the fee payer's address. * After framework release 1.8, this can optionally be set to `0x0`. * (Optionally) the sender aggregates signatures from other signers. * The sender can forward the signed transaction to the fee payer to sign and forward it to the blockchain. * Upon execution of the transaction, the sequence number of the sender account is incremented, all gas fees are deducted from the gas fee payer, and all refunds are sent to the gas fee payer. Alternatively, if the fee payer knows the operation and all signers involved, the fee payer could generate and sign the transaction and send it back to the other signers to sign. ## Technical Details [](#technical-details) In Aptos, a sponsored transaction reuses the same SignedTransaction as any other user transaction: ``` pub struct SignedTransaction { /// The raw transaction raw_txn: RawTransaction, /// Public key and signature to authenticate authenticator: TransactionAuthenticator,} ``` The difference is in the `TransactionAuthenticator`, which stores the authorization from the fee payer of the transaction to extract utility fees from their account: ``` pub enum TransactionAuthenticator {... /// Optional Multi-agent transaction with a fee payer. FeePayer { sender: AccountAuthenticator, secondary_signer_addresses: Vec, secondary_signers: Vec, fee_payer_address: AccountAddress, fee_payer_signer: AccountAuthenticator, },...} ``` To prepare a sponsored transaction for an account, the account must first exist on-chain. This is a requirement that is being removed with the 1.8 framework release. As of the 1.8 framework release, an account does not need to exist on-chain. However, the first transaction for an account requires enough gas to not only execute the transaction and cover the costs associated with account creation, even if an account already exists. Future improvements to the account model intend to eliminate this requirement. During signing of the transaction, all parties sign the following: ``` pub enum RawTransactionWithData {... MultiAgentWithFeePayer { raw_txn: RawTransaction, secondary_signer_addresses: Vec, fee_payer_address: AccountAddress, },} ``` Prior to framework release 1.8, all signers were required to know the actual fee payer address prior to signing. As of framework release 1.8, signers can optionally set the address to `0x0` and only the fee payer must sign with their address set. ## SDK Support [](#sdk-support) These are demonstrations of sponsored transactions: * The TypeScript SDK has [several examples](https://github.com/aptos-labs/aptos-ts-sdk/tree/main/examples/typescript-esm/sponsored_transactions) * The Python SDK has an example in [fee\_payer\_transfer\_coin.py](https://github.com/aptos-labs/aptos-python-sdk/blob/main/examples/fee_payer_transfer_coin.py). * The Rust SDK has a test case in [the API tests](https://github.com/aptos-labs/aptos-core/blob/0a62e54e13bc5da604ceaf39efed5c012a292078/api/src/tests/transactions_test.rs#L255). # Orderless Transactions > Execute transactions out of order for multi-machine signing scenarios while maintaining replay protection and security. As outlined in [AIP-123](https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-123.md), orderless transactions allow for transactions to be executed out of order, which is particularly useful in scenarios where multiple machines need to sign for a single sending account, but the order in which they sign does not affect the outcome of the transaction or matter to the creator. Replay is protected by a nonce, which is a unique identifier for a transaction. This allows for the transaction to be executed at any time within the expiration time, regardless of the order in which the machines sign the transaction, but not be able to be replayed after the nonce has expired. The maximum expiration time is 60 seconds for orderless transactions, which is not the same for sequence number transactions. ## Process Overview [](#process-overview) Orderless transactions are dependent on the transaction payload specified in [AIP-129](https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-129.md). The process for building and executing an orderless transaction is as follows: 1. Build a transaction with a `replayProtectionNonce` and a `TransactionPayload::TransactionPayloadPayload` that defines the operation to be executed. 2. Sign and submit the transaction as any other transaction, but with the `replayProtectionNonce` set. Ideally, the nonce should be a random u64 value. Note, that the behavior of the `replayProtectionNonce` is similar to a sequence number, but it does not guarantee ordered execution of transactions. Instead, it ensures that the transaction is unique and cannot be replayed (executed twice) with the same nonce. ## SDK Support [](#sdk-support) These are demonstrations of sponsored transactions: * The [TypeScript SDK](/build/sdks/ts-sdk/building-transactions/orderless-transactions) has documentation * The [Go SDK](https://github.com/aptos-labs/aptos-go-sdk/tree/main/examples/orderless_transaction) has an example # Encrypted Pending Transactions > Encrypt transaction payloads before they reach consensus so pending execution details stay confidential on supported networks. Encrypted pending transactions let you submit a transaction whose **Move payload** (entry function, arguments, and related executable data) is **encrypted while the transaction is pending**—for example while it is held in mempool and propagated between nodes. Validators decrypt the payload only when preparing execution after the block is confirmed. This reduces what observers can learn from the public pending transaction stream compared to a fully plaintext payload. This feature is currently live on **devnet**, with **testnet** support coming soon and **mainnet** to follow. The fullnode you use must expose a ledger **`encryption_key`** when encrypted submission is enabled. On networks or nodes where the key is absent, the client cannot build encrypted transactions. ## What clients can still observe [](#what-clients-can-still-observe) Encryption protects the **payload bytes** in transit and at rest in the pending phase, but a transaction is still a signed request to the chain. Depending on configuration and payload type, clients may still see metadata such as sender, gas parameters, expiration, signatures, and—where the protocol requires it—**metadata derived from the payload** (for example a **claimed entry function** for certain multisig or sponsored flows). Treat “encrypted pending” as **payload privacy**, not anonymity of the whole transaction. For the precise security model, see **[AIP-144: Encrypted Mempool](https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-144-encrypted-mempool.md)** and the underlying cryptography in [eprint 2025/2032](https://eprint.iacr.org/2025/2032). At the protocol level this feature is called **encrypted mempool** (AIP-144); the names are interchangeable. ## Cost [](#cost) Encrypted transactions pay a **minimum gas-unit price of 200 Octas/gas-unit**, twice the network base minimum, to cover validator decryption work. SDKs enforce this floor at build time; servers reject encrypted submissions priced below it. ## Signer restrictions [](#signer-restrictions) **Keyless**, **federated keyless**, and **account abstraction** accounts cannot sign encrypted transactions. The rule is enforced both client-side (where supported) and server-side. ## Relationship to orderless transactions [](#relationship-to-orderless-transactions) You can combine encrypted payloads with **[orderless transactions](/build/guides/orderless-transactions)** so parallel submitters use replay-protection nonces while still encrypting the executable. See the TypeScript SDK page below for build options. ## Not the same as confidential assets [](#not-the-same-as-confidential-assets) **Encrypted pending transactions** protect the transaction payload for _pending transactions_ on the way to execution. They are unrelated to **[confidential assets](/build/sdks/ts-sdk/confidential-asset)** transactions, which allow for privacy of balances and amounts _even after transactions are confirmed._ ## SDK support [](#sdk-support) **[TypeScript SDK](/build/sdks/ts-sdk/building-transactions/encrypted-pending-transactions)** — build with `options.encrypted` (`senderAuthenticationKey` is optional; SDK auto-fetches), submit encrypted builds. ## Further reading [](#further-reading) * [AIP-144: Encrypted Mempool](https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-144-encrypted-mempool.md) — protocol-level specification and rationale. * [Aptos Encrypted Mempool (eprint 2025/2032)](https://eprint.iacr.org/2025/2032) — formal cryptographic treatment. # Aptos Keyless > Integrate Keyless accounts for seamless user onboarding using social logins instead of traditional private key management. ## Integrate with Aptos Keyless accounts [](#integrate-with-aptos-keyless-accounts) * [Introduction](/build/guides/aptos-keyless/introduction) * [OIDC Support and Configuration](/build/guides/aptos-keyless/oidc-support) * [Integration Guide](/build/guides/aptos-keyless/integration-guide) * [Simple Example](/build/guides/aptos-keyless/simple-example) * [How Aptos Keyless works](/build/guides/aptos-keyless/how-keyless-works) * [Terminology and FAQ](/build/guides/aptos-keyless/other) ## Using an IAM Provider? Integrate with Aptos Federated Keyless [](#using-an-iam-provider-integrate-with-aptos-federated-keyless) * [Federated Keyless](/build/guides/aptos-keyless/federated-keyless) # Transaction Management > Build scalable transaction management systems on Aptos with proper account handling and sequence number management. This guide explains how to build a transaction management harness that can scale on the Aptos blockchain. ## Background [](#background) In Aptos, transactions are mapped back to an account in terms of the entity that signs or authorizes that transaction and provides an account-based sequence number. When the Aptos network receives a new transaction, several rules are followed with respect to this: * The transaction sent from an account must be authorized correctly by that account. * The current time as defined by the most recent ledger update must be before the expiration timestamp of the transaction. * The transaction's sequence number must be equal to or greater than the sequence number on-chain for that account. Once the initial node has accepted a transaction, the transaction makes its way through the system by an additional rule. If a transactions sequence number is higher than the current on-chain sequence number, it can only progress toward consensus if every node in the path has seen a transaction with the sequence number between the on-chain state and the current sequence number. Example: Alice owns an account whose current on-chain sequence number is 5. Alice submits a transaction to node Bob with sequence number 6. Bob the node accepts the transaction but does not forward it, because Bob has not seen 5. In order to make progress, Alice must either send Bob transaction number 5 or Bob must be notified from consensus that 5 was committed. In the latter, Alice submitted the transaction through another node. Beyond this there are two remaining principles: * A single account can have at most 100 uncommitted transactions submitted to the blockchain. Any more than that and the transactions will be rejected. This can happen silently if Alice submits the first 100 to Bob the node and the next 100 to Carol the node. If both those nodes share a common upstream, then that upstream will accept Alice's 100 sent via Bob but silently reject Alice's 100 sent via Carol. * Submitting to distinct transactions to multiple nodes will result in slow resolution as transactions will not make progress from the submitted node until the submitted knows that all preceding transactions have been committed. For example, if Alice sends the first 50 via Bob and the next 50 via Carol. ## Building a Transaction Manager [](#building-a-transaction-manager) Now that we understand the nuances of transactions, let's dig into building a robust transaction manager. This consists of the following core components: * A sequence number generator that allocates and manages available sequence numbers for a single account. * A transaction manager that receives payloads from an application or a user, sequence numbers from the sequence number generator, and has access to the account key to combine the three pieces together into a viable signed transaction. It then also takes the responsibility for pushing the transaction to the blockchain. * An on-chain worker, leader harness that lets multiple accounts share the signer of a single shared account. Currently, this framework assumes that the network builds no substantial queue, that is a transaction that is submitted executes and commits with little to no delay. In order to address high demand, this work needs to be extended with the following components: * Optimizing `base_gas_unit` price to ensure priority transactions can be committed to the blockchain. * Further handling of transaction processing rates to ensure that the expiration timer is properly set. * Handling of transaction failures to either be ignored or resubmitted based upon desired outcome. Note, an account should be managed by a single instance of the transaction manager. Otherwise, each instance of the transaction manager will likely have stale in-memory state resulting in overlapping sequence numbers. ### Implementations [](#implementations) * Python * [Sequence number manager](https://github.com/aptos-labs/aptos-core/pull/7987) * [Transaction manager](https://github.com/aptos-labs/aptos-core/pull/7987) * [Worker-leader smart contract](https://github.com/aptos-labs/aptos-core/pull/7986) ### Managing Sequence Numbers [](#managing-sequence-numbers) Each transaction requires a distinct sequence number that is sequential to previously submitted transactions. This can be provided by the following process: 1. At startup, query the blockchain for the account’s current sequence number. 2. Support up to 100 transactions in flight at the same time, that is 100 sequence numbers can be allocated without confirming that any have been committed. 3. If there are 100 transactions in flight, determine the actual committed state by querying the network. This will update the current sequence number. 4. If there are less than 100 transactions in flight, return to step 2. 5. Otherwise, sleep for .1 seconds and continue to re-evaluate the current on-chain sequence number. 6. All transactions should have an expiration time. If the expiration time has passed, assume that there has been a failure and reset the sequence number. The trivial case is to only monitor for failures when the maximum number of transactions are in flight and to let other services manages this otherwise. In parallel, monitor new transactions submitted. Once the earliest transaction expiration time has expired synchronize up to that transaction. Then repeat the process for the next transaction. If there is any failure, wait until all outstanding transactions have timed out and leave it to the application to decide how to proceed, e.g., replay failed transactions. The best method to waiting for outstanding transactions is to query the ledger timestamp and ensure it is at least elapsed the maximum timeout from the last transactions submit time. From there, validate with mempool that all transactions since the last known committed transaction are either committed or no longer exist within the mempool. This can be done by querying the REST API for transactions of a specific account, specifying the currently being evaluated sequence number and setting a limit to 1. Once these checks are complete, the local transaction number can be resynchronized. These failure handling steps are critical for the following reasons: * Mempool does not immediate evict expired transactions. * A new transaction cannot overwrite an existing transaction, even if it is expired. * Consensus, i.e., the ledger timestamp, dictates expirations, the local node will only expire after it sees a committed timestamp after the transactions expiration time and a garbage collection has happened. ### Managing Transactions [](#managing-transactions) Once a transaction has been submitted it goes through a variety of steps: 1. Submission to a REST endpoint. 2. Pre-execution validation in the Mempool during submission. 3. Transmission from Mempool to Mempool with pre-execution validation happening on each upstream node. 4. Inclusion in a consensus proposal. 5. One more pre-execution validation. 6. Execution and committing to storage. There are many potential failure cases that must be considered: * Failure during transaction submission (1 and 2): * Visibility: The application will receive an error either that the network is unavailable or that the transaction failed pre-execution validation. * If the error is related to availability or duplicate sequence numbers, wait until access is available and the sequence number has re-synchronized. * Pre-execution validation failures are currently out of scope, outside of those related to duplicate sequence numbers, account issues are likely related to an invalid key for the account or the account lacks sufficient funds for gas. * Failure between submission and execution (3, 4, and 5): * Visibility: Only known by waiting until the transaction has expired. * These are the same as other pre-execution validation errors due to changes to the account as earlier transactions execute. It is likely either duplicate sequence numbers or the account lacks sufficient funds for gas. * Failure during execution (6): * Visibility: These are committed to the blockchain. * These errors occur as a result of on-chain state issues, these tend to be application specific, such as an auction where a new bid might not actually be higher than the current bid. ### Workers and Identity [](#workers-and-identity) Using the above framework, a single account can push upwards of 100 transactions from the start of a block to the end of a block. Assuming that all 100 transactions are consumed within 1 block, it will take a bit of time for the next 100 slots to be available. This is due to the network delays as well as the multi-staged validator pipeline. To fully leverage the blockchain for massive throughput, using a single user account is not enough. Instead, Aptos supports the concept of worker accounts that can share the responsibility of pushing work through a shared account, also known as a resource account. In this model, each worker has access to the `SignerCap` of the shared account, which enables them to impersonate the shared account or generate the `signer` for the shared account. Upon gaining the `signer`, the transaction can execute the logic that is gated by the signer of the shared account. Another model, if viable, is to decouple the `signer` altogether away from permissions and to make an application specific capability. Then this capability can be given to each worker that lets them operate on the shared infrastructure. Note that parallelization on the shared infrastructure can be limited if any transaction would have any read or write conflicts. This won’t prevent multiple transactions from executing within a block, but can impact maximum blockchain performance. # AI Tools for Aptos Development > Use AI tools like Claude, Cursor, and GitHub Copilot to build on Aptos faster. Access the Aptos MCP server and LLMs.txt documentation feeds. Aptos provides first-class support for AI-powered development workflows. Whether you're using an AI coding assistant or an AI chat tool, you can give it deep knowledge of the Aptos blockchain, Move language, SDKs, and APIs. ## Aptos MCP Server [](#aptos-mcp-server) The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) lets AI coding tools directly call Aptos APIs, query on-chain data, and generate correct Aptos code — without needing to copy-paste documentation. [Claude Code](/build/ai/aptos-mcp/claude) Set up the Aptos MCP server for Claude Code [Cursor](/build/ai/aptos-mcp/cursor) Set up the Aptos MCP server for Cursor ## Aptos Agent Skills [](#aptos-agent-skills) [Aptos Agent Skills](https://github.com/aptos-labs/aptos-agent-skills) provides pre-built AI skills that give coding assistants deep Aptos expertise — covering Move smart contracts, the TypeScript SDK, and full-stack dApp scaffolding. ``` npx skills add aptos-labs/aptos-agent-skills ``` This command uses the [Skills CLI](https://www.npmjs.com/package/skills), which is invoked via `npx`. If `npx skills` is not available in your environment, ensure you have Node.js and npm installed and refer to the Skills CLI documentation for installation and troubleshooting details. [Aptos Agent Skills](/build/ai/aptos-agent-skills) Browse available skills for Move development, TypeScript SDK, and project scaffolding ## LLMs.txt Documentation Feeds [](#llmstxt-documentation-feeds) The [LLMs.txt standard](https://llmstxt.org/) provides machine-readable documentation feeds that AI tools can ingest to understand the full Aptos documentation. We publish three feeds to suit different context window sizes:
FeedURLBest for
llms.txt/llms.txtStructured index with page titles, descriptions, and per-page .md links
llms-small.txt/llms-small.txtCondensed docs for smaller context windows
llms-full.txt/llms-full.txtFull documentation — all pages concatenated
Some clients resolve `/.well-known/llms.txt`; production serves the same index via a redirect to [`/llms.txt`](/llms.txt) ([`https://aptos.dev/.well-known/llms.txt`](https://aptos.dev/.well-known/llms.txt)). The [`llms.txt`](/llms.txt) router also surfaces machine-readable **API** links—[`/aptos-spec.json`](/aptos-spec.json) (OpenAPI 3 JSON for the node REST API) and [`/rest-api`](/rest-api) (HTML reference)—plus **MCP**, Agent Skills, Explorer, GitHub, standards, and Indexer GraphQL `.md` links in one place. [How to use LLMs.txt with AI tools](/llms-txt) Instructions for Cursor, GitHub Copilot, Claude.ai, ChatGPT, Windsurf, and more ## Agent discovery endpoints [](#agent-discovery-endpoints) The docs site publishes a handful of standardized discovery documents so coding agents can bootstrap from a single root. Every page also returns a [`Link`](https://www.rfc-editor.org/rfc/rfc8288) response header and matching `