Saltearse al contenido

Tu Primera Transacción

Las transacciones son la forma fundamental de cambiar la data en la blockchain de Aptos. Piénsalo como enviar un paquete: necesitas especificar qué envías, a quién va dirigido, y luego rastrearlo hasta que se confirme el delivery. En términos de blockchain, las transacciones te permiten transferir coins, llamar funciones de smart contracts y actualizar el estado on-chain.

Este tutorial te guiará para crear y hacer submit de tu primera transacción en la blockchain de Aptos. Aprenderás cómo:

  1. Configurar tu entorno de desarrollo
  2. Crear cuentas de prueba y fondearlas
  3. Construir una transacción para transferir coins
  4. Simular la transacción para estimar costos
  5. Firmar y hacer submit de la transacción
  6. Verificar que la transacción se ejecutó exitosamente

Antes de que podamos crear transacciones, necesitamos configurar nuestro entorno de desarrollo con las herramientas y SDKs necesarios.

  1. Instalar el SDK de TypeScript

    Instala el SDK de TypeScript usando tu package manager preferido:

    Ventana de terminal
    npm install @aptos-labs/ts-sdk
  2. Crear un directorio para el proyecto

    Crea un directorio nuevo para tu proyecto:

    Ventana de terminal
    mkdir my-first-transaction
    cd my-first-transaction
  3. Crear un archivo nuevo

    Crea un archivo nuevo llamado transaction.ts:

    Ventana de terminal
    touch transaction.ts

En blockchain, todas las transacciones deben venir de una cuenta (account). Vamos a crear dos cuentas de prueba: una para enviar coins (Alice) y otra para recibirlos (Bob).

  1. Configurar el cliente

    Primero, necesitamos inicializar el cliente de Aptos que se conectará a la blockchain. Abre transaction.ts en tu editor y agrega:

    import {
    Account,
    Aptos,
    AptosConfig,
    Network,
    } from "@aptos-labs/ts-sdk";
    async function main() {
    // Inicializar el cliente de Aptos
    const config = new AptosConfig({ network: Network.DEVNET });
    const aptos = new Aptos(config);
    console.log("Conectado a Aptos devnet");
    // Más código irá aquí
    }
    main().catch(console.error);
  2. Generar cuentas

    Agrega este código dentro de tu función main() para crear dos cuentas: Alice (sender) y Bob (receiver):

    // Generar dos cuentas
    const alice = Account.generate();
    const bob = Account.generate();
    console.log("=== Direcciones ===");
    console.log(`Dirección de Alice: ${alice.accountAddress}`);
    console.log(`Dirección de Bob: ${bob.accountAddress}`);
  3. Fondear las cuentas

    Agrega este código después de generar las cuentas para obtener coins de prueba desde el faucet:

    // Fondear las cuentas con APT de prueba del faucet de devnet
    console.log("\n=== Fondeando cuentas ===");
    await aptos.fundAccount({
    accountAddress: alice.accountAddress,
    amount: 100_000_000, // 1 APT = 100,000,000 octas
    });
    console.log("Cuentas fondeadas exitosamente");
    // Revisar balances iniciales
    const aliceBalance = await aptos.getAccountAPTAmount({
    accountAddress: alice.accountAddress,
    });
    const bobBalance = await aptos.getAccountAPTAmount({
    accountAddress: bob.accountAddress,
    });
    console.log("\n=== Balances Iniciales ===");
    console.log(`Alice: ${aliceBalance} octas`);
    console.log(`Bob: ${bobBalance} octas`);
  4. Correr el código

    Probemos nuestro código hasta ahora:

    Ventana de terminal
    npx ts-node transaction.ts

    Deberías ver una salida similar a:

    Conectado a Aptos devnet
    === Direcciones ===
    Dirección de Alice: 0x978c213990c4833df71548df7ce49d54c759d6b6d932de22b24d56060b7af2aa
    Dirección de Bob: 0x7af2d6c93a2feafc9b69b5e8ad9d6b513b260f62f23f3a384a3a2e4a84694a9b
    === Fondeando cuentas ===
    Cuentas fondeadas exitosamente
    === Balances Iniciales ===
    Alice: 100000000 octas
    Bob: 0 octas

Ahora que tenemos cuentas fondeadas, vamos a crear una transacción para transferir coins de Alice a Bob. Esto es como llenar un formulario especificando qué quieres enviar y a quién.

  1. Entender la estructura de la transacción

    Una transacción en Aptos tiene varios componentes clave:

    1. Sender: La cuenta que inicia la transacción (Alice)
    2. Function: La función on-chain a llamar (en este caso, una transferencia de coins)
    3. Arguments: Data necesaria por la función (dirección del recipiente y monto)
    4. Gas parameters: Cantidad máxima de gas (max gas amount) y precio por unidad de gas (gas unit price)
    5. Expiration time: Cuándo la transacción deja de ser válida si no se ha ejecutado
    6. Sequence number: Un contador que previene ataques de replay
  2. Construir la transacción

    Vamos a agregar código para construir una transacción que transfiere 1000 octas de Alice a Bob:

    Agrega este código a tu función main():

    // 1. Construir la transacción
    console.log("\n=== 1. Construyendo la transacción ===");
    const transaction = await aptos.transaction.build.simple({
    sender: alice.accountAddress,
    data: {
    function: "0x1::aptos_account::transfer",
    functionArguments: [bob.accountAddress, 1000], // Transferir 1000 octas
    },
    });
    console.log("Transacción construida exitosamente");
    // Acceder a detalles de la transacción desde la raw transaction
    const 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()}`);

Antes de hacer submit de una transacción, es una buena práctica simularla primero para estimar el costo del gas. Esto es como revisar los costos de envío antes de mandar un paquete.

  1. Simular la transacción

    Agrega este código después de construir la transacción:

    // 2. Simular la transacción
    console.log("\n=== 2. Simulando la transacción ===");
    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(`Unidades de gas estimadas: ${gasUsed}`);
    console.log(`Costo de gas estimado: ${gasUsed * gasUnitPrice} octas`);
    console.log(`La transacción sería ${simulationResult.success ? "exitosa" : "fallida"}`);

5. Firmando y haciendo Submit de la Transacción

Sección titulada «5. Firmando y haciendo Submit de la Transacción»

Ahora que hemos construido y simulado la transacción, necesitamos firmarla con la private key de Alice y hacerle submit a la blockchain.

  1. Firmar la transacción

    La firma prueba que Alice autorizó esta transacción:

    Agrega este código después de simular la transacción:

    // 3. Firmar la transacción
    console.log("\n=== 3. Firmando la transacción ===");
    const senderAuthenticator = aptos.transaction.sign({
    signer: alice,
    transaction,
    });
    console.log("Transacción firmada exitosamente");
  2. Hacer submit de la transacción

    Agrega este código después de firmar la transacción para enviar la transacción firmada a la blockchain:

    // 4. Hacer submit de la transacción
    console.log("\n=== 4. Haciendo submit de la transacción ===");
    const pendingTransaction = await aptos.transaction.submit.simple({
    transaction,
    senderAuthenticator,
    });
    console.log(`Transacción enviada con hash: ${pendingTransaction.hash}`);

Después de hacer submit de una transacción, necesitamos esperar a que sea procesada por la blockchain. Esto es como esperar a que te entreguen el paquete.

  1. Esperar a que se complete la transacción

    Agrega este código después de enviar la transacción:

    // 5. Esperar a que la transacción se complete
    console.log("\n=== 5. Esperando a que se complete la transacción ===");
    const txnResult = await aptos.waitForTransaction({
    transactionHash: pendingTransaction.hash,
    });
    console.log(`Transacción completada con estado: ${txnResult.success ? "SUCCESS" : "FAILURE"}`);
    // Si quieres ver más detalles sobre la transacción:
    console.log(`VM Status: ${txnResult.vm_status}`);
    console.log(`Gas used: ${txnResult.gas_used}`);
  2. Verificar los resultados

    Agrega este código después de esperar a la transacción para revisar los balances y confirmar que la transferencia funcionó:

    // Revisar balances finales
    const aliceFinalBalance = await aptos.getAccountAPTAmount({
    accountAddress: alice.accountAddress,
    });
    const bobFinalBalance = await aptos.getAccountAPTAmount({
    accountAddress: bob.accountAddress,
    });
    console.log("\n=== Balances Finales ===");
    console.log(`Alice: ${aliceFinalBalance} octas (gastó ${aliceBalance - aliceFinalBalance} octas en transferencia y gas)`);
    console.log(`Bob: ${bobFinalBalance} octas (recibió 1000 octas)`);
  3. Correr el código completo

    Ventana de terminal
    npx ts-node transaction.ts

    Deberías ver una salida similar a:

    Conectado a Aptos devnet
    === Direcciones ===
    Dirección de Alice: 0x978c213990c4833df71548df7ce49d54c759d6b6d932de22b24d56060b7af2aa
    Dirección de Bob: 0x7af2d6c93a2feafc9b69b5e8ad9d6b513b260f62f23f3a384a3a2e4a84694a9b
    === Fondeando cuentas ===
    Cuentas fondeadas exitosamente
    === Balances Iniciales ===
    Alice: 100000000 octas
    Bob: 0 octas
    === 1. Construyendo la transacción ===
    Transacción construida exitosamente
    Sender: 0x978c213990c4833df71548df7ce49d54c759d6b6d932de22b24d56060b7af2aa
    Sequence Number: 0
    Max Gas Amount: 20000
    Gas Unit Price: 100
    Expiration Timestamp: 2025-03-05T22:59:21.000Z
    === 2. Simulando la transacción ===
    Unidades de gas estimadas: 146
    Costo de gas estimado: 14600 octas
    La transacción sería exitosa
    === 3. Firmando la transacción ===
    Transacción firmada exitosamente
    === 4. Haciendo submit de la transacción ===
    Transacción enviada con hash: 0x3a8a3e34a1c64ad9d7636a3a827b7ec3bb12d73825b36fa06d425c5a3b42cccc
    === 5. Esperando a que se complete la transacción ===
    Transacción completada con estado: SUCCESS
    VM Status: Executed successfully
    Gas used: 146
    === Balances Finales ===
    Alice: 99984400 octas (gastó 15600 octas en transferencia y gas)
    Bob: 1000 octas (recibió 1000 octas)

7. (Opcional) Explora tu Transacción On-Chain

Sección titulada «7. (Opcional) Explora tu Transacción On-Chain»

Ahora que has ejecutado exitosamente una transacción, puedes explorarla en el Aptos Explorer. Esto te ayudará a entender cómo se registran las transacciones en la blockchain y qué información está disponible públicamente.

  1. Copia tu transaction hash

    Desde la salida de tu terminal, copia el hash de la transacción que se imprimió después del submit. Se ve algo así:

    Transacción enviada con hash: 0x3a8a3e34a1c64ad9d7636a3a827b7ec3bb12d73825b36fa06d425c5a3b42cccc
  2. Abre el Aptos Explorer

    Ve al Aptos Explorer.

  3. Asegúrate de estar en Devnet

    Busca “Devnet” en la esquina superior derecha, o cambia de red haciendo clic en el dropdown y seleccionando Devnet.

    Cambiando a la red Devnet en Aptos Explorer

  4. Busca tu transacción

    Pega tu transaction hash en la barra de búsqueda en el medio de la página.

  5. Mira los detalles de la transacción

    Espera a que aparezcan los resultados, luego haz clic en el transaction hash para ver sus detalles.

    Deberías ver información sobre tu transacción, incluyendo:

    • Estado (debería ser “Success”)
    • Timestamp
    • Gas used
    • Direcciones del sender y el recipiente
    • Monto transferido
  6. Explora más a fondo

    Desde la página de detalles de la transacción, puedes:

    • Hacer clic en las direcciones del sender o recipiente para ver los detalles de su cuenta
    • Ver los cambios exactos hechos al estado de la blockchain
    • Ver el payload de la transacción y los argumentos

¡Felicitaciones! Has creado y ejecutado exitosamente tu primera transacción en la blockchain de Aptos. Aquí hay algunas sugerencias para explorar a continuación:

Aprende sobre transacciones más complejas:

Explora smart contracts o conceptos básicos de cuentas:

¡Únete al Discord de Aptos y comparte lo que estás construyendo!

Los ejemplos de código completo abajo combinan todos los snippets que hemos cubierto en este tutorial:

import { Account, Aptos, AptosConfig, Network } from "@aptos-labs/ts-sdk";
async function main() {
// Inicializar el cliente de Aptos
const config = new AptosConfig({ network: Network.DEVNET });
const aptos = new Aptos(config);
console.log("Conectado a Aptos devnet");
// Más código irá aquí
// Generar dos cuentas
const alice = Account.generate();
const bob = Account.generate();
console.log("=== Direcciones ===");
console.log(`Dirección de Alice: ${alice.accountAddress}`);
console.log(`Dirección de Bob: ${bob.accountAddress}`);
// Fondear las cuentas con APT de prueba del faucet de devnet
console.log("\n=== Fondeando cuentas ===");
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 empieza con 0 APT
});
console.log("Cuentas fondeadas exitosamente");
// Revisar balances iniciales
const aliceBalance = await aptos.getAccountAPTAmount({
accountAddress: alice.accountAddress,
});
const bobBalance = await aptos.getAccountAPTAmount({
accountAddress: bob.accountAddress,
});
console.log("\n=== Balances Iniciales ===");
console.log(`Alice: ${aliceBalance} octas`);
console.log(`Bob: ${bobBalance} octas`);
// 1. Construir la transacción
console.log("\n=== 1. Construyendo la transacción ===");
const transaction = await aptos.transaction.build.simple({
sender: alice.accountAddress,
data: {
function: "0x1::aptos_account::transfer",
functionArguments: [bob.accountAddress, 1000], // Transferir 1000 octas
},
});
console.log("Transacción construida exitosamente");
// Usar aserción de tipo para saltar el chequeo de tipos de TypeScript
const txnAny = transaction as any;
console.log(`Sender: ${alice.accountAddress}`); // Usar la dirección de sender conocida
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. Simular la transacción
console.log("\n=== 2. Simulando la transacción ===");
const [simulationResult] = await aptos.transaction.simulate.simple({
signerPublicKey: alice.publicKey,
transaction,
});
console.log(`Unidades de gas estimadas: ${simulationResult.gas_used}`);
console.log(
`Costo de gas estimado: ${
Number(simulationResult.gas_used) * Number(simulationResult.gas_unit_price)
} octas`
);
console.log(
`La transacción sería ${simulationResult.success ? "exitosa" : "fallida"}`
);
// 3. Firmar la transacción
console.log("\n=== 3. Firmando la transacción ===");
const senderAuthenticator = aptos.transaction.sign({
signer: alice,
transaction,
});
console.log("Transacción firmada exitosamente");
// Usar aserción de tipo para saltar el chequeo de tipos de TypeScript
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. Hacer submit de la transacción
console.log("\n=== 4. Haciendo submit de la transacción ===");
const pendingTransaction = await aptos.transaction.submit.simple({
transaction,
senderAuthenticator,
});
console.log(`Transacción enviada con hash: ${pendingTransaction.hash}`);
// 5. Esperar a que la transacción se complete
console.log("\n=== 5. Esperando a que se complete la transacción ===");
const txnResult = await aptos.waitForTransaction({
transactionHash: pendingTransaction.hash,
});
console.log(
`Transacción completada con estado: ${
txnResult.success ? "SUCCESS" : "FAILURE"
}`
);
// Si quieres ver más detalles sobre la transacción:
console.log(`VM Status: ${txnResult.vm_status}`);
console.log(`Gas used: ${txnResult.gas_used}`);
// Revisar balances finales
const aliceFinalBalance = await aptos.getAccountAPTAmount({
accountAddress: alice.accountAddress,
});
const bobFinalBalance = await aptos.getAccountAPTAmount({
accountAddress: bob.accountAddress,
});
console.log("\n=== Balances Finales ===");
console.log(
`Alice: ${aliceFinalBalance} octas (gastó ${
aliceBalance - aliceFinalBalance
} octas en transferencia y gas)`
);
console.log(`Bob: ${bobFinalBalance} octas (recibió 1000 octas)`);
}
main().catch(console.error);