1. Crear un Contrato Inteligente
Este es el primer capítulo del tutorial sobre construir una dapp de extremo a extremo en Aptos. Si no lo has hecho, revisa esa introducción y asegúrate de que tu entorno cumpla con los requisitos previos listados allí.
Ahora que estás todo configurado, exploremos el directorio contract
.
¿Qué es un archivo Move.toml
?
Sección titulada «¿Qué es un archivo Move.toml?»Un archivo Move.toml
es un archivo de manifiesto que contiene metadatos como nombre, versión y dependencias para el paquete.
Echa un vistazo al nuevo archivo Move.toml
. Deberías ver la información de tu paquete y una dependencia AptosFramework
. La dependencia AptosFramework
apunta a la rama principal del repositorio GitHub aptos-core/aptos-move/framework/aptos-framework
.
¿Por qué el directorio sources
?
Sección titulada «¿Por qué el directorio sources?»El directorio sources
contiene una colección de archivos de módulos .move
. Y más tarde cuando queramos compilar el paquete usando el CLI, el compilador buscará ese directorio sources
y su archivo Move.toml
.
¿Qué es el directorio tests
?
Sección titulada «¿Qué es el directorio tests?»El directorio tests
contiene archivos .move
que se usan para probar los archivos en nuestro directorio sources
.
Crear un módulo Move
Sección titulada «Crear un módulo Move»Se necesita una cuenta para publicar un módulo Move. Cuando instalamos la plantilla, la herramienta creó una nueva cuenta para nosotros y la agregó al archivo .env
.
Si abres ese archivo, verás contenido parecido a:
PROJECT_NAME=my-aptos-dappVITE_APP_NETWORK=devnetVITE_APTOS_API_KEY=""VITE_MODULE_PUBLISHER_ACCOUNT_ADDRESS=0x1cecfef9e239eff12fb1a3d189a121c37f48908d86c0e9c02ec103e0a05ddebb#Esta es la clave privada de la cuenta publicadora del módulo. Ten cuidado con quién la compartes, y asegúrate de que no se exponga al desplegar tu dApp.VITE_MODULE_PUBLISHER_ACCOUNT_PRIVATE_KEY=0x84638fd5c42d0937503111a587307169842f355ab661b5253c01cfe389373f43
La plantilla Boilerplate viene con un archivo message_board.move
pregenerado, un archivo de prueba relevante y un archivo Move.toml
.
Como se mencionó, nuestro directorio sources contiene nuestros archivos de módulos .move
; así que creemos un nuevo archivo todolist.move
.
- Crea un nuevo archivo
todolist.move
dentro del directoriosources
y agrega lo siguiente a ese archivo:
module todolist_addr::todolist {
}
- Abre el archivo
Move.toml
. - Agrega el siguiente código a ese archivo Move:
[addresses]todolist_addr='_'
¿Qué es el '_'
en el archivo Move.toml
?
Sección titulada «¿Qué es el '_' en el archivo Move.toml?»El '_'
es un marcador de posición para la dirección de cuenta. Cuando ejecutemos el compilador move
, el compilador lo reemplazará con la dirección de cuenta real.
create-aptos-dapp
viene con scripts premade para ejecutar fácilmente comandos move
, como compile
, test
y publish
.
- Abre cada uno de los archivos en el directorio
scripts/move
y actualiza la variablemessage_board_addr
para que seatodolist_addr
.
... namedAddresses: { todolist_addr: process.env.VITE_MODULE_PUBLISHER_ACCOUNT_ADDRESS, },...
Nuestra lógica de contrato
Sección titulada «Nuestra lógica de contrato»Ahora que tenemos nuestro módulo configurado, agreguemos algunas funcionalidades a él.
Nuestra aplicación de lista de tareas necesitará:
- La capacidad de crear una nueva lista de tareas
- La capacidad de crear una nueva tarea en esa lista
- La capacidad de marcar una tarea como completada
Actualiza el contenido del archivo todolist.move
para incluir la lógica del contrato inteligente completo:
module todolist_addr::todolist { use std::signer; use aptos_framework::account; use std::string::String; use aptos_framework::event; use std::vector;
// Estructura para una tarea individual struct Task has store, drop, copy { task_id: u64, address: address, content: String, completed: bool, }
// Estructura para la lista de tareas de un usuario struct TodoList has key { tasks: vector<Task>, set_task_event: event::EventHandle<Task>, task_counter: u64 }
/// No hay una lista de tareas encontrada en la dirección dada const E_NOT_INITIALIZED: u64 = 1; /// La tarea no existe const E_TASK_DOESNT_EXIST: u64 = 2; /// La tarea ya está completada const E_TASK_IS_COMPLETED: u64 = 3;
public entry fun create_list(account: &signer){ let todo_list = TodoList { tasks: vector::empty(), set_task_event: account::new_event_handle<Task>(account), task_counter: 0 }; // mover la TodoList bajo la cuenta del firmante move_to(account, todo_list); }
public entry fun create_task(account: &signer, content: String) acquires TodoList { // obtiene la dirección del firmante let signer_address = signer::address_of(account); // afirma que el firmante tiene creada una lista assert!(exists<TodoList>(signer_address), E_NOT_INITIALIZED); // obtiene la TodoList del firmante let todo_list = borrow_global_mut<TodoList>(signer_address); // incrementa el contador de tareas let counter = todo_list.task_counter + 1; // crea una nueva Tarea let new_task = Task { task_id: counter, address: signer_address, content, completed: false }; // agrega la nueva tarea al vector de tareas vector::push_back(&mut todo_list.tasks, new_task); // actualiza el contador de tareas todo_list.task_counter = counter; // emite un evento de nueva tarea event::emit_event<Task>( &mut todo_list.set_task_event, new_task, ); }
public entry fun complete_task(account: &signer, task_id: u64) acquires TodoList { // obtiene la dirección del firmante let signer_address = signer::address_of(account); // afirma que el firmante tiene creada una lista assert!(exists<TodoList>(signer_address), E_NOT_INITIALIZED); // obtiene la TodoList del firmante let todo_list = borrow_global_mut<TodoList>(signer_address); // afirma que la tarea existe assert!(task_id > 0 && task_id <= todo_list.task_counter, E_TASK_DOESNT_EXIST); // encuentra la tarea en el vector let tasks_len = vector::length(&todo_list.tasks); let i = 0; while (i < tasks_len) { let task_ref = vector::borrow_mut(&mut todo_list.tasks, i); if (task_ref.task_id == task_id) { // afirma que la tarea no está completada assert!(task_ref.completed == false, E_TASK_IS_COMPLETED); // actualiza el estado de la tarea a completada task_ref.completed = true; }; i = i + 1; } }
#[view] public fun get_tasks(account_addr: address): vector<Task> acquires TodoList { assert!(exists<TodoList>(account_addr), E_NOT_INITIALIZED); let todo_list = borrow_global<TodoList>(account_addr); todo_list.tasks }}
¡Excelente! Ahora tenemos nuestro contrato inteligente completo. Procedamos a compilar y desplegar nuestro contrato.
Compilar nuestro contrato
Sección titulada «Compilar nuestro contrato»Para compilar nuestro contrato, ejecuta:
npm run move:compile
Si todo está configurado correctamente, deberías ver una salida exitosa de compilación.
Desplegar nuestro contrato
Sección titulada «Desplegar nuestro contrato»Para desplegar nuestro contrato a devnet, ejecuta:
npm run move:publish
¡Felicidades! Has creado y desplegado exitosamente tu primer contrato inteligente Move en Aptos.
En el siguiente capítulo, configuraremos el frontend para interactuar con nuestro contrato inteligente.