El hype por las Aplicaciones Descentralizadas(DApps) no para. Esto genera que muchos desarrolladores tengan la necesidad de experimentar este maravilloso mundo llamado Web3.
Todo sobre la Web3 suena maravilloso hasta que, buscas un tutorial, y el resultado es completa desesperación, una alarma a 30 minutos de sonar y mucho café con Korn a todo volumen.
Puedes tomar el texto anterior como un relato personal. El ecosistema Web3 es muy cambiante e involucra mucha adaptación. Despiertas un lunes por la mañana y descubres que se han lanzado docenas librerías, conceptos nuevos que estudiar, Tokens, The Merge(1)
, y personas tirando hate en Twitter porque Solana no sube a $40 USD.
En resumen, Web3 puede generar muchísimo FOMO(2)
sin importar la experiencia que tengas como desarrollador, tu background profesional, y mucho menos seniority.
Dando finiquito a la introducción anterior. A continuación podrás leer una guía detallada y elaborada con cariño para todos los interesados en adentrarse a este mundo. Tú puedes, no estás sólo en esto 🤗.
Al inicio de los tiempos dios creó la Web 1.0, sitios estáticos, contenido estático, y mucho texto con hipervínculos a más contenido estático.
La Web 2.0 es la Web que todos conocemos y consumimos. Esa popular y llena de memes “Internet”. Contenido dinámico y aplicaciones para consumir servicios, productos o interactuar con tus amigos de Facebook y Discord.
Web 3.0 es un concepto de descentralización de estos servicios, productos y aplicaciones. Un lugar dónde no existen intermediarios, y mucho menos empresas que se alimenten de tu información.
Ethereum(3)
es una red de computadores o “nodos” distribuidos globalmente*.*
Cada nodo en esta red comparte una réplica o “estado” de la información disponible en la red. Este “estado” es definido por un cómputo único llamado “Ethereum Virtual Machine” (4)
.
En Ethereum, cada nodo es partícipe en un consenso para cada evento que intente modificar el “estado” de la EVM. Ya que esto requiere poder de cómputo, a los nodos participantes se les aporta ETH(Ether) como incentivo.
Podemos imaginar “Ethereum” como una gigantesca “Máquina de estados finitos” (Finite State Machine ó FSM)
(5)
.
Con la EVM podemos manipular/condicionar el estado de la red usando “Contratos Inteligentes” (Smart Contracts).
IMPORTANTE: Para la fecha de lanzamiento de esta guía, Ethereum migró de Proof of Work a Proof of Stake, https://vitalik.ca/general/2017/12/31/pos_faq.html
Los “Smart Contracts” son programas/herramientas para acceder a la EVM, con ellos podemos leer, actualizar y escribir información a la red Ethereum.
En pocas palabras los desarrolladores tienen la posibilidad de crear aplicaciones “Semi-Turing Complete” usando lenguajes como Vyper y Solidity.
Solidity y Vyper son lenguajes de alto nivel para desarrollar Smart Contracts en la EVM. Usamos estos lenguajes porque la EVM sólo comprende “EVM Bytecode” (6)
.
Antes de iniciar a tirar código debes considerar la siguiente lista de elementos necesarios para esta guía:
Firefox o Chrome e instalar la extensión de Metamask
Se recomienda crear una cuenta en Metamask sólamente para pruebas
Visual Studio Code o un Editor de texto
Si usas Windows tener WSL (Windows Subsystem for Linux) configurado
NodeJS (Versión >= 16. Puedes usar nvm para una instalación sencilla)
Crear una cuenta en Alchemy. Lo usaremos para lanzar nuestro Smart Contract en Goerli Testnet.
Si usas Visual Studio Code puedes instalar estos plugins:
NextJs: Un framework de React muy popular que se vende como “El Framework de React para producción”. Personalmente es mi favorito del Ecosistema React y me agrada que sea de los más usados en Web3.
Wagmi: Colección de React hooks con lo requerido para trabajar con Ethereum. Peticiones, conectividad, interacción con Smart Contracts, firmas — TODO (7)
.
RainbowKit: Conectividad sencilla para React Devs. Un proyecto de los creadores de Rainbow Wallet.
TailwindCSS: Un framework de utilidad que expone una set de configuración consistente la cúal agiliza el desarrollo de componentes personalizados.
Alchemy: RPC Provider(8)
. Plataforma que nos hace la vida sencilla para la arquitectura de DApps. Sus clientes incluyen, OpenSea, Chainlink y Meta. NOTA: Existen 2 plataformas similares e igual de populares que Alchemy, hablamos de Moralis e Infura (9)
.
Hardhat: Entorno de desarrollo para Ethereum. Smart Contracts, Testing, Depuración y Tooling para Solidity.
Por último, pero no menos importante, Typescript.
Sí, ya sé que tu café se está enfriando, rellenalo que ahora entramos al objetivo de este documento; crear un pequeño proyecto, que lo extiendas y juegues con él mientras nos movemos en el Stack más popular para crear DApps. Y que por cierto, luego puedes incluir en tu portafolio ☺️.
La idea final es desarrollar un “Libro de Notas”.
Proyecto “sencillo” de codear para la Web2, pero que aportará mucho jugar con él en un enfoque Web3. ¿ Por qué ? , tanto desarrollar la interfaz, creación e integración del Smart Contract con Solidity requieren una atención/investigación muy similar, y así cualquier desarrollador puede aprovechar este documento 💛.
Al terminar aprenderemos cómo consumir información de Goerli, pintar esta información en un frontend y crear un Smart Contract que gestione este contenido :)
Crear este “GuestBook en Ethereum” estará dividido en 5 etapas, y una etapa completamente opcional. Estas son:
Genesis: RainbowKit y NextJS
Hardhat: Solidity Hello World
Goerli Testnet: Distribuyendo nuestro Smart Contract
Solidity Cardio: Eventos, Funciones y Variables de estado
Wagmi React: Interacción con el Frontend y finalización
Opcional, Plus Ultra: Lanzando nuestra DApp en Vercel
Iniciamos,
Primero necesitamos un proyecto NextJs, agregar RainbowKit y Wagmi como dependencias a este proyecto. El equipo de RainbowKit ya nos expone un comando para crear el boilerplate e incluir estas dependencias. Ejecutamos:
npm init @rainbow-me/rainbowkit@latest -y kort
Puedes cambiar el argumento ”kort”
con el texto que gustes, este será el nombre de nuestro proyecto.
Ya creado nuestro proyecto, nos ubicamos en el directorio creado. Si dejaste como nombre de proyecto “kort”
, haz cd kort
y ejecutas npm run dev
.
Hacer npm run dev
lanza un servidor de desarrollo en NextJs, este refrescará el contenido servido cada vez que hagamos cambios en el árbol de archivos de nuestro espacio de trabajo.
Si visitamos nuestro local en http://localhost:3000 veremos este Frontend:
Podemos conectarnos con una Wallet en este frontend…, pero sólamente a redes Mainnet(10)
, nosotros queremos, por pruebas, conectarnos a una Testnet, Goerli.
Si visualizas el contenido en pages/_app.tsx
y te ubicas en la línea #15 verás esto:
15 ...(process.env.NEXT_PUBLIC_ENABLE_TESTNETS === 'true'
La línea #15 evalúa que el valor de la “Variable de Entorno (11)
” NEXT_PUBLIC_ENABLE_TESTNETS
tiene como contenido “true”
, esta bandera define si permitimos Testnets en nuestro proyecto.
Para habilitarlas vamos a crear un archivo que tenga las variables de entorno para usar en nuestro proyecto. Ejecutamos:
echo "NEXT_PUBLIC_ENABLE_TESTNETS=true" > .env
Se creará un archivo .env
con el contenido NEXT_PUBLIC_ENABLE_TESTNETS=true
Para cargar las definiciones del archivo .env
a nuestro proyecto, cerramos el proceso dónde hicimos npm run dev
al comienzo de la guía, matando el proceso con CTRL + C
, luego ejecutamos npm run dev
por una vez más. Hacemos esto porque las definiciones en el archivo .env
se cargan en el arranque del servidor de desarrollo.
Y listo, ahora podemos conectarnos a Goerli Testnet con RainbowKit.
¡Felicidades!, acabamos de enlazar Metamask con el Frontend de “kort”
.
Continuemos,
pages/
|-- _app.tsx
|-- index.tsx
.env
package.json
Del árbol de archivos en nuestro proyecto nos enfocamos en cuatro elementos.
page/index.tsx
define la ruta inicial de nuestra DApp, el index.html
.
_app.tsx
es un archivo reservado de NextJs para personalizar las páginas dentro de /pages
.
.env
archivo para configurar Variables de entorno y configuración definida por el desarrollador. NOTA: No agregues este archivo en tu repositorio, los .env files guardan información privada de nuestros proyectos.
package.json
contiene metadatos y configuración para NodeJs. Acá se definen las dependencias, scripts y mucho más.
NextJs tiene el concepto de “fichero dentro de /pages”
= “una página”
. Estas “páginas” son por defecto un componente React.
A este concepto de enrutamiento
“directorio”
→“fichero template”
, se le conoce como File based routing system.
Con _app.tsx
podemos modificar cada página del proyecto, agregar estado persistente, layouts compartidas, y más. Es acá dónde inicializamos RainbowKit.
07 // _app.tsx
08
09 const { chains, provider, webSocketProvider } = configureChains(
10 [chain.mainnet, chain.polygon, chain.optimism],
11 [
12 alchemyProvider({
13 // Obten tu APIKey en https://dashboard.alchemyapi.io
14 }),
15 publicProvider(),
16 ]
17 )
18
La línea #10 define las redes en las que funcionará nuestra DApp. Cada elemento en este arreglo contiene el id
de la red, nombre, URLs de conexión RPC y URLs para los Exploradores de bloques de la red.
Líneas #11 y #15 definen los proveedores(providers
) de la red Ethereum. En el fragmento se usa Alchemy como proveedor RPC inicial para las redes configuradas la línea #10.
publicProvider
será un Fallback si en una red específica(Ejemplo si el usuario cambia Gnosis) dicho provider
no está disponible. Tener en cuenta que publicProvider
intentará interactuar con Ethereum Mainnet.
Llamamos
provider
a una abstracción consistente para una red. Básicamente es un cliente con una API estándar para interactuar con la chain.
24 const wagmiClient = createClient({
25 autoConnect: true,
26 connectors,
27 provider,
28 webSocketProvider,
29 })
RainbowKit es una herramienta para conexión, su funcionalidad vive gracias a Wagmi y sus React Hooks. Ver más información para configurar Wagmi acá: https://wagmi.sh/docs/client
Configurando #createClient
autoConnect: true
indica que Wagmi puede(por defecto) tomar la última Wallet enlazada a nuestro Frontend.
connectors
configuración de proveedores de Wallets de nuestra DApp. Por defecto son Metamask, Coinbase, Rainbow Wallet y WalletConnect.
provider
es el cliente obtenido de ejecutar configureChains
en el fragmento de la Línea #9.
webSocketProvider
si la ejecución de configureChains
resuelve con un cliente WebSockets, Wagmi realizará la conexión por WebSockets en vez de HTTP.
Ahora, vamos a crear nuestro primer Smart Contract 😍.
NOTA: Esta guía es para desarrollar Smart Contracts con Solidity y Hardhat como entorno de desarrollo. Existe un editor llamado Remix, dónde puedes probar cositas en la EVM antes de moverlas a tu codebase.
Primero, dependencias. Hardhat y hardhat-toolbox,
npm i -D hardhat @nomicfoundation/hardhat-toolbox
Ahora necesitamos un archivo de configuración para Hardhat. En la raíz de nuestro proyecto creamos el archivo hardhat.config.ts
y pegamos esto:
// hardhat.config.ts
import { HardhatUserConfig } from "hardhat/config"
import "@nomicfoundation/hardhat-toolbox"
const config: HardhatUserConfig = {
solidity: "0.8.9",
}
export default config
Para tener más ordenado nuestro código y seguir la convención de archivos en hardhat, vamos a crear una carpeta dónde vivirán nuestros Smart Contracts y otra para definir scripts/tareas que consumen estos Smart Contracts.
En consola, mkdir contracts scripts
.
Creadas las carpetas, dentro de contracts/
creamos un archivo llamado HolaMundo.sol
y pegamos el siguiente contenido:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
contract HolaMundo {
string hola = "Hola";
string mundo = "Mundo!!";
function saludar() external view returns (string memory) {
return string(abi.encodePacked(hola, " ", mundo));
}
}
Dentro de scripts/
creamos un archivo deploy.ts
y pegaremos el siguiente contenido:
import { ethers } from "hardhat"
async function main() {
const factory = await ethers.getContractFactory("HolaMundo")
const contract = await factory.deploy()
await contract.deployed()
console.log(await contract.saludar())
}
main().catch((error) => {
console.error(error)
process.exitCode = 1
})
Para ejecutar este script, hacemos:
npx hardhat run scripts/deploy.ts
Esto distribuye en local el Smart Contract “HolaMundo.sol”
usando la Hardhat Network.
Si visualizas este error, tranquilidad. NextJs ha definido que nuestro proyecto será un módulo “esnext”
para poder hacer imports dinámicos, causando este error. Para solventarlo cambiamos la propiedad “module”
en tsconfig.json
a “commonjs”
.
{
"compilerOptions": {
...tu configuración acá,
"module": "commonjs"
},
}
Ejecutamos de nuevo npx hardhat run scripts/deploy.ts
y Voilà.
¿Y si intentamos crear una función que espere un parámetro?
Hagámoslo…, y por cierto, a este punto ya tienes una base para jugar con lo que se te ocurra con Solidity, mira ejemplos de Uniswap y tomalos de guía.
function saludarConNombre(string calldata nombre)
external
pure
returns (string memory)
{
return string(abi.encodePacked("Hola ", nombre));
}
Antes de invocar nuestra función en el script deploy.ts
, una pequeña explicación de las palabras “pure”, “external” y “calldata” en este contexto.
pure
, una función “pura” es aquella que no modifica propiedades que están fuera de su scope. En pocas palabras, saludarConNombre()
es una función que sólo manipula la data con la que se invocó dicha función, más nunca escribirá ni leerá fuera de su contexto.
external
describe la “visibilidad” que la función saludarConNombre()
tendrá cuando se distribuya en la red. Existen 4 tipos de visibilidad, public, private, internal y external.
calldata
representa el alojamiento de memoria de la función.
Modificamos la función main()
para agregar saludos a “Seth” y “Atzil”,
async function main() {
const factory = await ethers.getContractFactory("HolaMundo")
const contract = await factory.deploy()
await contract.deployed()
console.log(await contract.saludar())
console.log(await contract.saludarConNombre("Seth"))
console.log(await contract.saludarConNombre("Atzil"))
}
npx hardhat run scripts/deploy.ts
una vez más,
abi.encodePacked
es usado(en este caso) para concatenar strings. La función encodePacked agrupa los bytes de una variable y retorna los bytes de toda esa agrupación, al final convertimos esos bytes a un string.
Para esta sección necesitas tener creada una cuenta en Alchemy, esto para distribuir nuestro contrato en Goerli Testnet : )
Como ya has notado, ejecutar los Smart Contracts requiere trabajo repetitivo, a medida que nuestro proyecto crece esto puede generar mucha distorsión.
La comunidad ha adoptado un plugin para agilizar este proceso — hardhat-deploy, el plugin de hardhat que permite definir scripts secuenciales e interactuar con el Hardhat runtime environment. Ya que esta guía intenta mostrarte lo más usado y adoptado por el ecosistema, vamos a instalarlo.
npm i -D hardhat-deploy dotenv
Ahora agregamos el plugin en hardhat.config.ts
,
// hardhat.config.ts
import { HardhatUserConfig } from "hardhat/config"
import "@nomicfoundation/hardhat-toolbox"
import "hardhat-deploy" // Importamos el plugin
import "dotenv/config" // Paquete para cargar variables de entorno
Luego de registrarte en Alchemy, crea una nueva App con el nombre que quieras, la descripción que quieras, pero eso sí, la chain será Ethereum
y en Network marcamos Goerli
.
Luego de creado el proyecto, vamos al Dashboard y copiamos el valor de “API KEY”
Agregamos el valor de API KEY
en nuestro archivo .env
,
NEXT_PUBLIC_ENABLE_TESTNETS=true
ALCHEMY_API_KEY=PEGA TU API KEY ACÁ
Creamos un archivo en deploy/00_HolaMundo.ts
y agregamos lo siguiente:
// deploy/00_HolaMundo.ts
import { HardhatRuntimeEnvironment } from "hardhat/types"
import { DeployFunction } from "hardhat-deploy/types"
const fn: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const { getNamedAccounts, deployments } = hre
const { deployer } = await getNamedAccounts()
// "deployer" es tomado de hardhat.config.ts#namedAccounts
await deployments.deploy("HolaMundo", {
from: deployer,
})
}
export default fn
El plugin hardhay-deploy ejecutará los archivos dentro de /deploy en orden, podemos agregar tantos scripts queramos
¿Deploy?. Aún no, espera un poco, un poquito más. Tenemos que definir en hardhat quién será el “deployer” de nuestro Smart Contract.
Para ello necesitamos la “Llave privada” de una Wallet. Para Metamask puedes seguír este post: https://metamask.zendesk.com/hc/en-us/articles/360015289632
Ahora que tenemos nuestra “Llave privada” la agregamos a nuestro .env
NEXT_PUBLIC_ENABLE_TESTNETS=true
ALCHEMY_API_KEY=PEGA TU API KEY ACÁ
PRIVATE_KEY=Y ACÁ TU LLAVE PRIVADA
Necesitamos agregar PRIVATE_KEY
a la configuración de Hardhat para Goerli. hardhat.config.ts
deberá quedar así:
// hardhat.config.ts
import { HardhatUserConfig } from "hardhat/config"
import "@nomicfoundation/hardhat-toolbox"
import "hardhat-deploy"
import "dotenv/config"
const ALCHEMY_API_KEY = process.env.ALCHEMY_API_KEY
const PRIVATE_KEY = process.env.PRIVATE_KEY!
const config: HardhatUserConfig = {
solidity: "0.8.9",
networks: {
goerli: {
url: `https://eth-goerli.g.alchemy.com/v2/${ALCHEMY_API_KEY}`,
accounts: [PRIVATE_KEY],
},
},
namedAccounts: {
deployer: 0, // Posición 0 en `networks.<goerli>.accounts[]`
},
}
export default config
Un último comando,
npx hardhat deploy --network goerli
Oye, espera un momento .
¿ Sabes que acabas de crear y distribuir un Smart Contract en Goerli ? , te mereces una donita más para el café 💛.
Compartelo con tus amigos, publicalo en tus redes sociales :3
Hay algo más que podemos hacer para mejorar el deployment, si verificas tu Smart Contract aportas muchos beneficios.
Ejemplo, si visualizas este Smart Contract podrás ver su código fuente, ejecutar sus métodos de escritura/lectura, y obtener información sobre bugs. Todo esto gracias a que dicho contrato está verificado en Etherscan.
Al verificar nuestro Smart Contract el código fuente de este será visible, lo cúal genera transparencia a cualquier interesado en la lógica detrás de una DApp. Y además cualquier persona puede interactuar con el contrato desde el Explorador.
Para verificar, primero creamos una cuenta en https://etherscan.io/apis, copiamos la API KEY
y agregamos la definición ETHERSCAN_API_KEY=”TU API KEY”
en .env
Configuramos el plugin hardhat-etherscan en hardhat.config.ts
// hardhat.config.ts
// Sacamos la definición ETHERSCAN_API_KEY de .env
const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY!
const config: HardhatUserConfig = {
...,
etherscan: {
apiKey: {
goerli: ETHERSCAN_API_KEY, // https://goerli.etherscan.io
},
},
}
export default config
El plugin usado es @nomiclabs/hardhat-etherscan y viene instalado junto a hardhat-toolbox
Del paso anterior(Distribuyendo HolaMundo.sol
) debió crearse una carpeta( deployments/
) con la información del lanzamiento.
En la ruta deployments/goerli/HolaMundo.json
observaras algo parecido a esto:
{
"address": "0xA1012F4B18badFd4fA9122fEc7e4de160aD0ffBB",
"abi": [
{
"inputs": [],
"name": "saludar",
"stateMutability": "view",
"type": "function"
},
{
"name": "saludarConNombre",
"stateMutability": "pure",
"type": "function"
}
]
}
Para verificar HolaMundo.sol
en etherscan necesitamos la propiedad address
, esta es la dirección dónde se alojó el contrato en la red.
ABI(Application Binary Interface) define los métodos y propiedades para interactuar con HolaMundo.sol
.
Anda, para verificar HolaMundo.sol
en Goerli ejecutamos:
npx hardhat verify --network goerli <address>
Para automatizar este proceso podemos crear un deploy script en /deploy
// deploy/99_VerifyABIs.ts
import { HardhatRuntimeEnvironment } from "hardhat/types"
import { DeployFunction } from "hardhat-deploy/types"
// Agrega las chains que ejecutarán `verify`
const TO_VERIFY_NETWORKS = ["goerli"]
// Lista el nombre de los Smart Contracts a verificar en EtherScan
const VERIFY_CONTRACTS = ["HolaMundo"]
const fn: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const { name } = hre.network
// Tomar todos los deployments hasta este momento
const deployed = await hre.deployments.all()
// Validar que estoy en una chain que puede ejecutar verify.
// No vamos a verificar nuestro contrato en Hardhat localhost
if (TO_VERIFY_NETWORKS.includes(name)) {
for (let name of VERIFY_CONTRACTS) {
const contract = deployed[name]
if (contract) {
await hre.run("verify:verify", {
address: contract.address,
})
} else throw `Could't find deployment for ${name}`
}
}
}
// Forzar ejecutar esta función al final
fn.runAtTheEnd = true
export default fn
Este script se ejecutará cada cuando hagamos npx hardhat deploy
: )
En esta sección aprenderemos un poco de los primitivos en Solidity. De antemano comento que no será un tutorial de condicionales, bucles y basis de otros lenguajes de programación.
Tu mayor fuente de información es la documentación oficial de Solidity, Google y amigos en comunidades Web3 : )
Los eventos en Solidity tienen muchos usos, exponer información, para por ejemplo comunicarnos con servicios externos de la red y además guardar data.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
contract NFTLogger {
// Declaramos un evento con el nombre `NuevaCompra`.
// En los eventos podemos "emitir" información.
// Esta puede ser filtrada al marcarse como `indexed`.
// * NOTA: A estas propiedades indexadas se les llama topics
event NuevaCompra(address indexed owner, string nft_id);
function comprarNFT() public {
// Emitimos el evento NuevaCompra
emit NuevaCompra(msg.sender, "NFT-004");
}
}
Ver ejemplo en Etherscan.
Las estructuras(structs
) en Solidity son un tipo de dato
que aloja propiedades primitivas, dónde estas propiedades se agrupan como una tupla. “Objetos”
struct UnaLLama {
bool guapa;
string nombre;
uint edad;
}
Para crear una “Llama”, hacemos:
LLama llama = LLama(true, "Juanita", 33);
// Creación por posición, LLama(guapa, nombre, edad)
Un pequeño ejemplo con Deploy scripts:
// contracts/Llama.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
contract Llama {
struct UnaLLama {
bool guapa;
string nombre;
uint edad;
}
function crearLLama(
bool _guapa,
string calldata _nombre,
uint _edad
) public pure returns (UnaLLama memory) {
return UnaLLama(_guapa, _nombre, _edad);
}
}
// scripts/deploy.ts
import { ethers } from "hardhat"
async function main() {
const factory = await ethers.getContractFactory("Llama")
const contract = await factory.deploy()
await contract.deployed()
console.log(await contract.crearLLama(true, "Juanita 2", 42))
}
main()
Los mappings
son en pocas palabras una tabla “clave-valor”.
mapping(string => UnaLLama) corralito;
function guardarLlamaEnCorral(
bool _guapa,
string calldata _nombre,
uint _edad
) external {
corralito[_nombre] = crearLLama(_guapa, _nombre, _edad);
}
function sacarLlamaDeCorral(string calldata _nombre)
external
view
returns (UnaLLama memory)
{
return corralito[_nombre];
}
Ejemplo de deploy script,
async function main() {
const factory = await ethers.getContractFactory("Llama")
const ct = await factory.deploy()
await ct.deployed()
console.log(await ct.crearLLama(true, "Juanita 2", 42))
const tx = await ct.guardarLlamaEnCorral(true, "Juanita", 42)
await tx.wait()
console.log(await ct.sacarLlamaDeCorral("Juanita"))
}
Estos son primitivos en muchos lenguajes de programación y apuesto que ya tienes idea de como usarlos. Vamos, modifica Llama.sol
y prueba : )
Lista de “tipos de datos” en Solidity: https://docs.soliditylang.org/en/v0.8.16/types.html
Sí NO has llevado la guía a la ligera, hasta este punto ya tienes lo suficiente conocimiento para crear tus contratos e integrarlos con el Frontend.
Mi enfoque no es enseñarte React, mucho menos Javascript. Del Frontend, NextJs es importante ya que se usa muchísimo en el Ecosistema, saber el enrutamiento y la estructura de archivos y cómo configurar Wagmi & Rainbowkit es suficiente para arrancar.
La documentación de estas herramientas es tu fuente de la verdad.
Para esta etapa vamos a necesitar un template que trae todo lo que vimos en la guía. La plantilla ya trae un set de componentes React y una interfaz básica para modificar y jugar con el código.
Para usar la plantilla, vamos a https://github.com/D3Portillo/nextjs-rainbow-hh y clickeamos en “Use this template”, agregamos un nombre que deseemos asignar para este repositorio a crear.
La estructura del proyecto es muy similar a nuestra experimentación anterior. La diferencia es que ahora hay una nueva carpeta(components/
) que contiene los trozos de interfaz que pintamos en nuestra DApp.
También ha cambiado el nombre de algunas variables de entorno. Unas llevan el prefijo NEXT_PUBLIC
, esto es porque NextJs tomara las definiciones de entorno sólo al ejecutarse en NodeJs más no en el navegador, para “permitir” acceder a estas definiciones se antepone NEXT_PUBLIC por convención y consistencia.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
contract GuestBook {
event NewNote(address indexed persona, string contenido);
function addNote(string calldata _contenido) external {
emit NewNote(msg.sender, _contenido);
}
}
Nuestro proyecto sólamente envía logs al invocar la función addNote
. Para acceder a los logs desde el frontend, filtramos los registros de la chain con el React Hook ubicado en la ruta lib/hooks/useNotesList.tsx
import { useEffect, useState } from "react"
import getContract from "@/lib/getContract"
import { noOp } from "@/lib/helpers"
const GuestBook = getContract("GuestBook")
function useNotesList() {
const [list, setList] = useState([] as Note[])
useEffect(() => {
GuestBook.queryFilter(GuestBook.filters.NewNote())
.then((list) => {
return list.map((item) => {
const {
args: { persona, contenido },
transactionHash,
} = item
return {
transactionHash,
persona,
contenido,
}
})
})
.then(setList)
.catch(noOp)
}, [])
return list
}
export default useNotesList
Esta es la información de los deployments que hagamos (espejo de los deploy scripts en /deploy
). Por lo que te invito a modificar /contracts/GuestBook.json
con una feature, agregar condicionales, o por ejemplo:
¿Qué sucede sí mi string está vacío ?, validación de errores.
Agregar un deploy a Ethereum Mainnet, Solana o Polygon.
¿No usar logs para obtener las notas dejadas en la DApp y usar una variable de estado ?
¿ Limitar la cantidad de Notas que puede dejar una persona ?
Lo que gustes, vos podes hacer lo que querrás 😋
Luego de modificar el proyecto ejecuta npx hardhat run deploy
, levanta nodos locales con npx hardhat run
y lo más importante, juega, modifica, pregunta : )
Haz deploy de tus cambios porque la información en /deployments
es mía, de mi cuenta de pruebas. Haz el proyecto tuyo.
Ya habrás notado que la guía es un tutorial de cómo crear el template que acabas de usar, Felicidades!! 🦄. Te invito a leer un poco la documentación de React y NextJs, no vendrá mal, y además no debe faltar leer sobre Hardhat y Solidity.
Sugerencias, comentarios, quejas; lo que se te ocurra 🥴, más que bienvenid@s :)
Vercel es una compañía amada por los Frontend devs para lanzar sus proyectos. “Develop, Preview, Ship” es su eslogan y les pega a la perfección.
Nota
randompersonal: Vercel es una de las empresas dónde quiero trabajar y tirar código. Tengo un crush con su CEO — Guillermo Rauch(12)
, un crack.
Para esta sección necesitamos crear una cuenta en https://vercel.com/new, y luego inicias sesión con tu GitHub.
Ahora buscamos el proyecto que vamos a lanzar en Vercel de la lista de repositorios(en mi caso es “mi-proyecto-guapo”). Damos a “Import” y configuramos variables de entorno.
Configuramos las variables de entorno. Recuerda que para forzar Goerli seteamos NEXT_PUBLIC_FORCE_GOERLI_PROVIDER=true
y reemplazas NEXT_PUBLIC_ALCHEMY_API_KEY
por tu API Key de Alchemy 😛.
Esperamos un momento, y tendremos nuestro DApp con url para compartir 😍
🤘Genial, hemos aprendido mucho. Ahora tú tienes conocimientos sobre el Ecosistema Web3, Ethereum, Sistemas Distribuidos y puntualmente “Cómo desarrollar Aplicaciones Descentralizadas” :)
Recuerda, la idea de esta guía es demostrar lo más posible cómo desarrollar DApps con un set de herramientas populares y validadas por los desarrolladores. Faltaron un par de cositas que espero con mucha inquietud presentar en otra Guía. Una de ellas es mejorar el consumo de información en el Frontend con Subgraphs.
Algo más, si te preguntas, ¿Cómo se interconecta la información de Ethereum con servicios externos a la red ?, la respuesta, Oracles.
Si llegaste hasta este punto de la lectura, y bueno; sí cualquier trozo de texto te ayudó para aprendizaje, espero y te sientas orgulloso. Sé que tienes muchas preguntas, pero ahora te será más sencillo buscar lo que no sabes y reforzar lo que sabes.
También, si este fue tu primer bailecito con la Web3, Bienvenido 🥰.
Me llamo Denny Portillo, Salvadoreño y estudiante de 42 Madrid. Trabajo como desarrollador Frontend Web3 en Daoism Systems. Quiero comentarte con mucha honestidad que en este ecosistema nunca paras de encontrar contenido que aprender. Muchas ocasiones te desesperas por TODO; agrego que esto me divierte, me gusta. No es aburrido y la Web3 tiene mucha vida por delante.
Si te interesa desarrollar DApps, hazlo. Inténtalo, equivócate, práctica, práctica, práctica. No dejes de dudar y preguntar. Únete a comunidades de desarrolladores y crea lo que se te ocurra, : )
¿No sabes a qué comunidad unirte ?, puedes iniciar en https://buildspace.so. En Buildspace aprendí a crear Smart Contracts y lanzarlos en una Testnet (13)
.
Para terminar. No sabes lo que me gustó crear este documento, espero compartas la guía con tus amigos y si tienes comentarios, dudas; arrojalas que estamos para apoyar.
Antes de irte, si gustas, sigueme en Twitter 🚀: https://twitter.com/d3portillo
The Merge: https://ethereum.org/en/upgrades/merge
Intro to Ethereum: https://ethereum.org/en/developers/docs/intro-to-ethereum
Ethereum Virtual Machine: https://ethereum.org/es/developers/docs/evm
Maquina de Estados: https://es.wikipedia.org/wiki/Máquina_de_estados
“Turing Completeness” y Modelo de ejecución en la EVM
Turing Complete Blockchain: https://academy.binance.com/en/glossary/turing-complete
Ethereum Yellow Paper #8fea825, Cap. 9 — Execution Model: https://ethereum.github.io/yellowpaper/paper.pdf
Linkedin post “5 Paquetes de React para Frontend Devs en Web3”: https://www.linkedin.com/posts/d3portillo_5-paquetes-de-react-para-frontends-en-web3-activity-6948890361192722432-nXGU
Ethereum RPC: https://ethereum.org/en/developers/docs/apis/json-rpc
Moralis vs Infura vs Alchemy: https://moralis.io/whats-the-difference-between-moralis-alchemy-and-infura
Mainnet Network: https://academy.binance.com/es/glossary/mainnet
Variables de Entorno: https://es.wikipedia.org/wiki/Variable_de_entorno
Guillermo es el creador de Socket.io, Mongoose y otras herramientas populares del ecosistema NodeJS. Hay contenido en este documento de su charla “Merging Design and Development”, dónde habla sobre “Consistent UI” y Sistemas distribuidos, mirala acá: https://www.youtube.com/watch?v=3hccXiXI0u8
Repositorios usados en la guía
Jeshejojo: https://github.com/D3Portillo/jeshejojo
Plantilla Bukrr: https://github.com/D3Portillo/nextjs-rainbow-hh
Blockend: El Desarrollo Backend, Frontend y de Smart Contracts para una DApp.
DApp: Aplicación Descentralizada (Decentralized App).
ETH: Ether(Ξ). Token principal y nativo de la red Ethereum.
EVM: Ethereum Virtual Machine.
Solidity: Lenguaje de programación parecido a Javascript para crear Smart Contracts en la EVM.
Testnet: Una copia/instancia para pruebas de una red.
WAGMI: We’re all gonna make it.
Vyper: Lenguaje de programación parecido a Python para crear Smart Contracts en la EVM.
Gracias Ana por la sugerencia en crear este documento :)