Skip to main content

Simulate Transaction

Simulate a transaction to estimate gas costs and validate execution without submitting to the blockchain.

When to Use This Method​

POST /transactions/simulate is essential for:

  • Gas Estimation - Calculate gas costs before submission
  • Validation - Test if a transaction will succeed
  • Error Detection - Identify issues before spending gas
  • UI Preview - Show users expected transaction outcomes

Request Format​

Headers​

  • Content-Type: application/json
  • Accept: application/json

Body Structure​

{
"sender": "0x1",
"sequence_number": "0",
"max_gas_amount": "0",
"gas_unit_price": "0",
"expiration_timestamp_secs": "1234567890",
"payload": {
"type": "entry_function_payload",
"function": "0x1::aptos_account::transfer",
"type_arguments": [],
"arguments": ["0x2", "100000"]
},
"signature": {
"type": "ed25519_signature",
"public_key": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"signature": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
}
}

Note: For simulation, you can use dummy signature values (all zeros).

Implementation Examples​

import { Aptos, AptosConfig, Network, Account } from "@aptos-labs/ts-sdk";

const config = new AptosConfig({
network: Network.MAINNET,
fullnode: "https://api-aptos-mainnet.n.dwellir.com/v1/YOUR_API_KEY",
});

const aptos = new Aptos(config);

// Basic transaction simulation
async function simulateTransaction(
sender: Account,
payload: any
) {
const transaction = await aptos.transaction.build.simple({
sender: sender.accountAddress,
data: payload,
});

const [simulationResult] = await aptos.transaction.simulate.simple({
signerPublicKey: sender.publicKey,
transaction,
});

return {
success: simulationResult.success,
gasUsed: simulationResult.gas_used,
gasUnitPrice: simulationResult.gas_unit_price,
maxGasAmount: simulationResult.max_gas_amount,
vmStatus: simulationResult.vm_status,
events: simulationResult.events,
changes: simulationResult.changes,
};
}

// Simulate with gas estimation
async function simulateWithGasEstimation(
sender: Account,
recipient: string,
amount: number
) {
const payload = {
function: "0x1::aptos_account::transfer",
functionArguments: [recipient, amount],
};

const transaction = await aptos.transaction.build.simple({
sender: sender.accountAddress,
data: payload,
options: {
maxGasAmount: 0, // Let simulation estimate
gasUnitPrice: 0, // Let simulation estimate
},
});

const [simulation] = await aptos.transaction.simulate.simple({
signerPublicKey: sender.publicKey,
transaction,
options: {
estimateGasUnitPrice: true,
estimateMaxGasAmount: true,
estimatePrioritizedGasUnitPrice: true,
},
});

return {
estimatedGasUsed: simulation.gas_used,
estimatedGasUnitPrice: simulation.gas_unit_price,
estimatedMaxGasAmount: simulation.max_gas_amount,
estimatedTotalCost: BigInt(simulation.gas_used) * BigInt(simulation.gas_unit_price),
success: simulation.success,
vmStatus: simulation.vm_status,
};
}

// Batch simulation for multiple transactions
async function batchSimulate(
sender: Account,
payloads: any[]
) {
const simulations = await Promise.all(
payloads.map(async (payload) => {
const transaction = await aptos.transaction.build.simple({
sender: sender.accountAddress,
data: payload,
});

const [result] = await aptos.transaction.simulate.simple({
signerPublicKey: sender.publicKey,
transaction,
});

return {
payload,
success: result.success,
gasUsed: result.gas_used,
error: result.success ? null : result.vm_status,
};
})
);

return simulations;
}

Common Use Cases​

1. Gas Cost Estimation​

Estimate gas costs before showing to users:

async function estimateTransactionCost(
sender: Account,
payload: any
): Promise<{
gasUnits: string;
gasPrice: string;
totalAPT: number;
totalUSD: number;
}> {
const [simulation] = await aptos.transaction.simulate.simple({
signerPublicKey: sender.publicKey,
transaction: await aptos.transaction.build.simple({
sender: sender.accountAddress,
data: payload,
}),
options: {
estimateGasUnitPrice: true,
estimateMaxGasAmount: true,
},
});

const gasUnits = simulation.gas_used;
const gasPrice = simulation.gas_unit_price;
const totalOctas = BigInt(gasUnits) * BigInt(gasPrice);
const totalAPT = Number(totalOctas) / 100_000_000; // 1 APT = 10^8 Octas

// Get APT price (implement your price feed)
const aptPriceUSD = await getAPTPrice();
const totalUSD = totalAPT * aptPriceUSD;

return {
gasUnits,
gasPrice,
totalAPT,
totalUSD,
};
}

2. Transaction Validation​

Validate complex transactions before submission:

async function validateComplexTransaction(
sender: Account,
payload: any
): Promise<{
valid: boolean;
errors: string[];
warnings: string[];
}> {
const errors: string[] = [];
const warnings: string[] = [];

try {
// Simulate transaction
const [simulation] = await aptos.transaction.simulate.simple({
signerPublicKey: sender.publicKey,
transaction: await aptos.transaction.build.simple({
sender: sender.accountAddress,
data: payload,
}),
});

if (!simulation.success) {
errors.push(`Transaction will fail: ${simulation.vm_status}`);
}

// Check gas requirements
const gasRequired = BigInt(simulation.gas_used) * BigInt(simulation.gas_unit_price);

// Get account balance
const resources = await aptos.getAccountResources({
accountAddress: sender.accountAddress,
});

const aptResource = resources.find(
r => r.type === "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>"
);

const balance = BigInt(aptResource?.data?.coin?.value || 0);

if (balance < gasRequired) {
errors.push(`Insufficient balance for gas. Need ${gasRequired}, have ${balance}`);
} else if (balance < gasRequired * 2n) {
warnings.push("Low balance warning: Less than 2x gas cost remaining");
}

// Check for expected state changes
if (simulation.changes.length === 0) {
warnings.push("Transaction produces no state changes");
}

// Check events
const criticalEvents = simulation.events.filter(
e => e.type.includes("failure") || e.type.includes("error")
);

if (criticalEvents.length > 0) {
warnings.push(`Transaction emits ${criticalEvents.length} error events`);
}

} catch (error) {
errors.push(`Simulation failed: ${error.message}`);
}

return {
valid: errors.length === 0,
errors,
warnings,
};
}

3. State Change Preview​

Show users what will change:

async function previewStateChanges(
sender: Account,
payload: any
): Promise<{
balanceChanges: any[];
resourceChanges: any[];
events: any[];
}> {
const [simulation] = await aptos.transaction.simulate.simple({
signerPublicKey: sender.publicKey,
transaction: await aptos.transaction.build.simple({
sender: sender.accountAddress,
data: payload,
}),
});

const balanceChanges = [];
const resourceChanges = [];

// Parse write set changes
for (const change of simulation.changes) {
if (change.type === "write_resource") {
const resourceType = change.data.type;

if (resourceType.includes("CoinStore")) {
// Extract coin balance change
const oldValue = change.data.old_value?.coin?.value || "0";
const newValue = change.data.data?.coin?.value || "0";
const diff = BigInt(newValue) - BigInt(oldValue);

balanceChanges.push({
address: change.address,
coinType: resourceType,
oldBalance: oldValue,
newBalance: newValue,
change: diff.toString(),
});
} else {
resourceChanges.push({
address: change.address,
type: resourceType,
changeType: change.data.old_value ? "modified" : "created",
});
}
}
}

return {
balanceChanges,
resourceChanges,
events: simulation.events,
};
}

4. Multi-Step Transaction Planning​

Simulate a sequence of dependent transactions:

async function simulateTransactionSequence(
sender: Account,
steps: Array<{name: string; payload: any}>
): Promise<any[]> {
const results = [];
let sequenceNumber = BigInt(
(await aptos.getAccountInfo({
accountAddress: sender.accountAddress,
})).sequence_number
);

for (const step of steps) {
const transaction = await aptos.transaction.build.simple({
sender: sender.accountAddress,
data: step.payload,
options: {
accountSequenceNumber: sequenceNumber,
},
});

const [simulation] = await aptos.transaction.simulate.simple({
signerPublicKey: sender.publicKey,
transaction,
});

results.push({
step: step.name,
success: simulation.success,
gasUsed: simulation.gas_used,
error: simulation.success ? null : simulation.vm_status,
});

if (!simulation.success) {
// Stop simulation if a step fails
break;
}

sequenceNumber++;
}

return results;
}

Error Analysis​

Parse Simulation Errors​

function parseSimulationError(vmStatus: string): {
code: string;
description: string;
suggestion: string;
} {
const errorMap: Record<string, {description: string; suggestion: string}> = {
"SEQUENCE_NUMBER_TOO_OLD": {
description: "Transaction sequence number is outdated",
suggestion: "Refresh account state and retry",
},
"INSUFFICIENT_BALANCE_FOR_TRANSACTION_FEE": {
description: "Not enough APT to pay for gas",
suggestion: "Add more APT to your account",
},
"TRANSACTION_EXPIRED": {
description: "Transaction expiration time has passed",
suggestion: "Create a new transaction with updated expiration",
},
"INVALID_SIGNATURE": {
description: "Transaction signature is invalid",
suggestion: "Check your signing key and retry",
},
"FUNCTION_NOT_FOUND": {
description: "The called function does not exist",
suggestion: "Verify the module and function names",
},
};

const errorInfo = errorMap[vmStatus] || {
description: "Unknown error occurred",
suggestion: "Check transaction parameters",
};

return {
code: vmStatus,
...errorInfo,
};
}

Performance Optimization​

Simulation Caching​

class SimulationCache {
private cache = new Map<string, any>();
private ttl = 10000; // 10 seconds

private getKey(sender: string, payload: any): string {
return `${sender}:${JSON.stringify(payload)}`;
}

async simulate(
sender: Account,
payload: any,
forceRefresh = false
) {
const key = this.getKey(sender.accountAddress.toString(), payload);
const cached = this.cache.get(key);

if (!forceRefresh && cached && Date.now() - cached.timestamp < this.ttl) {
return cached.result;
}

const transaction = await aptos.transaction.build.simple({
sender: sender.accountAddress,
data: payload,
});

const [result] = await aptos.transaction.simulate.simple({
signerPublicKey: sender.publicKey,
transaction,
});

this.cache.set(key, {
result,
timestamp: Date.now(),
});

return result;
}
}

Need help? Contact our support team or check the Aptos documentation.