Aptos dApp Development Guide
This comprehensive guide covers building modern decentralized applications (dApps) on the Aptos blockchain using the latest @aptos-labs/ts-sdk and development best practices for 2025.
Prerequisites
- Node.js 18+ for running the development environment
- Basic TypeScript/JavaScript knowledge for frontend development
- Understanding of React for UI components (optional but recommended)
- Aptos wallet installed (Petra, Pontem, or other Aptos-compatible wallets)
Core Technologies
Essential Packages
# Core Aptos SDK
npm install @aptos-labs/ts-sdk
# React integration (optional)
npm install react react-dom @types/react @types/react-dom
# State management
npm install @tanstack/react-query
# UI components (optional)
npm install @aptos-labs/wallet-adapter-react
Package Overview
- @aptos-labs/ts-sdk: Core SDK for blockchain interactions
- @aptos-labs/wallet-adapter-react: React hooks for wallet integration
- @tanstack/react-query: State management for async data
Basic Setup
1. Initialize Aptos Client
import { Aptos, AptosConfig, Network } from '@aptos-labs/ts-sdk';
// Initialize for different networks
const config = new AptosConfig({
network: Network.MAINNET // Network.TESTNET, Network.DEVNET
});
const aptos = new Aptos(config);
// Using BlockEden.xyz endpoint
const blockEdenConfig = new AptosConfig({
fullnode: 'https://aptos-mainnet.blockeden.xyz/<access_key>',
network: Network.MAINNET,
});
const blockEdenAptos = new Aptos(blockEdenConfig);
2. React dApp Setup with Wallet Integration
// App.tsx
import React from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { WalletProvider } from '@aptos-labs/wallet-adapter-react';
import { PetraWallet } from 'petra-plugin-wallet-adapter';
import { PontemWallet } from '@pontem/wallet-adapter-plugin';
const queryClient = new QueryClient();
const wallets = [
new PetraWallet(),
new PontemWallet(),
];
function App() {
return (
<QueryClientProvider client={queryClient}>
<WalletProvider wallets={wallets} autoConnect={true}>
<MyDApp />
</WalletProvider>
</QueryClientProvider>
);
}
export default App;
3. Wallet Connection Component
// WalletConnection.tsx
import React from 'react';
import { useWallet } from '@aptos-labs/wallet-adapter-react';
export function WalletConnection() {
const {
connect,
disconnect,
account,
connected,
wallets,
wallet
} = useWallet();
const handleConnect = async (walletName: string) => {
try {
await connect(walletName);
} catch (error) {
console.error('Failed to connect wallet:', error);
}
};
const handleDisconnect = async () => {
try {
await disconnect();
} catch (error) {
console.error('Failed to disconnect wallet:', error);
}
};
if (connected && account) {
return (
<div className="wallet-connected">
<p>Connected: {account.address}</p>
<p>Wallet: {wallet?.name}</p>
<button onClick={handleDisconnect}>Disconnect</button>
</div>
);
}
return (
<div className="wallet-selection">
<h3>Connect Your Wallet</h3>
{wallets.map((wallet) => (
<button
key={wallet.name}
onClick={() => handleConnect(wallet.name)}
disabled={!wallet.readyState}
>
Connect {wallet.name}
</button>
))}
</div>
);
}
Working with Transactions
Basic APT Transfer
import { Account, Aptos, AptosConfig, Network } from '@aptos-labs/ts-sdk';
import { useWallet } from '@aptos-labs/wallet-adapter-react';
export function TransferAPT() {
const { account, signAndSubmitTransaction } = useWallet();
const aptos = new Aptos(new AptosConfig({ network: Network.TESTNET }));
const transferAPT = async (recipient: string, amount: number) => {
if (!account) {
throw new Error('Wallet not connected');
}
try {
const transaction = await aptos.transaction.build.simple({
sender: account.address,
data: {
function: '0x1::aptos_account::transfer',
functionArguments: [
recipient, // recipient address
amount * 100000000, // amount in Octas (1 APT = 100,000,000 Octas)
],
},
});
const response = await signAndSubmitTransaction({
transaction,
});
// Wait for transaction confirmation
const executedTransaction = await aptos.waitForTransaction({
transactionHash: response.hash,
});
console.log('Transfer successful:', executedTransaction);
return executedTransaction;
} catch (error) {
console.error('Transfer failed:', error);
throw error;
}
};
return (
<div>
<button onClick={() => transferAPT('0x...', 1)}>
Send 1 APT
</button>
</div>
);
}
Multi-Agent Transactions
export function MultiAgentTransaction() {
const { account, signAndSubmitTransaction } = useWallet();
const aptos = new Aptos(new AptosConfig({ network: Network.TESTNET }));
const createMultiAgentTx = async (secondaryAccount: string) => {
if (!account) throw new Error('Wallet not connected');
try {
const transaction = await aptos.transaction.build.multiAgent({
sender: account.address,
secondarySignerAddresses: [secondaryAccount],
data: {
function: '0x1::aptos_account::transfer',
functionArguments: [
account.address, // Transfer back to primary account
1000000, // 0.01 APT
],
},
});
const response = await signAndSubmitTransaction({
transaction,
});
return await aptos.waitForTransaction({
transactionHash: response.hash,
});
} catch (error) {
console.error('Multi-agent transaction failed:', error);
throw error;
}
};
return (
<button onClick={() => createMultiAgentTx('0x...')}>
Create Multi-Agent Transaction
</button>
);
}
Data Fetching Patterns
Account Information and Balance
import { useQuery } from '@tanstack/react-query';
import { Aptos, AptosConfig, Network } from '@aptos-labs/ts-sdk';
const aptos = new Aptos(new AptosConfig({ network: Network.TESTNET }));
export function AccountBalance({ address }: { address: string }) {
const { data: balance, isLoading, error } = useQuery({
queryKey: ['balance', address],
queryFn: async () => {
const resources = await aptos.getAccountResources({
accountAddress: address,
});
const coinStore = resources.find(
(resource) => resource.type === '0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>'
);
if (coinStore?.data) {
return (coinStore.data as any).coin.value;
}
return '0';
},
enabled: !!address,
refetchInterval: 10000, // Refresh every 10 seconds
});
if (isLoading) return <div>Loading balance...</div>;
if (error) return <div>Error loading balance</div>;
const aptBalance = balance ? Number(balance) / 100000000 : 0;
return (
<div>
Balance: {aptBalance.toFixed(8)} APT
</div>
);
}
Account Resources and Modules
export function AccountResources({ address }: { address: string }) {
const { data: resources } = useQuery({
queryKey: ['resources', address],
queryFn: () => aptos.getAccountResources({ accountAddress: address }),
enabled: !!address,
});
const { data: modules } = useQuery({
queryKey: ['modules', address],
queryFn: () => aptos.getAccountModules({ accountAddress: address }),
enabled: !!address,
});
return (
<div>
<h3>Account Resources</h3>
<div>Total Resources: {resources?.length || 0}</div>
<div>Total Modules: {modules?.length || 0}</div>
{resources?.slice(0, 5).map((resource, index) => (
<div key={index}>
<strong>Type:</strong> {resource.type}
</div>
))}
</div>
);
}