Este artículo es una traducción de Nawodya Ishan, hecha por Héctor Botero. Puedes encontrar el artículo original aquí.
Sería genial escucharte en nuestro Discord, puedes contarnos tus ideas, comentarios, sugerencias y dejarnos saber lo que necesitas.
Si prefieres puedes escribirnos a @web3dev_eshttps://twitter.com/web3dev_es en Twitter.
Introducción
Avalanche es una nueva plataforma de blockchain, relativamente nueva, que apunta a resolver algunos de los problemas de escalabilidad y eficiencia que otras redes de blockchain se enfrentan, como Bitcoin y Ethereum. Fue creado por un equipo de científicos de la computación, dirigido por Emin Gün Sirer, una figura pública muy conocido en la comunidad del blockchain.
Avalanche usa un novedoso mecanismo de consenso llamado “Avalanche consensus” el cual, permite una alta tasa de transferencia efectiva, baja latencia y una alta firmeza en la transacción. A diferencia de los algoritmos de consenso Proof-of-Work (Prueba de Trabajo) y el Proof-of-Stake (Prueba de Participación), Avalanche se basa en una muestra aleatoria de la red de participantes para, rápidamente, llegar a un consenso en el estado de la red.
Una de las características claves de Avalanche, es su capacidad para soportar múltiples subredes o “blockchains dentro de un blockchain”. Esto quiere decir que diferentes grupos de usuarios pueden crear sus propias subredes con sus propias reglas y sus estructuras de gobernanza mientras siguen siendo capaces de interactuar con la larga red de Avalanche.
Otra característica notable es que, Avalanche es su soporte para la creación de contratos inteligentes usando el idioma de programación de Solidity el cual, es también usado por Ethereum. Esto permite a los desarrolladores a construir aplicaciones descentralizadas (dApps) en la plataforma de Avalanche con herramientas y lenguajes familiares.
En general, Avalanche es una plataforma de blockchain prometedora que ofrece mejores significantes en escalabilidad, velocidad y flexibilidad sobre otras redes existentes de blockchain. Su mecanismo de consenso único y el soporte a múltiples subredes, hace que sea una opción interesante para los desarrolladores y para los usuarios que buscan construir aplicaciones descentralizadas o participar en un ecosistema rápido y eficiente de blockchain.
Pasos
Prerequisitos
Antes de comenzar, asegúrate tener las siguientes herramientas instaladas:
- Node.js (v14+)
- Npm (v7+)
- HardHat (v2+) 4.Solidity (v0.8+)
Primer Paso: Configurando el proyecto
Crea un nuevo directorio para el proyecto y navega en él:
mkdir simple-storage
cd simple-storage
Inicializa un nuevo proyecto Node.js e instala las dependencias requeridas:
npm init -y
npm install --save-dev hardhat @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers typescript ts-node dotenv @types/dotenv @types/mocha @types/chai avalanche
Inicializa HardHat:
npx hardhat
Elige "Create an empty hardhat.config.js" cuando te salga la opción.
Segundo Paso: Configura TypeScript y HardHat
Crea un nuevo archivo tsconfig.json
en el directorio principal del proyecto con el siguiente contenido:
{
"compilerOptions": {
"target": "es2017",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"outDir": "dist"
},
"include": ["./scripts", "./test"],
"files": ["./hardhat.config.ts"]
}
Renombra hardhat.config.js
a hardhat.config.ts
y actualiza su contenido:
import { HardhatUserConfig } from 'hardhat/config';
import '@nomiclabs/hardhat-waffle';
import '@nomiclabs/hardhat-ethers';
import 'dotenv/config';
const config: HardhatUserConfig = {
solidity: '0.8.0',
networks: {
hardhat: {
chainId: 31337,
},
},
mocha: {
timeout: 20000,
},
};
export default config;
Tercer Paso: Creando el Contrato de Almacenamiento Simple
Crea un nuevo directorio contracts
y nuevo archivo de Solidity SimpleStorage.Sol
dentro de él:
mkdir contracts
touch contracts/SimpleStorage.sol
Añade el siguiente código a SimpleStorage.Sol
:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SimpleStorage {
uint256 private _storedData;
function set(uint256 value) public {
_storedData = value;
}
function get() public view returns (uint256) {
return _storedData;
}
}
Vistazo
El Contrato de Almacenamiento simple está diseñado para demostrar las funciones básicas de un contrato inteligente, permitiéndole a los usuarios almacenar y retirar datos del blockchain de Ethereum. El contrato consiste en un solo estado variable que mantiene un número entero sin firmar y dos funciones para configurar y obtener los valores almacenados.
Estados Variables
El contrato tiene un estado variable:
-
storedData
: este número entero sin firmar (uint256
) mantiene los datos almacenados. Es el lugar central para almacenar los datos del contrato y su valor puede ser modificado usando la funciónset()
y ser retirado usando la funciónget()
.
Funciones
El contrato tiene dos funciones principales:
**set()**
Esta función permite a los usuarios actualizar el valor de la variable de estado storedData
. Toma un simple parámetro de entrada, un valor uint256
el cual representa los nuevos datos a ser almacenados.
function set(uint256 x) public {
storedData = x;
}
Cuando se invoca, esta función actualiza los estados variables de storedData
con el valor de entrada ingresado.
**get()**
Esta función permite a los usuarios retirar el valor actual del estado variable de storedData
. No toma parámetros de entrada y regresa el valor de uint256
almacenado en storedData
.
function get() public view returns (uint256) {
return storedData;
}
Cuando se invoca, esta función regresa el valor actual al estado variable de storedData
, sin modificarlo.
Flujo de Interacción
Los siguientes pasos describen el flujo de interacción típica para los usuarios que interactúan con el contrato de almacenamiento simple:
-
Despliega el contrato: el usuario despliega el contrato de almacenamiento simple al blockchain de Ethereum. Al desplegarlo, el estado variable de
storedData
es inicializado con el valor de0
. -
Configura los datos almacenados: el usuario llama la función
set()
, proveyendo el valoruint256
como entrada. La función actualiza el estado variable destoredData
con el valor proveído. -
Obtén los datos almacenados: el usuario llama la función
get()
para retirar el valor actual del estado variable destoredData
. La función regresa el valor deuint256
sin modificarlo.
Conclusión
El contrato inteligente de almacenamiento simple demuestra la funcionalidad básica de un contrato inteligente en la blockchain de Ethereum. Le permite a los usuarios almacenar y retirar el valor de los números enteros sin firmar, a través de dos funciones simples: set()
y get()
. Este contrato sirve como un punto inicial para entender los fundamentos del desarrollo de un contrato inteligente y puede ser expandido para implementar características más complejas y casos de uso.
Cuarto Paso: Escribiendo pruebas para el contrato
Crea un directorio nuevo test
y un nuevo archivo TypeScript SimpleStorage.test.ts
dentro de él:
mkdir test
touch test/SimpleStorage.test.ts
Añade el siguiente código a SimpleStorage.test.ts
:
// @ts-ignore
import { ethers, waffle } from "hardhat";
import { Contract } from "ethers";
import { expect } from "chai";
describe("SimpleStorage", () => {
let simpleStorage: Contract;
beforeEach(async () => {
const SimpleStorage = await ethers.getContractFactory("SimpleStorage");
simpleStorage = await SimpleStorage.deploy();
await simpleStorage.deployed();
});
it("Initial stored data should be 0", async () => {
const storedData = await simpleStorage.get();
expect(storedData.toString()).to.equal("0");
});
it("Should set stored data to a new value", async () => {
await simpleStorage.set(42);
const storedData = await simpleStorage.get();
expect(storedData.toString()).to.equal("42");
});
});
Quinto Paso: Compila el contrato y ejecuta pruebas
Compila el contrato usando HardHat:
npx hardhat compile
Ejecuta las pruebas:
npx hardhat test
Si todo está configurado correctamente, deberías ver que las pruebas pasaron.
Sexto Paso: Despliega el contrato en una red local
Crea un directorio nuevo scripts
y nuevo archivo TypeScripts deploy.ts
dentro de él:
mkdir scripts
touch scripts/deploy.ts
Añade el siguiente código a deploy.ts
// @ts-ignore
import { ethers } from "hardhat";
async function main() {
const SimpleStorageFactory = await ethers.getContractFactory("SimpleStorage");
const simpleStorage = await SimpleStorageFactory.deploy();
await simpleStorage.deployed();
console.log("SimpleStorage deployed to:", simpleStorage.address);
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
Ejecuta el script para desplegar:
npx hardhat run scripts/deploy.ts
Deberías ver el contrato desplegado en una dirección local.
Séptimo Paso: Despliega el contrato en la testnet de Avalanche usando Ether.js
Para desplegar el contrato en la red de prueba de Avalanche, necesitas una cuenta de prueba con AVAX. Puedes obtener alguna desde Avalanche Faucet.
Añade la siguiente configuración a tu hardhat.config.ts
:
import { HardhatUserConfig } from 'hardhat/config';
import '@nomiclabs/hardhat-waffle';
import '@nomiclabs/hardhat-ethers'; // Add this line
import 'dotenv/config';
const config: HardhatUserConfig = {
solidity: '0.8.0',
networks: {
hardhat: {
chainId: 31337,
},
fuji: {
url: "https://api.avax-test.network/ext/bc/C/rpc",
chainId: 43113,
gasPrice: 225000000000,
accounts: [process.env.PRIVATE_KEY || ""],
},
},
mocha: {
timeout: 20000,
},
};
export default config;
Asegúrate de reemplazar la PRIVATE_KEY
en el array accounts
con tu clave privada.
Ejecuta el script de despliegue en la red de prueba de Avalanche:
npx hardhat run scripts/deploy.ts --network fuji
Deberías ver el contrato desplegado en la dirección de la red de pruebas de Avalanche.
Ahora haz creado, exitosamente, un contrato de almacenamiento simple en Solidity con TypeScript, EVM y AvalanceJS, junto a las pruebas locales y despliegues de HardHat.
Octavo Paso: Interactuando con el contrato usando AvalancheJS
Para interactuar con el contrato desplegado, crea un nuevo archivo TypeScript interact;ts
dentro del directorio scripts
:
touch scripts/interact.ts
Añade el siguiente código a interact.ts
:
import {ethers} from 'ethers';
import * as dotenv from 'dotenv';
dotenv.config();
const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS as string|| '';
if (!CONTRACT_ADDRESS) {
throw new Error('Please set your contract address in the interact.ts script');
}
// Your ABI Here
const ABI = [
{
"inputs": [],
"name": "get",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "set",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
];
const FUJI_RPC_URL = "https://api.avax-test.network/ext/bc/C/rpc";
const PRIVATE_KEY = process.env.PRIVATE_KEY as string || '';
async function interact() {
dotenv.config();
if (!CONTRACT_ADDRESS) {
throw new Error("Please set your contract address in the interact.ts script");
}
const provider = new ethers.providers.JsonRpcProvider(FUJI_RPC_URL);
const wallet = new ethers.Wallet(PRIVATE_KEY, provider);
console.log("Wallet address:", wallet.address);
const simpleStorage = new ethers.Contract(CONTRACT_ADDRESS, ABI, wallet);
console.log("Contract address:", CONTRACT_ADDRESS);
console.log("Contract instance:", simpleStorage);
// Lee los datos almacenados
const storedData = await simpleStorage.get();
console.log("Stored data:", storedData.toString());
// Configura los nuevos datos
const tx = await simpleStorage.set(100);
await tx.wait();
// Lee la actualización de los datos almacenados
const updatedStoredData = await simpleStorage.get();
console.log("Updated stored data:", updatedStoredData.toString());
}
interact().then(r => console.log("Complete"));
Reemplaza YOUR_CONTRACT_ADDRESS
con la dirección de tu contrato desplegado y añade el ABI de tu contrato.
Ejecuta el script de interacción:
npx ts-node scripts/interact.ts
Deberías verlos los datos almacenados, el recibo de la transacción y los datos actualizados almacenados en la output de la cónsola:
╰─ npx ts-node scripts/interact.ts
Wallet address: 0x2E3bE6ddaC75f8dA97d14009A540E917d881ea92
Contract address: 0x0697aBc8Dc960d53911f4A8BB8989826b78CaF61
Contract instance: Contract {
interface: Interface {
fragments: [ [FunctionFragment], [FunctionFragment] ],
_abiCoder: AbiCoder { coerceFunc: null },
functions: { 'get()': [FunctionFragment], 'set(uint256)': [FunctionFragment] },
errors: {},
events: {},
structs: {},
deploy: ConstructorFragment {
name: null,
type: 'constructor',
inputs: [],
payable: false,
stateMutability: 'nonpayable',
gas: null,
_isFragment: true
},
_isInterface: true
},
provider: JsonRpcProvider {
_isProvider: true,
_events: [],
_emitted: { block: -2 },
disableCcipRead: false,
formatter: Formatter { formats: [Object] },
anyNetwork: false,
_networkPromise: Promise { <pending> },
_maxInternalBlockNumber: -1024,
_lastBlockNumber: -2,
_maxFilterBlockRange: 10,
_pollingInterval: 4000,
_fastQueryDate: 0,
connection: { url: 'https://api.avax-test.network/ext/bc/C/rpc' },
_nextId: 42
},
signer: Wallet {
_isSigner: true,
_signingKey: [Function (anonymous)],
_mnemonic: [Function (anonymous)],
address: '0x2E3bE6ddaC75f8dA97d14009A540E917d881ea92',
provider: JsonRpcProvider {
_isProvider: true,
_events: [],
_emitted: [Object],
disableCcipRead: false,
formatter: [Formatter],
anyNetwork: false,
_networkPromise: [Promise],
_maxInternalBlockNumber: -1024,
_lastBlockNumber: -2,
_maxFilterBlockRange: 10,
_pollingInterval: 4000,
_fastQueryDate: 0,
connection: [Object],
_nextId: 42
}
},
callStatic: {
'get()': [Function (anonymous)],
'set(uint256)': [Function (anonymous)],
get: [Function (anonymous)],
set: [Function (anonymous)]
},
estimateGas: {
'get()': [Function (anonymous)],
'set(uint256)': [Function (anonymous)],
get: [Function (anonymous)],
set: [Function (anonymous)]
},
functions: {
'get()': [Function (anonymous)],
'set(uint256)': [Function (anonymous)],
get: [Function (anonymous)],
set: [Function (anonymous)]
},
populateTransaction: {
'get()': [Function (anonymous)],
'set(uint256)': [Function (anonymous)],
get: [Function (anonymous)],
set: [Function (anonymous)]
},
filters: {},
_runningEvents: {},
_wrappedEmits: {},
address: '0x0697aBc8Dc960d53911f4A8BB8989826b78CaF61',
resolvedAddress: Promise { <pending> },
'get()': [Function (anonymous)],
'set(uint256)': [Function (anonymous)],
get: [Function (anonymous)],
set: [Function (anonymous)]
}
Stored data: 0
Updated stored data: 100
Complete
¡Y eso es todo! Haz desarrollado, exitosamente, un contrato de almacenamiento simple en Solidity con TypeScript, EVM, HardHat y EtherJS, incluyendo la prueba local, despliegues en la red de prueba de Avalanche e interacciones con el contrato desplegado.
Discussion (0)