Las aplicaciones descentralizadas (dApps) utilizan una blockchain o contratos inteligentes en cadena para almacenar y referenciar datos, en lugar de depender de bases de datos centralizadas tradicionales. Una estructura común y simple de una dApp generalmente consiste en un front-end en React.js o Vue.js que utiliza Web3.js o Ethers.js para interactuar con contratos inteligentes desplegados en una blockchain compatible con EVM.
En este tutorial, aprenderás cómo crear una lista de tareas donde podrás agregar tareas y marcarlas como completadas o eliminarlas en la red CORE.
- Git v2.44.0
- Node.js v20.11.1
- npm v10.2.4
- Hardhat v10.2.4
- MetaMask Web Wallet Extension
-
Descarga este repositorio
-
Instala las dependencias en la ruta /contract.
npm install
-
Instala y configura la Extensión MetaMask para Chrome para usar con Core Testnet. Consulta aquí para una guía detallada.
-
Crea un archivo secret.json en la carpeta /contract y almacena la clave privada de tu billetera MetaMask en él. Consulta aquí para detalles sobre cómo obtener la clave privada de la cuenta MetaMask. Ejemplo:
{"PrivateKey":"ef1150b212a53b053a3dee265cb26cd010065b9340b4ac6cf5d895a7cf39c923"}
:::Advertencia: No olvides añadir este archivo al archivo .gitignore en la carpeta raíz de tu proyecto para evitar que tu clave privada/frases secretas se suban accidentalmente a un repositorio público. ¡Asegúrate de mantener este archivo en un lugar absolutamente seguro! :::
- Copia lo siguiente en tu archivo hardhat.config.js en /contract
/**
* @type import('hardhat/config').HardhatUserConfig
*/
require('@nomiclabs/hardhat-ethers');
require("@nomiclabs/hardhat-waffle");
const { PrivateKey } = require('./secret.json');
module.exports = {
defaultNetwork: 'testnet',
networks: {
hardhat: {
},
testnet: {
url: 'https://rpc.test.btcs.network',
accounts: [PrivateKey],
chainId: 1115,
}
},
solidity: {
compilers: [
{
version: '0.8.24',
settings: {
evmVersion: 'paris',
optimizer: {
enabled: true,
runs: 200,
},
},
},
],
},
paths: {
sources: './contracts',
cache: './cache',
artifacts: './artifacts',
},
mocha: {
timeout: 20000,
},
};
- Dentro de la carpeta /contract/contracts se encuentra el archivo
TodoList.sol
que contendrá el código del contrato inteligente a utilizar en este tutorial.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract TodoList {
struct Task {
uint id;
string content;
bool completed;
}
mapping(address => Task[]) private userTasks;
mapping(address => mapping(uint => uint)) private userTaskIndex;
mapping(address => uint) private taskCounts;
event TaskCreated(address indexed user, uint id, string content);
event TaskCompleted(address indexed user, uint id);
event TaskRemoved(address indexed user, uint id);
event TaskUpdated(address indexed user, uint id, string newContent);
function createTask(string calldata _content) external {
uint taskId = taskCounts[msg.sender]++;
userTasks[msg.sender].push(Task(taskId, _content, false));
userTaskIndex[msg.sender][taskId] = userTasks[msg.sender].length - 1;
emit TaskCreated(msg.sender, taskId, _content);
}
function getTasks(uint _startIndex, uint _limit) external view returns (Task[] memory) {
Task[] memory allTasks = userTasks[msg.sender];
uint totalTasks = allTasks.length;
require(_startIndex < totalTasks, "Start index out of bounds");
uint endIndex = _startIndex + _limit > totalTasks ? totalTasks : _startIndex + _limit;
uint taskCount = endIndex - _startIndex;
Task[] memory paginatedTasks = new Task[](taskCount);
for (uint i = 0; i < taskCount; i++) {
paginatedTasks[i] = allTasks[totalTasks - 1 - (_startIndex + i)];
}
return paginatedTasks;
}
function completeTask(uint _taskId) external {
uint index = userTaskIndex[msg.sender][_taskId];
require(index < userTasks[msg.sender].length, "Task does not exist");
Task storage task = userTasks[msg.sender][index];
require(!task.completed, "Task already completed");
task.completed = true;
emit TaskCompleted(msg.sender, _taskId);
}
function removeTask(uint _taskId) external {
uint index = userTaskIndex[msg.sender][_taskId];
Task[] storage tasks = userTasks[msg.sender];
require(index < tasks.length, "Task does not exist");
tasks[index] = tasks[tasks.length - 1];
userTaskIndex[msg.sender][tasks[index].id] = index;
tasks.pop();
delete userTaskIndex[msg.sender][_taskId];
emit TaskRemoved(msg.sender, _taskId);
}
function updateTask(uint _taskId, string calldata _newContent) external {
uint index = userTaskIndex[msg.sender][_taskId];
Task[] storage tasks = userTasks[msg.sender];
require(index < tasks.length, "Task does not exist");
Task storage task = tasks[index];
task.content = _newContent;
emit TaskUpdated(msg.sender, _taskId, _newContent);
}
}
- Para compilar el contrato inteligente
TodoList
definido enTodoList.sol
, desde el directorio /contract ejecuta el siguiente comando. (Cada vez que se realice un cambio en el código del contrato, debemos recompilarlo).
npx hardhat compile
-
Antes de desplegar tu contrato inteligente en la cadena Core, es aconsejable ejecutar una serie de pruebas para asegurarse de que el contrato funciona como se desea. Consulta la guía detallada aquí para más detalles.
-
Crea una carpeta
scripts
en el directorio /contract de tu proyecto. Dentro de esta carpeta, crea un archivodeploy.js
; pega el siguiente script en él.
async function main() {
const [deployer] = await ethers.getSigners();
console.log("Deploy contract with the account:", deployer.address);
const TodoList = await ethers.getContractFactory("TodoList");
const todoList = await TodoList.deploy();
console.log("Contract Address:", todoList.address);
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
-
Asegúrate de que tu billetera MetaMask tenga tokens de prueba tCORE para la Red de Pruebas Core. Consulta aquí para detalles sobre cómo obtener tokens tCORE.
-
Ejecuta el siguiente comando desde el directorio raíz de tu proyecto para desplegar tu contrato inteligente en la cadena Core.
npx hardhat run scripts/deploy.js
- En la carpeta raíz, instala todas las dependencias.
npm install
-
En la ruta src/contractABI debemos copiar el ABI de nuestro contrato inteligente en caso de hacer modificaciones, esta información se obtendrá de contract/artifacts/contracts/TodoList.json.
-
Una vez que el contrato inteligente esté desplegado, es necesario copiar la dirección y reemplazarla en cada uno de los componentes donde realizamos llamadas al contrato, en este caso src/components/New.tsx y src/components/Get.tsx.
-
Para probar si todo está funcionando correctamente, ejecuta la aplicación utilizando el siguiente comando. Esto servirá la aplicación con la función de recarga en caliente en http://localhost:5173
npm run dev
- Para agregar una nueva tarea, primero debe ingresar el texto o la descripción de la tarea.
- Una vez hecho esto, haga clic en el botón "Add Task" y acepte la transacción en metamask.
- Para marcar una tarea como completada, primero debes dirigirte a la opción "Task List" del menú.
- Debes localizar la tarea que deseas marcar como completada y luego hacer clic en el botón "Complete".
- Por último, solo tendrás que aceptar la transacción en metamask y la tarea quedará marcada como completada.
- Para eliminar una tarea, primero debes dirigirte a la opción "Task List" del menú.
- Debes localizar la tarea que deseas eliminar y luego hacer clic en el botón "Remove".
- Por último, solo tendrás que aceptar la transacción en metamask y la tarea será eliminada.