# Ethereum Developer Pack

El **Ethereum Developer Pack** es un conjunto de módulos para aprender a desarrollar en el ecosistema de Ethereum. Encontrarás información sobre los conceptos básicos de blockchain hasta seguridad de Smart Contracts.

<figure><img src="/files/gWa9rXle2y8B29ha2zI7" alt=""><figcaption></figcaption></figure>

Este documento es una herramienta pedagógica que actualizamos constantemente. Creemos en la importancia de contenidos open-source. Si quieres mejorar los contenidos, únete a nuestro programa de [Kipu Explorers](/contribuye/kipu-explorer).

[![GitBook](https://img.shields.io/static/v1?message=Documented%20on%20GitBook\&logo=gitbook\&logoColor=ffffff\&label=%20\&labelColor=5c5c5c\&color=3F89A1)](https://www.gitbook.com/preview?utm_source=gitbook_readme_badge\&utm_medium=organic\&utm_campaign=preview_documentation\&utm_content=link)


# Intro a Smart Contracts

**Objetivo:** Revisar los conceptos básicos de blockchain y Ethereum para entender la máquina descentralizada sobre la que corren los smart contracts. Crear el primer smart contract en una testnet.

<figure><img src="/files/QryeDpjuT4iWlX1uCb8p" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Este documento es una herramienta pedagógica que actualizamos constantemente. Creemos en la importancia de contenidos open-source. Si quieres mejorar los contenidos, únete a nuestro programa de [Kipu Explorers](/contribuye/kipu-explorer).
{% endhint %}


# Fundamentos de Blockchain

## Temario

En esta sección aprenderás lo siguiente:

* Antecedentes
* Bitcoin
* Qué es Blockchain
* Conceptos Clave en Blockchain
* Cómo funciona la Blockchain
* Tipos de Blockchain
* Modelos de Consenso


# Antecedentes

La historia de la blockchain es la de una tecnología que ha evolucionado y se ha utilizado en una variedad de aplicaciones más allá de las criptomonedas. Aunque el [whitepaper de Bitcoin](https://bitcoin.org/bitcoin.pdf), publicado en 2008 por una persona o grupo de personas bajo el seudónimo de Satoshi Nakamoto, se considera el origen de la tecnología blockchain, en realidad es el resultado de la combinación de varias tecnologías previas, como la criptografía de clave pública y la teoría de juegos.

Antes de la blockchain, se hicieron intentos para crear monedas digitales, pero sin éxito.

A continuación algunas de las tecnologías que posibilitaron el surgimiento del concepto de blockchain:

* **1979 - Merkle Tree :** Ralph Merkle propuso una estructura de datos criptográfica llamada "Merkle Tree" o "Árbol de Merkle", que permite la verificación eficiente de grandes cantidades de datos. El árbol de Merkle divide los datos en bloques y los organiza en una estructura de árbol, donde cada bloque está vinculado a su bloque padre a través de una función de hash criptográfico.
* **1989 - DigiCash :** El sistema de DigiCash utilizaba una moneda digital llamada "eCash" que se podía comprar en un banco y luego usar para hacer pagos en línea. [Aunque DigiCash no tuvo éxito comercialmente](https://www.forbes.com/forbes/1999/1101/6411390a.html?sh=3bb4cf96715f), se considera una de las primeras empresas en desarrollar una criptomoneda y sentó las bases para el desarrollo posterior de la tecnología blockchain y las criptomonedas como Bitcoin.
* **1992 - Control de modificación de documentos digitales:** Haber y Stornetta propusieron la marca temporal de documentos digitales en *merkle trees*. Publicaron [papers](https://link.springer.com/article/10.1007/BF00196791) proponiendo un sistema para asegurar que los bloques no sean modificables y puedan ser encadenados criptográficamente.
* **1997 - Hashcash :** [Una tecnología de prueba de trabajo](http://www.hashcash.org/papers/hashcash.pdf) que utiliza una función hash criptográfica para crear un token que prueba que se ha realizado un cierto esfuerzo computacional. Utilizado principalmente para combatir el spam en el correo electrónico.
* **1998 - B-Money y Bit Gold** : Fueron dos propuestas de criptomonedas previas a Bitcoin, con enfoques en la privacidad y la distribución justa ([B-Money](http://www.weidai.com/bmoney.txt)) y en la criptografía y como moneda de respaldo (oro digital) de otras criptomonedas ([Bit Gold](https://www.investopedia.com/terms/b/bit-gold.asp)). Wei Dai, creador de B-Money propuso que cada nodo participante en la red guarde una copia completa del registro de transacciones realizadas. Por otro lado, fue el famoso Nick Zsabo quien estuvo detrás de la creación de Bit Gold.
* **1999 - P2P :** Shawn Fanning creó una aplicación llamada "Napster" que permitía a los usuarios compartir archivos de música entre sí de forma descentralizada, sin necesidad de un servidor centralizado. Esto fue posible gracias a la tecnología P2P o "peer-to-peer", que permitía a los usuarios conectarse directamente entre sí y compartir archivos.
* **2004 - RPoW :** Hal Finney crea Reusable Proof of Work [(RPoW)](https://es.wikipedia.org/wiki/Reusable_Proof_Of_Work), una criptomoneda basada en la prueba de trabajo que utiliza tokens que se pueden reutilizar en lugar de generar nuevos tokens.
* **2008 - Bitcoin :** Una persona o grupo de personas que se hace llamar Satoshi Nakamoto publicó un documento titulado "Bitcoin: A Peer-to-Peer Electronic Cash System", que describía una nueva criptomoneda basada en la tecnología blockchain.


# Bitcoin

El contexto de la creación de Bitcoin se remonta a la crisis financiera de 2008, que dejó al descubierto las debilidades del sistema financiero tradicional. Satoshi Nakamoto, el creador de Bitcoin, publicó un documento técnico en 2008 titulado "Bitcoin: A Peer-to-Peer Electronic Cash System". En este documento, describió una forma de crear una moneda digital descentralizada que no requería intermediarios y que permitía a los usuarios realizar transacciones sin la necesidad de confiar en terceros.

Para lograr esto, Nakamoto creó la tecnología blockchain, que es una base de datos distribuida que se encuentra en diferentes nodos en lugar de estar centralizada en un solo lugar. Cada nodo tiene una copia exacta de toda la información almacenada en la blockchain, lo que la hace inmutable y resistente a la manipulación. La tecnología blockchain permite la creación de una moneda digital que no puede ser falsificada o duplicada, y que puede ser transferida de manera segura y confiable sin la necesidad de intermediarios costosos y lentos.

La tecnología blockchain utilizada en Bitcoin garantiza la transparencia y la inmutabilidad de las transacciones, lo que aumenta la confianza de los usuarios en la moneda digital.


# Qué es Blockchain

Es un registro distribuido de activos y transacciones almacenados en forma de bloques de datos criptográficamente vinculados. Sólo permite añadir información y es permanente.

**Un registro distribuido**

Similar a un registro contable, donde se anotan todas las transacciones realizadas en una cuenta, y que se replica de forma completa en todos los nodos de una red. Ningún nodo o grupo de nodos tiene la capacidad de controlar toda la red P2P (peer to peer).

**De activos y transacciones**

En general información valiosa: transacciones de activos, datos de trazabilidad, código de programación, etc. Almacenados en bloques de datos criptográficamente vinculados.

Varias transacciones se encapsulan en un bloque que se encadena a los bloques anteriores (por ello, cadena de bloques o blockchain) luego de que se realiza en la red un procedimiento criptográfico denominado prueba de trabajo (proof of work) en el que se consume energía y poder computacional.

**Solo permite añadir información y es permanente**

Las transacciones antiguas no pueden modificarse, sólo se permite añadir nuevas transacciones.

{% hint style="info" %}
Los datos de una blockchain son inalterables
{% endhint %}


# Conceptos Clave en Blockchain

La genialidad de Satoshi Nakamoto se refleja en que supo integrar en su propuesta avances realizados en materias como: redes de computadores, teoría de juegos y criptografía.

Revisemos cada uno de estos conceptos.

### Redes de computadoras

Las redes se refieren a la forma en que los nodos se organizan para validar y mantener la integridad de la red. Las redes pueden ser centralizadas, descentralizadas o distribuidas, según cómo se administren y quién tenga el control de la red.

<figure><img src="/files/XTSiv3mXJmdDXaL97ilW" alt=""><figcaption></figcaption></figure>

#### Centralizada

Arquitectura en la que todos los nodos de la red se conectan a un servidor central que tiene el control y la capacidad de tomar decisiones para la red. Esto significa que la red no es descentralizada y la toma de decisiones y el control de la red están en manos de una sola entidad central.

| Ventajas                       | Desventajas                                         |
| ------------------------------ | --------------------------------------------------- |
| Implementación simple y rápida | El servidor centralizado es un punto único de fallo |
| Fácil mantenimiento y control  | Falta de transparencia y control centralizado       |

#### Descentralizada

Es una arquitectura en la que los nodos están distribuidos en múltiples ubicaciones y no están controlados por una única entidad central. En lugar de eso, cada nodo tiene una copia completa de la base de datos de la cadena de bloques y los usuarios de la red tienen cierto grado de control y toma de decisiones

| Ventajas                         | Desventajas                             |
| -------------------------------- | --------------------------------------- |
| Mayor privacidad y transparencia | Altos costes de gestión y mantenimiento |
| Mayor seguridad\*                | Menor control y eficiencia\*            |

#### Distribuida

Una red distribuida de blockchain es una red descentralizada de nodos interconectados que mantienen una copia idéntica del libro mayor compartido y trabajan juntos para validar transacciones y garantizar la integridad de la red. Esto proporciona una mayor transparencia, seguridad y resistencia a la censura en comparación con los sistemas centralizados.

| Ventajas                    | Desventajas                             |
| --------------------------- | --------------------------------------- |
| Tolerancia extrema a fallas | Altos costes de gestión y mantenimiento |
| Transparencia mejorada      | Lenta coordinación entre nodos          |

### Teoría de Juegos

La teoría de juegos es la rama de las matemáticas que estudia la elección óptima para un individuo cuando sus resultados dependen de lo que hagan a su vez otros individuos.

La teoría de juegos se aplica en diferentes aspectos del diseño de una blockchain para hacer que los participantes (mineros o validadores) se comporten de una forma que no ponga en riesgo la integridad, continuidad y seguridad de la red.

Así, podemos ver la aplicación de la teoría de juegos en los siguientes aspectos:

#### Protocolos de Consenso

La teoría de juegos puede utilizarse para modelar y analizar los comportamientos estratégicos de los nodos en un protocolo de consenso, como en el caso de Proof of Work (PoW) y Proof of Stake (PoS). Estos protocolos garantizan que todos los nodos de la red estén de acuerdo en el estado actual de la blockchain. Esto evita la posibilidad de que se introduzcan bloques fraudulentos o transacciones no válidas en la cadena.

#### Incentivos

Diseñar sistemas de incentivos efectivos es crucial para alinear los intereses de los participantes en una red blockchain. La teoría de juegos puede ayudar a modelar cómo los actores racionales responden a diferentes incentivos y cómo se pueden ajustar los mecanismos de consenso para lograr un comportamiento deseado.

Por ejemplo, en PoS, los nodos con más activos tienen más probabilidades de ser seleccionados para validar bloques, lo que proporciona un incentivo para actuar de manera honesta.

#### Seguridad y Ataques

La teoría de juegos se aplicar para analizar posibles ataques y estrategias defensivas en el contexto de blockchain. Por ejemplo, modelar cómo un atacante puede intentar realizar un doble gasto en una red blockchain y cómo los mecanismos de consenso pueden prevenir o mitigar tales ataques.

Los mecanismos de consenso ayudan a proteger la red contra varios tipos de ataques, como ataques del 51% en PoW, donde un actor malintencionado controla más de la mitad del poder computacional de la red. Al implementar un mecanismo de consenso robusto, se dificulta la realización de ataques maliciosos.

Un concepto que es común a la teoría de juegos y a la ciencia de la computación es el [Problema de los Generales Bizantinos](https://www.microsoft.com/en-us/research/uploads/prod/2016/12/The-Byzantine-Generals-Problem.pdf). En este problema se plantea la cuestión de cómo los componentes de un sistema distribuido pueden llegar a un consenso cuando algunos de esos componentes pueden comportarse de manera maliciosa o, más generalmente, cuando pueden ocurrir fallas y los nodos deben tomar decisiones en conjunto. La analogía se basa en un escenario hipotético en el que los generales de un ejército bizantino deben coordinar sus acciones para atacar o retirarse, pero algunos de los generales pueden ser traidores y dar órdenes falsas.

Este problema es fundamental para entender los desafíos de coordinar sistemas distribuidos en presencia de fallas y comportamientos maliciosos, y ha llevado al desarrollo de conceptos y algoritmos en la teoría de la computación distribuida, como el algoritmo del consenso de Byzantine Fault Tolerance (BFT). Estos conceptos son esenciales en la construcción y mantenimiento de sistemas descentralizados, como la blockchain.

En el caso particular de Bitcoin, la forma de resolver el problema de ataques o fallas en la conexión es a través del consenso de Proof of Work, que proporciona un mecanismo de consenso descentralizado, seguro y resistente a la censura. La combinación de incentivos económicos, esfuerzo computacional y descentralización contribuye a la robustez y seguridad de la red.

### Criptografía

La criptografía es esencial en la blockchain para garantizar la seguridad y la privacidad de las transacciones. En la blockchain, cada transacción se registra en un bloque, que se agrega a una cadena de bloques. Cada bloque contiene un hash, que es una cadena de caracteres única que identifica el bloque y todas las transacciones que contiene.

#### **Qué es un hash**

Un hash (picadillo en español) es un código que se obtiene al aplicar una función criptográfica a una información de entrada. Podríamos por ejemplo aplicar una función hash a un documento, un video, una imagen o un programa y obtendremos como resultado un código único y de un tamaño fijo, un hash, equivalente a una huella digital del archivo de origen.

<div align="left" data-full-width="true"><figure><img src="/files/N5d3Xbxr0Bch5RXLznUW" alt="" width="375"><figcaption></figcaption></figure></div>

Si alguien alterase de forma accidental o fraudulenta el archivo de origen, el hash cambiaría totalmente y podríamos detectar de esa manera que el archivo original ha sido modificado.

Para un archivo digital podemos generar un hash a través de una función criptográfica, pero no podemos reconstruir el documento original a partir del hash (es unidireccional).

Para generar hashes se utilizan diversas funciones, según el nivel de fortaleza de encriptación que se desee lograr. En el caso del protocolo de Bitcoin una de las funciones hash utilizadas es la SHA-256 (SHA es Secure Hash Algorithm) y da como resultado un código de 64 caracteres hexadecimales (256 bits).

El sistema de numeración hexadecimal tiene 16 símbolos. Su equivalencia en en sistema de numeración decimal se muestra a continuación.

Decimal 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15&#x20;

Hex. 0 1 2 3 4 5 6 7 8 9 A B C D E F

#### **Requisitos de una función hash**

Una función hash debe cumplir los siguientes requisitos:

* Funcionar en una sola dirección. No se debe poder reconstruir el contenido de origen a partir del hash.
* Ser determinística. Para un mismo documento el hash siempre debe ser el mismo.
* Calcularse rápidamente. El hash debe poder generarse de forma rápida y eficiente.
* Tener efecto avalancha. Ante el menor cambio en el archivo de origen, el nuevo hash debe ser completamente diferente. No debe ser predecible.
* Evitar colisiones. No debe ocurrir que para dos archivos de origen diferentes se genere un mismo hash; en realidad, podría ocurrir pero con muy baja probabilidad.

Puedes probar como funciona un hash [aquí](https://andersbrownworth.com/blockchain/hash).

#### **Merkle Tree**

Es una estructura de datos en forma de árbol utilizada en criptografía y en la construcción de blockchain para asegurar y verificar la integridad de los datos almacenados en bloques. Esta estructura fue propuesta por Ralph Merkle en 1979 y se ha vuelto fundamental en sistemas distribuidos y criptomonedas como Bitcoin.

La idea básica detrás de un árbol de Merkle es resumir una gran cantidad de datos en una estructura de árbol, de manera que se pueda verificar eficientemente si un elemento específico está incluido en un conjunto y asegurar la integridad de los datos.

Aquí hay una descripción simplificada de cómo funciona un árbol de Merkle:

1. **División en Pares:**
   * Los datos a ser incluidos en el árbol se dividen en bloques (o elementos individuales).
2. **Hash de Pares:**
   * Cada bloque se somete a una función de hash, creando así un conjunto de resúmenes de hash.
3. **Combinación de Hash:**
   * Los resúmenes de hash se combinan en pares, y a estos pares también se les aplica una función de hash para crear un nivel superior del árbol.
4. **Repetición:**
   * El proceso se repite hasta que solo queda un hash, conocido como la "raíz de Merkle" o "Merkle root". Esta raíz resume todos los datos en el árbol de manera única.

En un árbol de Merkle, cambiar un solo dato en un bloque tendría un impacto en los resúmenes de hash en todos los niveles superiores del árbol, lo que significa que se puede detectar fácilmente si se ha alterado un dato.

<figure><img src="/files/X0dfyWcSC4JVc9PkUH3J" alt=""><figcaption></figcaption></figure>

En la construcción de nuevos bloques, la raíz de Merkle se incluye en la cabecera del bloque y se utiliza para verificar la autenticidad de las transacciones en ese bloque. Si tienes la raíz de Merkle y un conjunto de transacciones, puedes verificar eficientemente si una transacción específica está incluida en el bloque sin necesidad de acceder a todas las transacciones.

#### **Otros usos de la criptografía en blockchain**

La blockchain también utiliza claves criptográficas para garantizar que solo los propietarios legítimos de las criptomonedas puedan realizar transacciones. Cada usuario tiene un par de claves, una pública y una privada, que se utilizan para firmar transacciones.

La clave pública se comparte con otros usuarios de la red y se utiliza para verificar la firma digital de la transacción, mientras que la clave privada se mantiene en secreto y se utiliza para firmar las transacciones.

Entre las principales herramientas de criptografía utilizadas en blockchain tenemos:

* SHA-256 es una función hash criptográfica utilizada en blockchain para asegurar la integridad de los datos. Cuando se agrega un nuevo bloque a la cadena de bloques, se aplica la función SHA-256 a todo el bloque, lo que produce un valor hash único que identifica el bloque. SHA-256 es una de las funciones hash más utilizadas en blockchain debido a su seguridad y eficiencia.
* ECDSA (Algoritmo de firma digital de curva elíptica) es un algoritmo de firma digital utilizado en blockchain para verificar la autenticidad de las transacciones. Cada transacción en la cadena de bloques se firma digitalmente con la clave privada del remitente utilizando ECDSA. Entonces, la red verifica la firma utilizando la clave pública del remitente para asegurarse de que la transacción es auténtica. ECDSA es un algoritmo seguro y eficiente que se utiliza ampliamente en blockchain.
* RIPEMD-160 es una función hash criptográfica utilizada en blockchain para generar direcciones de criptomonedas únicas. Cuando se crea una billetera de criptomonedas, se genera una clave privada única. Luego, se utiliza RIPEMD-160 para generar una dirección pública única a partir de la clave privada. La dirección pública se utiliza para enviar y recibir criptomonedas. RIPEMD-160 es una función hash segura y eficiente que se utiliza ampliamente en blockchain para generar direcciones únicas y proteger la privacidad de los usuarios.

#### **Por qué decimos que bitcoin es una criptomoneda**

Siempre escuchamos que bitcoin es una criptomoneda. Ahora podemos entender por qué es cripto: su existencia está soportada en varias herramientas de criptografía que permiten:

* proteger la historia de las transacciones guardadas en la blockchain
* generar un mecanismo para premiar a los mineros participantes
* verificar que quien envía los bitcoins es el propietario de las monedas
* garantizar que las transacciones sean válidas
* asegurar la privacidad de quienes realizan transacciones.


# Cómo funciona la Blockchain

Para entender cómo funciona una blockchain utilizaremos como ejemplo la primera: Bitcoin. La explicaremos de forma simplificada pero mostrando los principales elementos de la blockchain.

Partamos del hecho de que existe una red distribuida de tipo P2P de computadoras (nodos).

El concepto de red P2P es clave para entender cómo funciona una blockchain. Esta red está permanentemente activa y se encarga de hacer funcionar el sistema de Bitcoin, para ello todos los nodos corren un mismo programa que contiene las reglas de funcionamiento de la blockchain.

Empecemos:

1. Iris quiere enviar un bitcoin a Pepe. Para ello Iris inicia una transferencia desde su billetera digital hacia la dirección bitcoin de Pepe. La transacción registra los datos de dirección de origen, dirección de destino, importe de la transferencia y la comisión si está dispuesta a pagarla.
2. La transacción se difunde desde los nodos más cercanos hacia toda la red P2P. Esto ocurre también en simultáneo con otras transacciones que realizan otros usuarios de la red.
3. Las transacciones se van acumulando en una mempool, que es un fichero en el que los nodos almacenan las transacciones que aún no han sido incorporadas en la blockchain y que por tanto están pendientes de validación.
4. Los nodos mineros (computadoras), que almacenan la blockchain completa, compiten en la creación de un nuevo bloque. Para ello conforman su bloque candidato tomando transacciones de la mempool (en promedio unas mil), añadiendo una cabecera, que entre otros datos contiene: el número de bloque (o altura de bloque), el hash del último bloque creado, la hora de creación , un resumen de las transacciones incluidas y un número dummy denominado nonce que le servirá para participar en la prueba de trabajo a realizar.
5. Con su bloque candidato conformado, cada nodo minero participa en una competencia denominada prueba de trabajo, que requiere consumo a tope de su capacidad de procesamiento, generando hashes utilizando una función como SHA-256. La competencia demora en promedio 10 minutos hasta que un nodo minero obtiene un hash correcto. El nodo minero que obtuvo la solución transmite a toda los nodos de la red su bloque ganador para su verificación.
6. Los nodos verifican que las transacciones sean correctas (que, por ejemplo, Iris tenga bitcoins para transferir a Pepe y que Iris haya autorizado esa transferencia utilizando su clave privada) y que se ha obtenido un hash que cumple los requisitos de la prueba de trabajo. Si todo está bien, se acepta el nuevo bloque y se añade a la blockchain. El nodo ganador recibe del sistema los bitcoins de premio definidos en ese momento (actualmente 6.25 BTC) y las comisiones incluidas en las transacciones de su bloque.
7. Los nodos de la red, retiran de sus mempools las transacciones que ya están en el nuevo bloque y vuelven a trabajar desde el punto 4. La transferencia de Iris a Pepe ya ha sido validada por la red y registrada para siempre en la blockchain.

Aquí tienes un video que resume el proceso anterior:

{% embed url="<https://youtu.be/-n4691RFg9c?si=En3P9qSm8dl2CxsB>" %}


# Tipos de Blockchain

En términos generales, existen tres tipos de blockchains: privadas, públicas e híbridas.

Las blockchains privadas son redes controladas por una entidad centralizada y solo un grupo selecto de participantes tiene permiso para acceder y validar las transacciones.

Las blockchains públicas, en cambio, son redes descentralizadas y cualquier persona puede unirse, enviar transacciones y validarlas.

Las blockchains híbridas combinan características de las blockchains públicas y privadas, ofreciendo flexibilidad y adaptabilidad según las necesidades de los usuarios.

Cada tipo de blockchain tiene sus propias ventajas y desventajas, y la elección de una u otra dependerá de las necesidades específicas del usuario o de la organización que utiliza la red.

<figure><img src="/files/GNyCk8VGeqczVQ7TTjgi" alt=""><figcaption></figcaption></figure>

|           | Privada                                             | Pública                                                     | Hibrida                                     |
| --------- | --------------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------- |
| Propósito | Uso interno de una organización o grupo de empresas | Uso público y descentralizado en todo el mundo              | Combinación de blockchain privado y público |
| Velocidad | Muy rápido por limitación de nodos                  | Más lento, requiere validaciones                            | Varía según el diseño de la red             |
| Seguridad | Alta, porque está limitado a ciertos usuarios       | Alta debido a la descentralización y la validación de nodos | Varía según el diseño de la red             |
| Costo     | Menos costoso por nodos limitados                   | Más costoso debido a mayor cantidad de nodos                | Varía según el diseño de la red             |


# Modelos de Consenso

Consenso es el acuerdo entre un grupo de participantes de un sistema distribuido respecto al estado del sistema. Cuando se alcanza el consenso, todo el sistema distribuido puede ser visto como una sola entidad. El sistema puede estar sujeto a fallas de algunos nodos o a ataques maliciosos. El consenso es llegar a un acuerdo bajo situaciones adversas.

En blockchain, el modelo de consenso se refiere al proceso mediante el cual se toman decisiones sobre qué transacciones se agregan a la cadena de bloques y cómo se hacen. Hay varios modelos de consenso diferentes, y cada uno tiene sus propias ventajas y desventajas.

Los modelos de consenso más conocidos son:

* **Prueba de trabajo (PoW):** Este modelo de consenso es utilizado por Bitcoin y otras criptomonedas. Los nodos de la red compiten para resolver problemas matemáticos complejos, y el primero en hacerlo recibe una recompensa en forma de criptomonedas. Este modelo es muy seguro, pero también es muy exigente en términos de energía y recursos.

  El modelo de consenso de PoW se denomina consenso de Nakamoto que tiene los siguientes atributos:

  * Cualquier nodo se puede unir o abandonar la red en cualquier momento.
  * Cualquier puede enviar mensajes corruptos a los demás.
  * Cualquier usuario puede utilizar tantas identidad como quiera.
  * Para prevenir que obtenga alguna ventaja alguien que crea múltiples identidades de forma deshonesta, el poder de voto debe ser escaso, lo que se logra vinculando el poder de voto a un recurso escaso como energía o electricidad.

  PoW fue diseñado para abordar el problema de los generales bizantinos en una red descentralizada al hacer costoso y difícil manipular la red a los nodos malintencionados, lo que la hace muy segura.

<figure><img src="/files/oLWtzKAQ6NWDiDersS94" alt=""><figcaption></figcaption></figure>

PoW tiene algunas desventajas, como por ejemplo el alto consumo de energía y la centralización en manos de grandes pooles de minería.

* **Prueba de participación (PoS):** Este modelo de consenso se basa en el “stake”, el importe de criptomonedas que los participantes ponen a disposición de la red, en lugar de en la potencia de procesamiento. Los nodos que tienen más criptomonedas tienen mayor probabilidad de ser elegidos para validar las transacciones. Este modelo es más eficiente energéticamente que PoW.

  PoS consume mucho menos energía, ya que no requiere la resolución de problemas matemáticos complejos y costosos, además de que facilita la escalabilidad y el tiempo de confirmación de una transacción es mucho más rápido comparado con PoW.

<figure><img src="/files/4o4IGeMbh85tH1POaf16" alt=""><figcaption></figcaption></figure>

Previamente a entrar en el sorteo para elegir al validador, los participantes ponen a disposición de la red una cantidad determinada de criptomonedas como garantía.

1. El siguiente validador o creador de bloque se selecciona aleatoriamente.
2. El validador crea y propaga el siguiente bloque en la cadena.
3. Otros validadores verifican la integridad del bloque y las transacciones.
4. Los validadores reciben recompensas por comportarse correctamente y pueden ser penalizados por acciones maliciosas.
5. El proceso de selección y validación se repite continuamente.


# El nuevo Internet

Bitcoin es una blockchain especializada en la creación de una moneda (bitcoin), así como en el registro de las transacciones que se realizan con ella. En su momento, algunos pioneros reconocieron que se podía utilizar esta tecnología para otras aplicaciones diferentes. Por ejemplo el proyecto Colored Coins buscaba gestionar activos del mundo real (acciones, monedas, propiedades inmobiliarias) en forma de tokens sobre la red de Bitcoin. Así como en este caso, se empezaron a desarrollar aplicaciones específicas para cada caso de uso sobre Bitcoin, utilizando el lenguaje de programación de este protocolo (Bitcoin Script) y llevando al protocolo de Bitcoin al límite de su potencial.

Pero Bitcoin Script tiene sus limitaciones, no es un lenguaje de programación Turing-complete, pues le falta funciones tan comunes como los loops que permiten realizar una misma instrucción muchas veces utilizando un contador. Tiene sentido restringir esta funcionalidad en una blockchain de uso específico para controlar dinero digital. No queremos por ejemplo que alguien por error o con mala intención genere un loop infinito que haga caer la red; pero no tiene sentido para crear aplicaciones que vayan más allá de controlar transacciones de dinero. Así que estos pioneros tenían que desarrollar sus ideas utilizando un lenguaje de programación limitado y fabricando cada uno sus propias navajas suizas, útiles para un número limitado de casos de uso. En el 2013, un joven ruso-canadiense de 19 años, estudiante de la Universidad de Waterloo en Canadá, llamado Vitalik Buterin, solicitó a su universidad tomarse un año sabático para trabajar en proyectos de criptomonedas. Inició una gira por diferentes lugares del mundo como Estados Unidos, Holanda e Israel, en los que tenía amigos trabajando en la frontera del potencial de Bitcoin.

En Israel había dos proyectos muy innovadores llamados Colored Coins y Mastercoin. Pudo colaborar con ellos pero se dio cuenta rápidamente que el enfoque que utilizaban para el desarrollo no era el adecuado, no se trataba de crear navajas suizas, útiles para propósitos limitados, sino que era mejor crear bloques de LEGO para que cada uno pueda construir lo que quisiera.

Su idea no fue tan bien recibida como él esperaba, pues los líderes de los proyectos a los que hizo su propuesta priorizaban más la oportunidad de salir al mercado con lo que ya tenían, en lugar de aventurarse a alguno nuevo.

Dado que Vitalik no tuvo eco, decidió crear una nueva blockchain que fuera de propósito general, en la que se pudiera programar lo que uno quisiera. Para ello escribió el White Paper de Ethereum «Una plataforma de siguiente generación para smart contracts y aplicaciones descentralizadas» donde describió las especificaciones de una tecnología que revolucionaría el mundo, como años atrás lo había hecho Satoshi Nakamoto.

El [WhitePaper](https://ethereum.org/669c9e2e2027310b6b3cdce6e1c52962/Ethereum_Whitepaper_-_Buterin_2014.pdf) de Vitalik publicado en el 2013 capturó el interés de un grupo de colaboradores, entre los que destacan Gavin Wood, Charles Hoskinson, Mihai Alisie, Anthony Di Iorio y Joseph Lubin. En el 2014 obtuvieron el financiamiento para el proyecto bajo una forma de crowdfunding y en el 2015 pudieron poner en funcionamiento Ethereum gracias al esfuerzo de un ejército de colaboradores a nivel mundial.

Ethereum es una evolución de Bitcoin, que incorpora un lenguaje de programación Turing complete y la posibilidad de ejecutar código que se guarda en la propia blockchain.

Es una gran computadora mundial capaz de ejecutar contratos inteligentes y aplicaciones descentralizadas.

Estas aplicaciones funcionan exactamente como están programadas, sin posibilidad de censura, fraude o interferencia de terceros, ya que el código fuente del contrato inteligente es inmutable.


# Web 3

Web3 se refiere a la tercera fase del desarrollo de la World Wide Web, caracterizada por la descentralización y la adopción generalizada de tecnologías blockchain.

Para comprender completamente Web3, es útil echar un vistazo al contexto histórico.

### **Web1: Estática y de Consumo**

La Web1 se centró en proporcionar información estática. Los usuarios eran principalmente consumidores pasivos de contenido. Las páginas web eran estáticas y la interactividad era limitada.

### **Web2: Interactividad y Colaboración**

Con la llegada de la Web2, la web se transformó en un espacio más interactivo y social. Plataformas como redes sociales, blogs y servicios colaborativos permitieron a los usuarios generar y compartir contenido. Sin embargo, estas plataformas están centralizadas, lo que significa que la mayoría de los datos y el control residen en manos de unas pocas entidades.

### **Web3: Descentralización y Blockchain**

Web3 surge como una respuesta a las limitaciones de la Web 2, abogando por la descentralización y aprovechando tecnologías blockchain para empoderar a los usuarios. La blockchain, la tecnología detrás de criptomonedas como Bitcoin y Ethereum, es fundamental para Web3 debido a su capacidad para crear consenso descentralizado y contratos inteligentes.

|                      | Web estática (web1)                 | Web social (web2)                           | Web descentralizada (web3)                                |
| -------------------- | ----------------------------------- | ------------------------------------------- | --------------------------------------------------------- |
| Interacción          | Unidireccional                      | Bidireccional                               | Descentralizada                                           |
| Tecnología           | Html/Css                            | Javascript                                  | Solidity                                                  |
| Monetización         | Basada principalmente en publicidad | Publicidad y venta de datos de los usuarios | Eliminación de intermediarios (Propia creación de assets) |
| Herramienta          | Website                             | Post                                        | Token                                                     |
| Gobierno             | Comunidad                           | Corporaciones                               | Comunidad                                                 |
| Desbloquea           | Acceso a información                | Posibilidad de publicar                     | Propiedad                                                 |
| Control de datos     | Usuarios sin control de sus datos   | Usuarios con control parcial de datos       | Los usuarios tienen el control total de sus datos         |
| Quien capta el valor | Nadie                               | Corporaciones                               | Participantes                                             |
| Permite              | Leer                                | Leer-Escribir                               | Leer-Escribir-Poseer                                      |


# Elementos Fundamentales

1. **Descentralización:**
   * La descentralización es el pilar de Web3. En lugar de depender de servidores centralizados, Web3 se basa en una red de nodos distribuidos. Esto no solo mejora la resistencia a la censura, sino que también otorga a los usuarios un mayor control sobre sus datos y activos digitales.
2. **Blockchain y Contratos Inteligentes:**
   * La blockchain es esencial para establecer un consenso descentralizado y garantizar la inmutabilidad de los datos. Los contratos inteligentes, programas autoejecutables en la blockchain, permiten acuerdos automáticos y sin intermediarios.
3. **Interoperabilidad:**
   * Web3 promueve la interoperabilidad entre diferentes plataformas y protocolos. Esto significa que los usuarios pueden interactuar y transferir datos y activos de manera fluida entre diversas aplicaciones y servicios sin restricciones innecesarias.
4. **Propiedad y Control de Datos:**
   * Web3 busca devolver la propiedad y el control de los datos a los usuarios. Con la autenticación descentralizada y el cifrado, los usuarios pueden compartir selectivamente sus datos sin perder el control total sobre ellos.
5. **Tokenización de Activos:**
   * La tokenización implica representar activos del mundo real como tokens en la blockchain. Esto facilita la transferencia y la negociación de activos digitales, desde criptomonedas hasta activos físicos como bienes raíces.


# Impacto de Ethereum en Diversos Sectores

1. **Finanzas Descentralizadas (DeFi):**
   * Web3 ha dado lugar a la explosión de las Finanzas Descentralizadas (DeFi), donde los servicios financieros tradicionales se reemplazan por protocolos y contratos inteligentes en la blockchain, permitiendo transacciones y préstamos sin intermediarios.
2. **Identidad Digital:**
   * Los sistemas de identidad digital en Web3 permiten a los usuarios tener control total sobre su información personal, compartiéndola selectivamente y eliminando la necesidad de autenticación centralizada.
3. **NFTs (Tokens No Fungibles):**
   * Los NFTs, tokens únicos e indivisibles en la blockchain, han revolucionado la propiedad digital, permitiendo la autenticación y transferencia de activos digitales como arte, música y bienes virtuales.
4. **Internet de las Cosas (IoT):**
   * Web3 proporciona un marco seguro y descentralizado para la interconexión de dispositivos IoT, mejorando la confiabilidad y la privacidad en la recopilación y el intercambio de datos.
5. **Juegos Descentralizados:**
   * Los juegos basados en blockchain aprovechan Web3 para permitir la propiedad real de activos del juego, así como la interoperabilidad entre diferentes plataformas.


# Wallets

Las wallets o billeteras de criptomonedas en web3 son programas o hardware diseñado para almacenar las claves digitales que permiten el acceso a nuestros activos en la blockchain. Las wallets nos abren las puertas del Jardín Infinito:

<figure><img src="/files/tFNNxuoBv6G1DmBChw4C" alt="" width="282"><figcaption></figcaption></figure>

#### Qué se puede hacer con una wallet

* Conectarte con diversas dApps
* Confirmar transacciones
* Comprar criptomonedas
* Custodiar tus criptomonedas
* Aprobar y revocar permisos
* Otorgar partes de tu identidad digital a aplicaciones descentralizadas
* Comprar o mintear NFT
* Votar a favor de propuestas o peticiones en una DAO
* Crear, construir y gestionar tu identidad Web3


# Componentes de una wallet

Existen cinco componentes de una wallet:

1. **Claves privadas:** Las claves privadas permiten a los usuarios acceder y controlar sus fondos en la red Blockchain. Estas claves son utilizadas para firmar transacciones y para verificar la propiedad de los activos.
2. **Claves públicas**: Las claves públicas son un componente de las claves criptográficas y se utilizan para identificar las cuentas de la wallet en la red Blockchain. Las claves públicas se pueden compartir libremente y se utilizan para recibir transacciones en la wallet. Las claves públicas derivan de la clave privada.
3. **Frase Semilla:** Una frase semilla es una secuencia de palabras que se utiliza para recuperar una wallet en caso de pérdida de las claves privadas. La semilla es una copia de seguridad de las claves privadas y se puede utilizar para restaurar una wallet en un nuevo dispositivo.
4. **Interfaz de usuario:** La interfaz de usuario es la parte de la wallet con la que el usuario interactúa para realizar transacciones, ver saldos y administrar la configuración de la wallet.
5. **Dirección:** La dirección o address de una wallet deriva de la clave pública, formada por 42 hexadecimales, esta dirección la podemos compartir de forma pública a terceros.


# Tipos de Wallet

Existen distintos tipos de Wallets que se pueden utilizar para diferentes situaciones o usos, pero todas permiten al usuario tener un control sobre sus claves privadas.

### **Hot wallet**

Es una wallet en línea y conectada a internet, que es fácil de acceder y usar, pero que puede ser más vulnerable a ataques. Son más convenientes que el otro tipo de billetera debido a su capacidad para almacenar, enviar, recibir y ver tokens.

Entre las más reconocidas están:

* **Metamask:** Es la wallet más popular y con mejor compatibilidad en diferentes dispositivos.
* **Trust Wallet:** Puede enviar, recibir y almacenar Bitcoin y muchas otras criptomonedas y activos digitales de forma segura.
* **Rabby Wallet:** Es multichain EVM y prioriza seguridad respecto a experiencia.

### **Cold wallet**

Una Cold Wallet (o billetera fría) es una forma de almacenar criptomonedas de forma segura, fuera de línea y desconectada de Internet. A diferencia de las Hot Wallets , que están conectadas a Internet y por lo tanto son más vulnerables a los ataques cibernéticos, las Cold Wallets son dispositivos físicos que permiten almacenar las claves privadas de la billetera de forma segura.

Hay diferentes tipos de Cold Wallets, pero los dos principales son los dispositivos hardware y los dispositivos de papel.

* **Dispositivos hardware** son pequeños dispositivos electrónicos que se parecen a una memoria USB, pero que tienen una función específica de almacenar las claves privadas y firmar transacciones. Estos dispositivos se conectan a una computadora a través de un cable USB y permiten la gestión de las criptomonedas de forma segura.

  La forma en que funcionan los dispositivos hardware es que almacenan la información de las claves privadas de forma cifrada, lo que significa que solo el usuario que posee la billetera fría puede acceder a las claves. Para realizar una transacción, el usuario debe conectar el dispositivo a una computadora, ingresar la información de la transacción y confirmar en el dispositivo hardware presionando un botón físico. El dispositivo hardware luego firma digitalmente la transacción con la clave privada y la envía a la red de Blockchain para su verificación y validación.

  Entre las marcas más conocidas en cold wallets están: Trezor y Ledger.
* **Dispositivos de papel** son una forma más rudimentaria de almacenamiento en frío. Consisten en imprimir las claves privadas en papel y almacenarlas en un lugar seguro. Aunque esto puede parecer una forma insegura de almacenamiento, en realidad es bastante segura siempre y cuando se tomen las precauciones adecuadas para proteger la pieza de papel. Por ejemplo, el papel debe ser almacenado en una caja fuerte o lugar seguro y no debe ser accesible por personas no autorizadas.

Otra manera de clasificar los tipos de wallet es según la forma en que el usuario guarda sus claves privadas:

### **Custodial Wallet**

Es una billetera donde un tercero mantiene la custodia de tus claves privadas, por ende, de tus criptomonedas. Fácil de usar, seguro (a menos que el tercero sea hackeado o haga mal las cosas), conveniente (puedes comprar y vender criptomonedas en el mismo lugar). Ejemplos de custodial wallets: Coinbase, Binance, Kraken, etc.

### **Non Custodial Wallet**

Es una billetera donde tú tienes el control total de tus claves privadas y, por lo tanto, de tus criptomonedas. Se tiene mayor seguridad (tú tienes el control total de tus claves privadas), privacidad (no tienes que compartir tus datos personales con un tercero), libertad (puedes usar diferentes servicios para comprar y vender criptomonedas). Ejemplos de custodial wallets: Ledger, Trezor, MetaMask, etc.

#### Comparación Custodial y Non Custodial Wallets

|                            | CUSTODIAL                             | NON CUSTODIAL                            |
| -------------------------- | ------------------------------------- | ---------------------------------------- |
| Control de Claves Privadas | No lo tienes                          | Tú tienes el control                     |
| Seguridad                  | Dependes de terceros                  | Tú eres el responsable                   |
| Privacidad                 | Tienes que compartir datos personales | No tienes que compartir con nadie        |
| Libertad                   | Limitada al servicio de terceros      | Mayor libertad para diferentes servicios |


# Códigos mnemónicos

Los códigos mnemónicos, a menudo llamados frases de recuperación o seed phrases, son secuencias de palabras fáciles de recordar que actúan como representaciones humanas de información criptográfica más compleja. Aunque parecen meras palabras, estas frases encierran el acceso a carteras de criptomonedas, activos digitales y datos sensibles. La simplicidad aparente de las palabras oculta la complejidad matemática y criptográfica que las respalda.

Uno de los estándares más conocidos de generación de códigos mnemónicos es el BIP39, propuesto por el equipo de Bitcoin en 2013. BIP39 (Bitcoin Improvement Proposal 39) establece las pautas para la generación de códigos mnemónicos y su conversión en semillas binarias utilizadas para derivar claves privadas y, en última instancia, para acceder a fondos de criptomonedas. Prueba cómo funciona el BIP39 [aquí](https://iancoleman.io/bip39/).

El estándar BIP39 especifica una estructura precisa para la creación de códigos mnemónicos. Comienza con la elección de una semilla de entropía, que puede generarse de manera aleatoria o derivarse de datos específicos. Esta semilla se somete a funciones criptográficas, como el hash SHA-256, y se divide en segmentos de bits que forman el código mnemónico.

El vocabulario BIP39 consta de una lista de palabras predefinidas, y cada palabra representa un grupo específico de bits de la semilla. El estándar utiliza una longitud de 12, 15, 18, 21 o 24 palabras, brindando opciones para diferentes niveles de seguridad y facilidad de uso.

<figure><img src="/files/bqHrSqmcGsfOySz0Neuo" alt=""><figcaption></figcaption></figure>

La elección de una buena entropía es esencial para la seguridad de un código mnemónico. La entropía, en términos sencillos, es la medida de la imprevisibilidad. Una entropía adecuada garantiza que las claves generadas sean lo suficientemente complejas como para resistir ataques de fuerza bruta y garantiza la seguridad de los activos almacenados.

Una vez que se tiene un código mnemónico BIP39, el proceso de derivación jerárquica (HD, por sus siglas en inglés) es fundamental. HD BIP32 permite derivar una secuencia infinita de claves privadas a partir de una única semilla, lo que facilita la administración de múltiples direcciones y transacciones.

Cada palabra en el código mnemónico actúa como un índice en una jerarquía determinística, permitiendo la generación de claves privadas únicas y, por ende, la seguridad de los fondos asociados.

La implementación exitosa del estándar BIP39 llevó a la proliferación de HD wallets, carteras determinísticas jerárquicas que utilizan códigos mnemónicos para gestionar direcciones y transacciones de forma segura. Estas carteras han simplificado significativamente la experiencia de los usuarios al permitir la recuperación de carteras completas con solo la frase de recuperación.

A pesar de su utilidad, los códigos mnemónicos también presentan desafíos. La seguridad de estas frases recae en la responsabilidad del usuario para almacenarlas de manera segura. Factores como el riesgo de pérdida o robo físico del papel o dispositivo que almacena la frase de recuperación subrayan la necesidad de consideraciones adicionales en la gestión de estos códigos.

Además, la fragilidad humana puede llevar a la elección de frases mnemotécnicas débiles o predecibles, lo que pone en riesgo la seguridad de los fondos. La educación y la conciencia son, por lo tanto, cruciales en la gestión correcta de códigos mnemónicos.


# Ethereum 101

### Ethereum es una computadora

Bitcoin, la primera blockchain, estableció la idea de una moneda digital descentralizada, pero Ethereum llevó la visión un paso más allá. En lugar de limitarse a ser una simple moneda digital, Ethereum se propuso ser una plataforma que permitiera la ejecución de contratos inteligentes y aplicaciones descentralizadas (DApps). Esta visión ambiciosa se cristalizó con la creación del lenguaje de programación turing-complete en Ethereum, permitiendo la creación de contratos inteligentes que van más allá de simples transacciones de valor.

En ese sentido, Ethereum es una computadora mundial descentralizada.

<figure><img src="/files/9EIbV0MYpNnjNj270m8Y" alt=""><figcaption></figcaption></figure>

Decimos que es una computadora porque tiene los mismos componentes de una computadora tradicional:

* **Procesador y memoria.** Los nodos que conforman la red de Ethereum ejecutan un software denominado cliente de ejecución que habilita la Máquina Virtual de Ethereum (Ethereum Virtual Machine - EVM) que es el componente encargado de procesar las transacciones que circulan por la red P2P y que utiliza una memoria temporal.
* **Almacenamiento o storage.** Ethereum también guarda los programas, los datos asociados a ellos, las transacciones y otra información en el almacenamiento de la blockchain.
* **Interfaz.** La forma de interactuar con esta computadora es a través de internet, wallets y aplicaciones descentralizadas.

Adicionalmente a estos componentes y a diferencia de una computadora convencional, Ethereum cuenta con un **protocolo de consenso** que permite que los nodos que conforman la red distribuida se pongan de acuerdo en cada momento respecto a cuál es el estado de la red es decir respecto al estado actual de la blockchain y las transacciones que se han realizado, El consenso es esencial en entornos descentralizados como Ethereum, donde no hay una autoridad central que dicte las reglas. El protocolo de consenso es Proof of Stake (POS)

La EVM constituye la capa de ejecución, el almacenamiento es la capa de disponibilidad de datos (Data Availability) y el POS es la capa de consenso.

La computadora Ethereum tiene los siguientes atributos:

* Es descentralizada, con miles de nodos validadores en todo el mundo.
* No se requiere tener un permiso para acceder a ella (permissionless).
* Es una sola computadora para todo el planeta. No hay una computadora en cada nodo.
* Es verificable. Tanto el software que la soporta (clientes de ejecución y consenso), como los programas que se ejecutan sobre ella son de código abierto. Además que todas las transacciones que se realizan en ella pueden ser trazados.
* Es inmutable. Ethereum, al igual que Bitcoin, es inmutable, lo que significa que una vez que se registra una transacción o un contrato inteligente, no puede ser alterado ni borrado. Esto crea un registro histórico transparente y resistente a la manipulación, generando confianza en un entorno digital donde la confianza es a menudo escasa.


# Smart Contracts

Los códigos ejecutables o programas dentro de Ethereum se llaman smart contracts (contratos inteligentes), bajo la premisa de que un programa es similar a un contrato: una vez cumplidas ciertas condiciones se desencadenan acciones. Ethereum es una computadora mundial descentralizada que ejecuta smart contracts.

Un ejemplo de smart contract para soportar una apuesta por un partido de fútbol podría ser el siguiente:

El smart contract recibe a través de una transacción el importe de la apuesta (10 dólares) de cada uno de los dos jugadores participantes. El jugador A gana si el equipo XYZ gana o empata. El jugador B gana si el equipo XYZ pierde. El smart contract obtiene el resultado del partido de fútbol de una fuente confiable y predefinida, que podría ser otro smart contract, una página web en la que iría a buscar el resultado o de una persona delegada. Según el resultado del partido, el smart contract envía electrónicamente 20 dólares al ganador.

**Respecto al término “Smart Contract”**

Por un lado, el término smart contracts o contratos inteligentes puede ser engañoso, pues no son contratos ni son inteligentes. No son contratos porque no existe obligación legal para su cumplimiento. No son inteligentes porque si se diera alguna situación no contemplada en el smart contract, éste no sabría cómo resolverla, pues para un programa sólo existen las situaciones consideradas en su código.

Por otro lado, el origen de este concepto viene de principios de los 90, cuando Nick Szabo el inventor de Bit Gold, propuso el término indicando que «los smart contracts combinan protocolos con interfaces de usuario para formalizar y asegurar relaciones sobre redes de computadoras. Este esquema elimina la necesidad de pagar a intermediarios como auditores, contadores, abogados y notarios públicos, al ejecutarse los acuerdos a través de un programa de computadora».

Para efectos prácticos, consideremos que los smart contracts son programas que corren en Ethereum. Más adelante en el curso veremos cómo crear un smart contract.


# Cuentas

Las cuentas Ethereum son entidades que tienen un saldo (balance) en ETH que puede ser usado para realizar transacciones. Pueden ser controladas por usuarios o pueden ser habilitadas como smart contracts.

Veamos los tipos de cuentas que existen y el contenido que pueden tener.


# Tipos de cuentas

Hay dos tipos de cuentas:

* **EOA (Externally Owned Account).** Una cuenta de un usuario de Ethereum, controlada por cualquiera que tenga las claves privadas.
* **Contract Account**. Un cuenta de que tiene código ejecutable de contrato inteligente y es controlada por la lógica de su código. No tiene claves privadas.

Ambos tipos de cuentas pueden:

* Recibir, mantener y enviar ETH y tokens.
* Interactuar con smart contracts existentes.

Sin embargo, tienen diferencias importantes.

|                        | Cuenta EOA                          | Contract Account                                                             |
| ---------------------- | ----------------------------------- | ---------------------------------------------------------------------------- |
| Costo de creación      | Ninguno                             | Tiene costo porque utiliza almacenamiento de la blockchain                   |
| Transacciones posibles | Puede iniciar cualquier transacción | Solo puede enviar transacciones en respuesta a una transacción recibida      |
| Interacción con EOAs   | Solo transferencias de ETH          | Una EOA puede enviarle transacciones que disparen la ejecución de su código. |

La dirección Ethereum (address) de una EOA se genera a partir de su clave privada. Primero se crea la clave pública con un primer cifrado y luego la dirección, aplicando un hash a la clave pública (ver claves públicas y privadas en la clase anterior).

Las direcciones de las *contract accounts* también tienen 42 caracteres y empiezan con «0x», pero se generan criptográficamente a partir de la dirección de la cuenta del creador del contrato y del número de transacciones que se han enviado desde esa dirección (nonce).


# Contenido de cuentas

En el gráfico siguiente podemos comparar el contenido de una EOA y una contract account. Si bien una EOA tiene cuatro campos para almacenar información, no utiliza los campos de almacenamiento ni código puesto que estos sólo se habilitan para las cuentas de smart contracts.

<figure><img src="/files/KdpIrziVQsoxswxfnurE" alt=""><figcaption></figcaption></figure>

Los campos son los siguientes:

* **Nonce:** Para una EOA representa la cantidad de transacciones enviadas desde la cuenta. Para una contract account, la cantidad de contratos creados. Cada transacción debe tener un nonce único, y el nonce se incrementa en uno con cada nueva transacción enviada desde la cuenta. Esto ayuda a prevenir *replay attacks* y garantiza que las transacciones se procesen en el orden correcto.
* **Saldo:** La cantidad de ETH que tiene la cuenta.
* **Hash de almacenamiento:** La raíz de un árbol Merkle-Patricia del contenido del almacenamiento del contrato. Los datos del contrato protegidos y comprimidos por criptografía.
* **Hash de código:** El código ejecutable asociado, en forma de hash. No se puede modificar.


# Transacciones

Las transacciones son acciones iniciadas por una EOA, es decir por una cuenta manejada por un humano, no por un smart contract. Una transacción sería por ejemplo enviar 10 ETH de A a B.

Las transacciones cambian el estado de computadora mundial descentralizada a la que se conoce también como una máquina de estados.

<figure><img src="/files/Aom6qB34lQyn2hsAQJNV" alt=""><figcaption></figcaption></figure>

Las transacciones para ser procesadas por la EVM deben ser enviadas a los nodos de la red de Ethereum.

Se dice que las transacciones en Ethereum son atómicas porque son indivisibles. Esto significa que una transacción, o bien se ejecuta por completo, o bien no se ejecuta en absoluto.

#### Tipos de transacciones

Las transacciones más comunes son las siguientes:

* **Transacciones regulares.** Una transferencia de ETH desde una cuenta a otra.
* **Transacción de despliegue de contrato.** Una transacción para crear un contrato inteligente. No tiene dirección de destino (to) y carga el código del smart contract en el campo input.
* **Transacción de ejecución de contrato.** Una transacción que interactúa con un contrato inteligente desplegado. En este caso, la dirección de destino (to) es la dirección del contrato inteligente.


# Componentes

Componentes de una transacción

Una transacción de Ethereum tiene los siguientes componentes:

<figure><img src="/files/70t9Xan7ofYBDZgK6GVR" alt=""><figcaption></figcaption></figure>

#### **Metadatos**

Incluye la información de origen y destino, el importe de ETH a enviar, detalles del gas y la firma:

* **chainId:** identifica a la blockchain. 0x1 para Ethereum.
* **type:** tipo de transacción. Hay dos tipos: nuevo contrato (0x0) y todas las demás (0x2).
* **hash:** hash de la transacción.
* **blockNumber:** el número secuencial que identifica al bloque dentro de la blockchain.
* **blockHash:** el hash del bloque que contiene la transacción.
* **transactionIndex:** posición de la transacción dentro del bloque.
* **from:** dirección que inicia la transacción.
* **nonce:** número de transacciones enviadas desde la dirección que envía la transacción. Una vez que la transacción ha sido incluida en el bloque, el nonce se incrementa. Protege de ataques de replay.
* **to:** dirección a la que se envía la transacción (EOA o cuenta de contrato).
* **value:** importe de ETH a transferir. No se utiliza para otra criptomoneda.
* **gas:** unidades de gas utilizadas por la transacción.
* **gasPrice:** importe pagado en esta transacción por unidad de gas (en wei).
* **maxFeePerGas:** importe máximo dispuesto a pagar por gas (en wei) por el usuario que envía la transacción. Incluye base fee y priority fee.
* **maxPriorityFeePerGas:** importe máximo dispuesto a pagar por gas por el el usuario que envía la transacción (en wei) por encima de la base fee. Este fee se paga directamente al validador para que priorice la inclusión de la transacción en el bloque.
* **(r, s, v)** estos tres valores conforman la firma del usuario que creó la transacción. Se usan para verificar que el usuario autorizó la transacción antes de que sea ejecutada por la EVM. Se usa criptografía de curvas elípticas (ECDSA).

#### **Opciones**

Contiene:

* **accessList:** una lista de direcciones y claves de almacenamiento que la transacción tiene previsto acceder. El access list se introdujo con la propuesta de mejora [EIP-2930](https://eips.ethereum.org/EIPS/eip-2930), que tiene como objetivo mejorar la escalabilidad y reducir los costes de gas de las transacciones que acceden a almacenamiento externo. El costo de gas por acceder estas direcciones tiene un descuento.

#### **Datos**

Contiene:

* **input:** los datos enviados por la transacción en formato binario. .

Existen tres opciones para lo que contiene el input:

1. Transacciones ETH - vacío.
2. Nuevo contrato inteligente - código del contrato inteligente.
3. Llamada a un contrato inteligente - nombre de la función y parámetros para su ejecución.

El campo “input” no es parte del estado de la EVM. Simplemente provee datos para ser utilizados por el contrato durante la transacción.

Así se vería una transacción completa:

<figure><img src="/files/6xw4nUjGt6x3M2Lu1s5I" alt=""><figcaption></figcaption></figure>


# Ciclo de vida

<figure><img src="/files/S4TpTOXscVoMSlODLxyv" alt=""><figcaption></figcaption></figure>

**Paso 1**

La transacción es creada y firmada utilizando normalmente una wallet.

Se obtiene el hash de la transacción y se envía a un nodo de Ethereum, donde será recibida por el cliente de ejecución.

El cliente de ejecución verifica si existe suficiente saldo para procesar la transacción y verifica si la firma es válida.

Una vez aprobada añade la transacción a su mempool local.

Luego transmite la transacción a los nodos conectados de la red.

Los otros nodos reciben la transacción y la añaden a sus mempools locales.

**Paso 2**

Utilizando el protocolo de consenso Proof of Stake, un nodo es seleccionado aleatoriamente para proponer un bloque, para ello seleccionará transacciones de la mempool.

El nodo envía el bloque creado a la red.

**Paso 3**

Se selecciona un grupo de nodos de forma aleatoria que tendrán el rol de testigos (attesters).

Estos nodos vuelven a realizar la creación del bloque localmente para asegurar de que el bloque propuesto es legítimo.

Si el bloque es válido, es añadido a la blockchain local de los nodos.

**Paso 4**

El bloque se añade a la cadena de Ethereum luego de un proceso denominado Finalidad (Finality).

Si 2/3 de los ETH en stake votan a favor de un bloque este pasa a estado “justificado”.

Cuando otro bloque justificado se añade sobre el bloque justificado anterior, el bloque pasa a ser “finalizado”.

De esta manera el bloque final es producido y la transacción queda registrada para siempre.

<figure><img src="/files/PQyheEpgGgxWFKUFHBPb" alt=""><figcaption></figcaption></figure>


# Gas

En la criptoeconomía de Ethereum, gas es el combustible necesario para ejecutar los smart contracts. Es la unidad de medida del poder computacional requerido para ejecutar transacciones.

Una transacción puede comprender muchas instrucciones de máquina. Cada instrucción tiene un costo denominado en gas que tiene un costo preestablecido en el [Yellow Paper de Ethereum](https://ethereum.github.io/yellowpaper/paper.pdf) (apéndice G). Así por ejemplo, una instrucción de tipo suma (ADD) cuesta 3 gas, pero una más compleja como chequear un balance cuesta 700 gas.

El uso de gas también sirve para evitar que los usuarios corran programas que saturen los recursos de la red o que incurran en prácticas de programación ineficientes. Un loop erróneo, por ejemplo, sólo se ejecutará hasta que se le acabe el gas.

Para determinar el fee de un transacción tenemos dos componentes:

1. **Cantidad de gas.** Corresponde a las unidades de gas en función de las operaciones que una transacción consume. No es lo mismo hacer una transferencia de ETH, editar un campo de datos o almacenar un byte de datos. Cada una de estas transacciones consume una cantidad de gas diferente.

| Transacción          | Gas    |
| -------------------- | ------ |
| Transferencia de ETH | 21,000 |
| Editar un slot       | 5,000  |
| Byte de datos        | 16     |

2. **Precio del gas.** Tiene dos componentes: base fee y priority fee. El precio del gas se expresa en una fracción de ETH denominada gwei, que es equivalente a un mil millonésimo de esa moneda (1gwei = 10^-9 ETH).

#### **Base fee**

O tarifa base, se determina de forma automática por el protocolo en función de la demanda de transacciones. Si los bloques se llenan por encima valor objetivo (15 millones de gas), la base fee se incrementa automáticamente. Lo opuesto ocurre cuando los bloques se llenan por debajo del valor objetivo.

A partir de la actualización EIP 1559 (London) en el año 2021, este componente del gas se quema.

#### **Priority fee**

Es el importe que se paga por encima del base fee como una propina para el validador que genera el bloque. En función de este importe los validadores priorizan la inclusión de las transacciones en los bloques.

Según el fee que hayamos incluido nuestra transacción se ejecutará más pronto o más tarde, porque los validadores seleccionarán con preferencia del pool de transacciones que tienen para procesar a aquellas que tengan el precio de gas más alto. Por lo tanto, si tenemos urgencia en que nuestra transacción se ejecute tendremos que proponer un precio de gas más alto que el promedio del momento.

Supongamos que queremos ejecutar una transacción de 21000 gas, como una transferencia de ETH. Si la base fee es 15 gwei y estamos dispuestos a pagar una priority fee de 2 gwei.

El cálculo del fee a pagar por esta transacción, asumiendo que el precio del ETH es US$ 1600, se muestra en el siguiente gráfico:

<figure><img src="/files/vJDjKBn12ybw84Ja1zHf" alt=""><figcaption></figcaption></figure>

### Límite de gas

Si bien la transacción de nuestro ejemplo requería 21,000 unidades de gas, lo normal es que se define un número más alto, denominado «límite de gas» que expresa lo máximo que estamos dispuestos a considerar por transacción, dado que podríamos haber cometido un error y la transacción requiera más de 21,000 gas. Si nuestro estimado se quedara corto, la transacción no terminaría de ejecutarse y perderíamos nuestro dinero. Por otro lado, si la necesidad de gas fuera menor se nos devolvería el gas no consumido. El límite de gas o “gas limit” es normalmente estimado por nuestra wallet en función de las operaciones que la transacción que estamos enviando va a ejecutar, por lo que se recomienda no modificar este dato.

### Dónde visualizar el precio del gas

En sitios como [EtherScan](https://etherscan.io/gastracker) y [Ultra sound money](https://ultrasound.money/) se puede monitorear los precios promedios de mercado de gas. Ten en cuenta que el precio del gas puede variar mucho de un momento a otro, variaciones de 500% o más en pocos minutos no son raras, es importante monitorearlo para no pagar demasiado por una transacción.

<figure><img src="/files/6ZnGskiis0lT023do3uE" alt=""><figcaption></figcaption></figure>


# Solidity

Si bien existen diversos lenguajes de programación para crear contratos inteligentes en Ethereum, el más difundido se llama Solidity, que es muy similar a JavaScript o C++.

A diferencia de Bitcoin Script, Solidity es un lenguaje Turing-complete, es decir que se puede programar lo que uno quiera con él.

Sin embargo la EVM no entiende Solidity sino Bytecode. Por ello, antes de enviar un smart contract o programa a la EVM el código creado en Solidity debe pasar por un compilador que lo convierte en Bytecode.

El código en Bytecode se carga en una transacción de creación de contrato para quedar registrado en la blockchain como una cuenta de contrato.

<figure><img src="/files/hxh2lf9s5jACJYIuJwHr" alt=""><figcaption></figcaption></figure>

En el proceso de compilación hay otro resultado adicional aparte del bytecode. Es la ABI o Application Binary Interface que es un archivo en formato JSON que contiene todos los métodos y sus respectivos parámetros que tiene el contrato inteligente. La comunicación con el contrato inteligente se realiza a través de esta ABI. Sería el equivalente a un API en el mundo Web2. Todo lo que se programa en Ethereum es de código abierto. Esto quiere decir que cualquiera puede descargar ese código para luego modificarlo y crear una nueva aplicación.

Si estás interesado en aprender a programar en Solidity hay muchas fuentes. El sitio oficial de [Solidity](https://docs.soliditylang.org/) es una buena referencia. También encontrarás en el [Módulo 2](/modulo-2/fundamentos-de-solidity) ejercicios y conceptos en el curso del Ethereum Developer Pack.


# EVM

Llegamos al corazón de Ethereum, su procesador y memoria. La Ethereum Virtual Machine (EVM) es el componente clave que permite la ejecución de contratos inteligentes. Funciona como un entorno de ejecución descentralizado, asegurando que el código se ejecute de manera consistente en todos los nodos de la red. Esto garantiza la coherencia y la integridad en un entorno en el que la confianza no se deposita en una entidad central, sino en la red descentralizada en su conjunto.


# La máquina de estados

Ethereum es una máquina de estados y la EVM es el procesador de transacciones que permite los cambios de estados.

El estado es una fotografía de la información que se almacena en la blockchain en un momento determinado.

El estado de una blockhain incluye:

* Los saldos de las cuentas: El saldo de una cuenta es la cantidad de ETH que posee la cuenta.
* Los contratos inteligentes: El estado almacena los contratos inteligentes que se han creado en Ethereum.
* Los datos asociados a los contratos: Por ejemplo quien posee determinado NFT o cuántos tokens ERC 20 tiene una determinada EOA.

Luego de la ejecución de una transacción por la EVM, el estado se actualiza para reflejar los resultados de esa ejecución.

<figure><img src="/files/nfXDUVNc2iIxbA4bA8DF" alt=""><figcaption></figcaption></figure>


# Opcodes

Para que la EVM pueda ejecutar transacciones que llaman a un smart contract debe convertir el bytecode del smart contract a opcodes.

Los opcodes son instrucciones que la Ethereum Virtual Machine (EVM) puede ejecutar. Los opcodes se utilizan para realizar operaciones básicas, como sumar, restar, comparar y asignar valores. También se utilizan para ejecutar operaciones más complejas, como crear contratos inteligentes y llamar a funciones.

Los opcodes de Ethereum se codifican como un byte (2 caracteres hexadecimales). Cada opcode tiene un significado específico. Por ejemplo, el opcode ADD se utiliza para sumar dos valores, el opcode SUB se utiliza para restar dos valores y el opcode EQ se utiliza para comparar dos valores.

La EVM tiene un conjunto de 144 opcodes. Los opcodes se dividen en las siguientes categorías:

* **Operaciones aritméticas.** Se utilizan para realizar operaciones básicas, como sumar, restar, multiplicar y dividir.
* **Operaciones lógicas.** Se utilizan para realizar operaciones lógicas, como AND, OR y NOT.
* **Operaciones de comparación.** Se utilizan para comparar dos valores.
* **Operaciones de asignación.** Se utilizan para asignar un valor a una variable.
* **Operaciones de control de flujo.** Se utilizan para controlar el flujo de ejecución de un contrato inteligente.
* **Operaciones de memoria.** Se utilizan para manipular la memoria de la EVM.
* **Operaciones de llamadas.** Se utilizan para llamar a funciones de contratos inteligentes.
* **Operaciones de creación de contratos.** Se utilizan para crear nuevos contratos inteligentes.

Ejemplos de opcodes

Aquí hay algunos ejemplos de cómo se utilizan los opcodes de Ethereum:

Para sumar dos valores, se puede utilizar el opcode ADD. Por ejemplo, el siguiente código sumará los valores de las variables x e y:

x = 10 y = 20

z = ADD(x, y)

Para comparar dos valores, se puede utilizar el opcode EQ. Por ejemplo, el siguiente código comparará los valores de las variables x e y:

x = 10 y = 20

z = EQ(x, y)

Para asignar un valor a una variable, se puede utilizar el opcode MSTORE. Por ejemplo, el siguiente código asignará el valor 10 a la variable x: x = 10

Una muy buena referencia para entender el funcionamiento de los opcodes es la página [EVM Opcodes](https://www.evm.codes/) donde puedes ver la relación entre el código de un smart contracts y sus opcodes.


# Cómo funciona la EVM

De una forma simplificada, cuando un usuario quiere ejecutar una función de un smart contract, envía una transacción hacia la dirección o cuenta del smart contract. La EVM toma el bytecode, lo descompone en opcodes, los ejecuta y actualiza el nuevo estado en la blockchain. Tal como se muestra en el gráfico siguiente.

<figure><img src="/files/fn7TGBFT2opC1CL4yJlc" alt="" width="563"><figcaption></figcaption></figure>

Para ver la ejecución con mayor detalle veamos cuáles son los componentes que tiene la EVM.

**El código** El código es el lugar donde se almacena el contrato inteligente. Los datos de programa almacenados en el código son persistentes como parte de un campo de estado de una contract account. El código es el bytecode interpretado y ejecutado por la EVM durante la ejecución del contrato inteligente. El código es inmutable, lo que significa que no se puede modificar, pero se puede leer con las instrucciones CODESIZE y CODECOPY. El código de un contrato puede ser leído por otros contratos, con instrucciones EXTCODESIZE y EXTCODECOPY.

**El Contador de Programa (PC)** El Contador de Programa codifica qué instrucción, almacenada en el bytecode, debe ser leída a continuación por la EVM. El PC normalmente se incrementa en un byte para apuntar a la siguiente instrucción, con algunas excepciones. Por ejemplo, la instrucción PUSHx tiene más de un byte y hace que la PC omita su parámetro. La instrucción JUMP no aumenta el valor de la PC, sino que modifica el contador del programa a una posición especificada en la parte superior de la pila. JUMPI también hace esto, si su condición es verdadera (un valor de código distinto de cero); de lo contrario, incrementa la PC como otras instrucciones.

**La pila o stack** El stack es una lista de elementos de 32 bytes que se utilizan para almacenar entradas y salidas de instrucciones de contratos inteligentes. Se crea una pila por contexto de llamada y se destruye cuando finaliza el contexto de llamada. Cuando se coloca un nuevo valor en la pila, se coloca en la parte superior y las instrucciones solo utilizan los valores superiores. La pila actualmente tiene un límite máximo de 1024 valores. Todas las instrucciones interactúan con la pila, pero se puede manipular directamente con instrucciones como PUSH1, POP, DUP1 o SWAP1.

**La memoria** La memoria EVM no es persistente y se destruye al final del contexto de llamada. Al comienzo de un contexto de llamada, la memoria se inicializa a 0. La lectura y escritura de la memoria generalmente se realiza con las instrucciones MLOAD y MSTORE respectivamente, pero también se puede acceder a ellas mediante otras instrucciones como CREATE o EXTCODECOPY.

**El almacenamiento o storage** El almacenamiento es una correspondencia o mapa de slots de 32 bytes a valores de 32 bytes. El almacenamiento es la memoria persistente de los contratos inteligentes: cada valor escrito por el contrato se retiene después de completar una llamada, a menos que su valor se cambie a 0 o se ejecute la instrucción SELFDESTRUCT. La lectura de bytes almacenados de una clave no escrita también devuelve 0. Cada contrato tiene su propio almacenamiento y no puede leer ni modificar el almacenamiento de otro contrato. El almacenamiento se lee y escribe con instrucciones SLOAD y SSTORE.

En el gráfico siguiente se visualiza cómo interactúan estos componentes. Ante una llamada a un smart contract se ejecutan los siguientes pasos:

1. Se identifica el código a ejecutar.
2. Mientras haya gas disponible, el contador de programa (PC) va incorporando una a una las instrucciones que se traducen en opcodes y se ejecutan en la pila (stack), que se apoya en la memoria (temporal) o en el storage (persistente), para almacenar y recuperar datos asociados a la ejecución.
3. Si el gas no se agotó antes de terminar la ejecución, se ha generado un nuevo estado para la blockchain. De lo contrario, el estado se revierte a la situación anterior a la ejecución de la transacción. El gas consumido no se recupera.

<figure><img src="/files/gTdAis8ew4pbDwtgpPtO" alt=""><figcaption></figcaption></figure>


# Clientes de ejecución

¿Dónde vive la EVM? La EVM es parte del cliente de ejecución que es el software que corre en los nodos.

Las funciones de un cliente de ejecución incluyen:

* **Recibir y procesar transacciones.** Recibe las transacciones de la red y las procesa según las reglas de la red Ethereum.
* **Ejecutar contratos inteligentes.** Ejecuta los contratos inteligentes según su código.
* **Mantener el estado de la cadena de bloques.** Actualiza el estado de la cadena de bloques con cada nueva transacción o contrato inteligente que se ejecuta.

Los clientes de ejecución son de código abierto y han sido programados en diferentes lenguajes para reducir el riesgo en el funcionamiento de la blockchain y de que exista un solo punto de falla.

En el cuadro siguiente se muestran los principales clientes de ejecución que existen.

| Cliente    | Lenguaje | Sistema Operativo     |
| ---------- | -------- | --------------------- |
| Geth       | Go       | Linux, Windows, macOS |
| Nethermind | C#, .NET | Linux, Windows, macOS |
| Besu       | Java     | Linux, Windows, macOS |
| Erigon     | Go       | Linux, Windows, macOS |
| Reth       | Rust     | Linux, Windows, macOS |


# DApps

Las Dapps (Decentralized Applications) son aplicaciones descentralizadas y son la forma en que normalmente interactúan los usuarios con los smart contracts de una blockchain como Ethereum.

Las dApps proveen una interfaz amigable, detrás de la cual orquestan la ejecución de los diferentes smart contracts que requieren para proveer su servicio. Una página donde se puede hacer seguimiento a las principales Dapps que existen en la actualidad es [DappRadar](https://dappradar.com/).

Existen miles de dApps disponibles en diversas categorías como redes sociales, subastas, juegos, seguros, exchanges, etc.

La arquitectura de una Dapp se muestra a continuación.

<figure><img src="/files/tVGu7LMs02cNain5ulzD" alt="" width="563"><figcaption></figcaption></figure>


# Blockchain Explorer

Un blockchain explorer es una herramienta que permite a los usuarios explorar una cadena de bloques. Los blockchain explorers pueden proporcionar información sobre la blockchain, como los bloques, las transacciones, las direcciones y los contratos inteligentes.

Los blockchain explorers funcionan recopilando datos de la blockchain. Los datos se recopilan utilizando una variedad de métodos, como el rastreo de los nodos de la cadena de bloques o el uso de APIs proporcionadas por los desarrolladores de las blockchains.

Una vez que los datos se han recopilado, se almacenan en una base de datos. Los usuarios pueden acceder a los datos de la base de datos utilizando una interfaz de usuario proporcionada por el blockchain explorer.

Uno de los blockchain explorers más usados es [Etherscan](https://etherscan.io/).

<figure><img src="/files/npMqDO0XKRWIpV6N7qYe" alt=""><figcaption></figcaption></figure>


# Funciones de un blockchain explorer

Los blockchain explorers pueden proporcionar una amplia gama de funciones, incluyendo:

* Explorar bloques: Permiten ver los detalles de la blockchain. Esto incluye información como la fecha y hora de creación del bloque, el hash del bloque, la dificultad del bloque y las transacciones incluidas en el bloque.
* Explorar transacciones: Podemos encontrar información como la fecha y hora de la transacción, la cantidad de ETH transferida, la dirección de origen y la dirección de destino.
* Explorar direcciones: Información como el saldo de la dirección, las transacciones enviadas desde la dirección y las transacciones recibidas en la dirección.
* Explorar contratos inteligentes: Podemos encontrar el código del contrato, los datos de la instancia del contrato y las transacciones realizadas en el contrato.


# Beneficios de utilizar un blockchain explorer

Los blockchain explorers ofrecen beneficios para los usuarios de blockchain:

* Transparencia: Los blockchain explorers proporcionan una visión transparente de la cadena de bloques. Esto permite a los usuarios ver todas las transacciones y bloques que se han realizado en la cadena de bloques.
* Accesibilidad: Los blockchain explorers son herramientas accesibles que pueden ser utilizadas por personas de todos los niveles de experiencia.
* Utilidad: Los blockchain explorers pueden ser utilizados para una variedad de propósitos, como la investigación, el análisis y el desarrollo de DApps.


# Remix

Remix es un entorno de desarrollo integrado (IDE) de código abierto para el desarrollo de contratos inteligentes en Ethereum. Proporciona una variedad de herramientas y funcionalidades que hacen que el desarrollo de contratos inteligentes sea más fácil y rápido. La ventaja de esta herramienta es que se puede utilizar directamente desde un navegador internet.

Remix está disponible en: <https://remix.ethereum.org/>


# Características de Remix

Remix ofrece una amplia gama de características, que incluyen:

* **Editor de código.** Remix incluye un editor de código completo que admite la sintaxis de Solidity, el lenguaje de programación para contratos inteligentes de Ethereum.
* **Integración con Solidity Compiler.** Remix está integrado con el compilador de Solidity, lo que permite a los desarrolladores compilar sus contratos inteligentes directamente desde el IDE.
* **Integración con Ethereum Wallets.** Remix se integra con una variedad de carteras Ethereum, lo que permite a los desarrolladores enviar y recibir ETH y tokens ERC-20.
* **Emulador de Ethereum.** Remix incluye un emulador de Ethereum que permite a los desarrolladores probar sus contratos inteligentes sin tener que desplegarlos en la cadena de bloques.
* **Debugger.** Remix incluye un depurador que permite a los desarrolladores depurar sus contratos inteligentes paso a paso.

Remix tiene cuatro paneles:

<figure><img src="/files/Q6ba45mKONvY7rwkgLFR" alt=""><figcaption></figcaption></figure>

* La mayoría de plugins (complementos) aparecen en el panel lateral al ejecutarse.
* La edición de código se hace en el panel principal.
* Los resultados de las transacciones y otras acciones se muestran en el terminal.
* El salto de un plugin a otro se hace en el panel de íconos.

En el panel principal, si haces clic en el botón **Home** puedes acceder a los plugins destacados, plantillas, tutoriales y otra información útil.

<figure><img src="/files/NFg5vGculL2FRnFqUZCn" alt=""><figcaption></figcaption></figure>

Puedes acceder a la lista completa de plugins a través del botón Plugin Manager ubicado en la parte inferior del panel de íconos.

<div align="left"><figure><img src="/files/awYTQPclNo0RUpYFQm5Y" alt="" width="127"><figcaption></figcaption></figure></div>

En la parte superior del panel de íconos encontrarás los siguientes botones por defecto.

<figure><img src="/files/pPqVLiFNzFongBKWcXHh" alt="" width="375"><figcaption></figcaption></figure>


# Workspaces o espacios de trabajo

Los espacios de trabajo te permiten separar tus proyectos. Puedes guardar muchos contratos inteligentes y la información asociada a ellos en un mismo espacio de trabajo.

Para crear un espacio de trabajo, haz clic en el explorador de archivos del panel de íconos, luego selecciona el menú hamburguesa y selecciona la opción Create.

<figure><img src="/files/AJwYFwOtjvSL6Rg78d88" alt="" width="375"><figcaption></figcaption></figure>

Observa que no sólo puedes crear espacios de trabajo, sino que tienes muchas opciones más.


# Cargar y compilar un contrato

Para cargar un contrato, debes hacer clic en el ícono del explorador de archivos en el panel de íconos.

Luego selecciona el contrato que te interesa cargar desde la carpeta contracts del espacio de trabajo en el que estás y haz click sobre él. El archivo aparecerá en el panel principal.

<figure><img src="/files/zTrsr9kwG238951voLRi" alt=""><figcaption></figcaption></figure>

Para compilarlo, haz en el click en el ícono del compilador. Te aparecerá el compilador en el panel lateral.

<figure><img src="/files/oTBobRXa7wMRVXfrGW23" alt="" width="375"><figcaption></figcaption></figure>

En la parte superior puedes ver la versión del compilador que está activa. Puedes escoger una versión diferente si es necesario.

En el cuadro CONTRACT puedes ver el contrato que vas a compilar.

Para proceder a compilar haz click sobre el botón azul.


# Desplegar en la máquina virtual de Remix (Remix VM)

De lo revisado anteriormente, recordarás que compilar un contrato implica convertir el código de Solidity en Bytecode. Ahora llevaremos ese código a una blockchain de prueba.

Para ello, haz clic en el ícono de desplegar y ejecutar transacciones.

<figure><img src="/files/GuxvY5JQAPZKkGQgf4rp" alt="" width="375"><figcaption></figcaption></figure>

En el campo ENVIRONMENT puedes seleccionar diferentes máquinas virtuales.

También pueden visualizar la cuenta desde la que desplegarás el contrato (campo ACCOUNT), donde Remix te ha asignado 100 ETH de prueba. En realidad tienes disponible 15 cuentas de prueba cada una con 100 ETH. Puedes seleccionar cualquiera de ellas en ese campo.

El GAS LIMIT es la cantidad de gas máxima que estás dispuesto a consumir en el despliegue del contrato. Es relevante cuando tengas que hacer el despliegue en una blockchain pública.

El campo VALUE es la cantidad de ETH que enviarás con la transacción, puedes seleccionar expresar el valor en ETH, wei, gwei o Finney.

El campo CONTRACT indica cuál es el contrato que vas a desplegar. Es importante verificarlo cuando tienes un código que contiene varios contratos.

Cuando hayas verificado todos los valores haz click en el botón naranja Deploy.

Habrás desplegado tu contrato compilado en la Remix VM, una blockchain simulada que corre en tu ventana de navegador. Es un ambiente muy conveniente para realizar prototipos muy rápidamente. A diferencia de una blockchain pública como Ethereum no requieres aprobar cada transacción.


# Interactuando con funciones

Ahora accederemos a las funciones de un contrato desplegado. Puedes observar que luego de haber desplegado el contrato aparece en la parte inferior del panel lateral un contrato desplegado en la sección Deployed Contracts.

<figure><img src="/files/4GHFtnHZ4DOe8wnzwoaT" alt="" width="375"><figcaption></figcaption></figure>

Selecciónalo y te aparecerán las funciones habilitadas en el contrato desplegado.

<figure><img src="/files/oIY6T0sSgCdRo2ju1d2L" alt="" width="375"><figcaption></figcaption></figure>

Una función aparece en color naranja y te permite ingresar un campo de datos de tipo número. Es una función pública (accesible por cualquiera) y que puede modificar datos de la blockchain, por lo tanto tendrá un costo de gas.

La otra función aparece en azul y es una función de solo lectura que no consumirá gas.

Para ejecutar la función de escritura (naranja) se debe ingresar un dato numérico y hacer click sobre el botón.

En el caso de la función de lectura sólo hace falta hacer clic sobre el botón.

Si el contrato se ha desplegado sobre Remix VM no será necesario aprobar ninguna de estas transacciones.


# Desplegar en una red pública

Para hacer el despliegue sobre una red pública, ya se mainnet o una testnet como Sepolia, es necesario contar con una wallet como Metamask con ETH suficiente para pagar el gas de la transacción.

Debes conectar tu wallet a la red en la que quieres hacer el despliegue del contrato.

Luego debes proceder de forma similar a el despliegue que hicimos sobre Remix VM, solo que ahora habrá que seleccionar un ENVIRONMENT diferente. En nuestro caso seleccionamos: Injected Provider - Metamask. Nuesta wallet está conectada a la red de prueba Sepolia y tiene fondos suficientes para ejecutar la transacción de despliegue.

<figure><img src="/files/PJhvR14PFQhziUCJWuBl" alt="" width="375"><figcaption></figcaption></figure>

Verificamos que el contrato es el correcto y hacemos clic sobre Deploy.

Esta vez sí requerimos firmar la transacción para aprobar el consumo de gas.

<figure><img src="/files/wSlnRKCPSbmdvyQWgXuv" alt="" width="375"><figcaption></figcaption></figure>

Damos a Confirmar y la transacción de creación será enviada.

Unos segundos después podremos ver creado nuestro contrato en la sección Deployed Contracts.

<figure><img src="/files/cPlIBqtJCMzrtAHXUeMp" alt="" width="375"><figcaption></figcaption></figure>

En el terminal veremos datos de la transacción ejecutada.

<figure><img src="/files/VOqm72ENImGWRqxPPnlW" alt=""><figcaption></figcaption></figure>

Ahora podemos interactuar con las funciones del contrato de la misma forma que lo hicimos en Remix VM. Solo que esta vez tendremos que aprobar la transacción de escritura y pagar el gas necesario para ejecutarla, utilizando nuestra wallet.


# Crea tu primer Smart Contract

En esta parte final de este primer módulo vamos a crear un contrato inteligente y desplegarlo en una red de prueba (testnet) de Ethereum, como Sepolia.

El contrato que desplegaremos es Register01.sol:

```solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.26;

contract Register01 {
    string private storedInfo;

    function setInfo(string memory myInfo) external {
        storedInfo = myInfo;
    }

    function getInfo() external view returns (string memory) {
        return storedInfo;
    }
}
```

Veamos lo que hace Register01.sol:

Este contrato simple permite almacenar y recuperar información de texto. La función setInfo se utiliza para modificar la información almacenada, mientras que la función getInfo se utiliza para consultar la información actual sin realizar cambios en el estado del contrato.

* Versión del Compilador Solidity Utilizada: 0.8.26. En Solidity se utiliza la keyword **pragma** para declarar la versión de compilador que se debe utilizar para ejecutar el contrato.
* Licencia: GPL-3.0
* Variables de Estado:
  * **storedInfo**: Variable privada que almacena información de tipo string (texto).
* Funciones:
  * **setInfo** (Función de Transacción Externa): Actualiza el valor de storedInfo con la información proporcionada como argumento.
    * Parámetros: myInfo, el texto a cargar en storedInfo.
  * **getInfo** (Función de Consulta Externa): Devuelve el valor actual de storedInfo sin modificar el estado del contrato.
    * Parámetros: Ninguno

Llevemos el código del contrato a Remix.

<figure><img src="/files/an7pZdTF52GPs6KWdI6C" alt=""><figcaption></figcaption></figure>

El check verde al lado del ícono del compilador nos indica que se compiló correctamente. Podemos proceder a desplegarlo.

<figure><img src="/files/bi4Z00p00EKxMHcccIAC" alt=""><figcaption></figcaption></figure>

Debemos asegurarnos de:

* Tener nuestra wallet conectada a Sepolia,
* Haber seleccionado en Environment la opción Injected Provider-Metamask,
* Tener suficiente ETH en Sepolia y
* Verificar que el contrato a desplegar es el correcto.

Ahora sí procedamos a desplegar.

<figure><img src="/files/aQOBlj3N9G3tDkP4PH0C" alt="" width="375"><figcaption></figcaption></figure>

Aprobemos la transacción en nuestra wallet.

<figure><img src="/files/iIrpAELpj2MzwRmeMtj8" alt=""><figcaption></figcaption></figure>

El contrato ha sido desplegado y se le ha asignado una cuenta de contrato (contract account) en Sepolia. Haciendo clic en el ícono de copiar del gráfico anterior podemos obtener esta cuenta.

<figure><img src="/files/friVe7ckMSwoyXhqIzPO" alt="" width="375"><figcaption></figcaption></figure>

En el ejemplo es: 0x94A6985da19A3eA8A1182FA24791069365CAB79f

Vayamos al [explorador de bloques de Sepolia](https://sepolia.etherscan.io/) para comprobarlo.

Ingresamos la cuenta en el campo de búsqueda de Sepolia

<figure><img src="/files/fuvmHjrkc0RncNeiZGRE" alt=""><figcaption></figcaption></figure>

Podemos confimar que se ha creado una cuenta de contrato desde nuestra EOA en Sepolia.

<figure><img src="/files/96sDQALKNYhe5R5ApI31" alt=""><figcaption></figcaption></figure>

Si vamos a la pestaña Contract podemos visualizar el bytecode que se ha desplegado en esta cuenta de contrato

<figure><img src="/files/z8DItbaZdkXJAcTrnhqL" alt=""><figcaption></figcaption></figure>

¿Cómo podemos comprobar que corresponde al código de Register01.sol?

Debemos Verificarlo y Publicarlo. Para ello hacemos clic sobre la frase en azul “Verify and Publish”.

Nos aparecerá la pantalla siguiente en la que debemos completar la dirección del contrato, el tipo de compilador utilizado, la versión de compilador y el tipo de licencia Open Source. Luego hacemos clic en Continuar.

<figure><img src="/files/Sp9vMo08SZ97elEf0QEo" alt=""><figcaption></figcaption></figure>

En la siguiente pantalla debemos copiar el código Solidity de Register01.sol

<figure><img src="/files/yVITMG6222nK45ZR4Kwh" alt=""><figcaption></figcaption></figure>

Completamos el captcha y hacemos clic en Verify and Publish.

<figure><img src="/files/qoVvgRn1u6TIDGTrFzNp" alt="" width="254"><figcaption></figcaption></figure>

<figure><img src="/files/byAFCUuk54U5g5YrNbhV" alt=""><figcaption></figcaption></figure>

Si la correspondencia es correcta, nos aparecerá una pantalla como la siguiente donde ahora también podremos ver la ABI.

Si regresamos a la cuenta del contrato en Etherscan veremos que ahora hay información adicional.

<figure><img src="/files/DAJC4tb2AVOw8nXqJNfg" alt=""><figcaption></figcaption></figure>

Junto al botón azul del contrato ahora hay un check verde, que indica que el contrato ha sido verificado. El código del contrato ahora también aparece junto al bytecode y el ABI.

Pero también aparecen botones para interactuar con el contrato: Read Contract (para funciones de lectura) y Write Contract (para escritura/modificación).

Podemos interactuar entonces con nuestro contrato inteligente desde el explorador de bloques.

Por ejemplo, si quisiéramos grabar en la variable storedInfo la frase “Hello World”, tendríamos que hacer clic sobre el botón Write Contract.

<figure><img src="/files/4VsMSRFyqA7p6pqdSWBQ" alt=""><figcaption></figcaption></figure>

Vemos que setInfo es la única función de escritura. Para utilizarla primero debemos conectar nuestra wallet a Etherscan. Para ello, hacer clic en Connect to Web3.

<figure><img src="/files/MXrtNKNOmatyiVS15gbK" alt=""><figcaption></figcaption></figure>

Una vez conectada nuestra wallet, nos aparece el campo para ingresar el texto a grabar. Lo ingresamos y hacemos clic sobre Write.

Al ser una transacción de escritura, debemos aprobar la transacción y pagar el gas correspondiente.

<figure><img src="/files/PgYiUUbHYsjILSkPtS9D" alt="" width="375"><figcaption></figcaption></figure>

Para verificar si la transacción ha sido exitosa podemos ir a Read Contract y visualizar el contenido de la variable storedInfo utilizado la función de lectura getInfo.

<figure><img src="/files/nXCp6MoTI6dDUo6Oruf9" alt=""><figcaption></figcaption></figure>

getInfo nos muestra que el contenido es “Hello World”.

Recapitulando has seguido los siguientes pasos:

1. Creaste un contrato
2. Lo compilaste
3. Lo desplegaste en una blockchain pública, en este caso Sepolia.
4. Lo validaste y publicaste utilizando un explorador de bloques. De forma que una persona interesada en tu contrato pueda entender qué hace.
5. Interactuaste con el contrato utilizando el explorador de bloques.

### **¡Felicitaciones has culminado la Intro a Smart Contracts!**


# Fundamentos de Solidity

**Objetivo:** Aprender el principal lenguaje para programar smart contracts: Solidity.&#x20;

**Duración:** 9 horas (3 clases de 3 horas cada una).

<figure><img src="/files/Xa12h2ptrMfqkf7KKuFK" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Este documento es una herramienta pedagógica que actualizamos constantemente. Creemos en la importancia de contenidos open-source. Si quieres mejorar los contenidos, únete a nuestro programa de [Kipu Explorers](/contribuye/kipu-explorer).
{% endhint %}


# Hello World

### License

Todos los programas deben empezar indicando su tipo de licencia, por ejemplo:

```solidity
// SPDX-License-Identifier: MIT
```

SPDX señala el tipo de licencia que corresponde al código. Si no se incluyen un tipo de licencia, el compilador de Solidity dará una advertencia. El SPDX se incluye dentro del bytecode por el compilador. En el caso de programas que son de código abierto como los que se utilizan en Ethereum, es importante dejar en claro los derechos de autor involucrados para que quienes quieran utilizarlos, o incluso copiarlos, tengan en cuenta las consecuencias legales. Por ejemplo los contratos inteligentes de Uniswap V3 utilizan la siguiente licencia:

```solidity
// SPDX-License-Identifier: BUSL-1.1
```

La licencia BUSL protege los derechos de autor por un período de tiempo (por ejemplo 3 años) después del cual la licencia pasa a ser de dominio público.

Una lista completa del tipo de licencias se encuentra [aquí.](https://spdx.org/licenses/)

Una de las licencias más usadas es la MIT, que es una licencias sin restricciones, pero que por otro lado no asume responsabilidades por el uso del software.

### Pragma

La versión de Pragma indica la versión del compilador que debe ser utilizada para que el código sea compilado sin fallos. Si el compilador utilizado no corresponde al pragma del código se emitirá un error.

Por ejemplo, una declaración de pragma típica en Solidity puede verse así:

```solidity
pragma solidity ^0.8.0;
```

En este caso, "^0.8.0" significa que el contrato puede ser compilado por cualquier compilador de Solidity desde la versión 0.8.0 hasta versiones que no introduzcan cambios incompatibles. Esto ayuda a prevenir problemas de incompatibilidad cuando se utiliza el contrato en diferentes entornos y versiones del compilador.

### Contract

**Contract** es la palabra que representa en Solidity a un contrato. Un contrato en Solidity es una entidad que contiene código ejecutable en Ethereum. Los contratos pueden interactuar con otros contratos y contener variables, funciones y eventos que definen su comportamiento.

En lenguajes orientados a objetos su equivalente sería una clase. Este sería un ejemplo de un contrato muy simple.

```solidity
contract HelloWorld {
string public greet = “Hello World!”;
}
```

Este es un contrato inteligente denominado **HelloWorld** que contiene una sola variable llamada **greet** que es de tipo string y es visible públicamente, a la que se le ha asignado el valor “Hello World!”. El código del contrato inteligente está delimitado por las llaves (”{” y “}”).

### Comentarios

Los comentarios en Solidity, al igual que en muchos otros lenguajes de programación, tienen como finalidad proporcionar explicaciones y documentación dentro del código fuente. Estos comentarios no afectan la ejecución del programa y son ignorados por el compilador.

Los comentarios son una forma de documentar el código fuente para que otras personas (o incluso el mismo programador en el futuro) puedan entender rápidamente la lógica y el propósito de las diferentes partes del contrato.

Los comentarios también se utilizan para explicar el propósito y el funcionamiento de funciones y variables, lo que facilita la comprensión del código y su uso.

Solidity permite realizar comentarios en una sola línea o en múltiples líneas.

Para un comentario en una sola línea se utilizan el símbolo “//” para iniciarlo. Lo que esté a continuación del símbolo y hasta el final de la línea será ignorado por el compilador.

```solidity
// Este es un comentario y será ignorado por el compilador 
```

Para incluir un comentario que utilice múltiples líneas se debe utilizar los símbolos “/*” y “*/” para delimitarlo.

```solidity
/* Este es un comentario de varias
líneas que también
será ignorado por el compilador*/ 
```


# Tipos de Datos

Solidity es un lenguaje de tipado estático. Esto significa que el tipo de una variable se determina al momento de la compilación y no puede cambiar durante la ejecución del programa. En Solidity, cuando declaras una variable, es necesario especificar su tipo de manera explícita, y este tipo no cambia a lo largo del código una vez establecido.

Por ejemplo una variable denominada **myNumber** de tipo entero se declara de la siguiente manera.

```solidity
uint256 myNumber; 
```

Este enfoque proporciona beneficios como la detección temprana de errores y la optimización de rendimiento, ya que el compilador puede realizar verificaciones y optimizaciones específicas basadas en el tipo de datos conocido. Por el contrario, lenguajes de tipado dinámico como Python o JavaScript permiten que el tipo de una variable pueda cambiar durante la ejecución del programa.

Existen dos tipos de datos: tipos de valor y tipos de referencia.

### Tipos de valor

Este tipo de datos almacenan directamente el valor.

A continuación indicamos los que corresponden a esta categoría.

#### Enteros sin signo (uint)

Corresponde a números enteros no negativos (unsigned) Ejemplo:

```solidity
uint256 varInt = 25; // se define un uint con valor 25
```

Dependiendo del tamaño de tus datos puedes elegir desde 8 bits hasta 256 bits, en saltos de 8. Por ejemplo, uint8, uint16, uint24 hasta uint256.

Los uint8 solo cubren valores de 0 a 255 (2^8-1).

🚨 Si la variable toma un valor fuera de estos extremos ocurrirá un error en la ejecución y la transacción se revertirá.

Si quisiéramos asignar a este tipo de datos el valor máximo posible tendríamos que utilizar la siguiente instrucción

```solidity
uint256 maxVal = type(uint256).max;
```

💡Si al momento de escribir tu código sólo indicas “uint” sin señalar el número de bits, se asumirá que corresponde a uint256.

🥸 El valor por defecto de este tipo de dato es cero.

#### Enteros con signo (int)

Números enteros con signo. Ejemplo:

```solidity
int8 varInt = -4; // se define un int con valor -4
```

Aplica las mismas consideraciones respecto al número de bits que en el caso de los uint.

Sin embargo, en este caso int8 cubre los valores entre -128 y 127 (256 valores enteros total).

🥸 Su valor por defecto es cero.

#### Booleanos (bool)

Valores booleanos, es decir, true o false.

Ejemplo:

```solidity
bool isAllowed = true; // se define isAllowed como verdadero
```

🥸 Su valor por defecto es “false”.

#### Dirección (address)

Direcciones de 20 bytes en Ethereum. Representan direcciones públicas de EOA o contratos inteligentes.

Ejemplo:

```solidity
address owner = 0x742d35Cc6634C0532925a3b844Bc454e4438f44e;
```

💡 Una dirección Ethereum tiene 40 caracteres hexadecimales y un prefijo “0x” que indica que el número que sigue a continuación es un hexadecimal.

🥸 Su valor por defecto es: 0x0000000000000000000000000000000000000000 que es conocido también como 0x0, address(0) o address(0x0).

#### Enumeración (enum):

Ofrece una forma legible de asignar valores significativos a variables. Los enums son útiles para definir estados o categorías específicas. Esto proporciona claridad y facilita la comprensión del código al asignar significado semántico a los valores. Los enums mejoran la legibilidad, la mantenibilidad y evitan errores asociados con el uso de valores numéricos directos.

```solidity
enum Estado = {Pendiente, Aprobado, Rechazado};
```

### Tipos de referencia - Bytes y Strings

Los tipos de referencia son tipos de datos más complejos que almacenan una referencia a una ubicación de memoria. Cuando se asignan a otras variables o se pasan como argumentos a funciones, se pasa la referencia, no una copia de los datos subyacentes.

Cuando se manejan estos tipos en funciones, especialmente cuando se pasan como argumentos o se devuelven desde funciones, es importante tener en cuenta que se manejan por referencia. Esto significa que si modificas un string o un bytes dentro de una función, podrías estar modificando el dato original, no una copia de él.

Los tipos de referencia que veremos por ahora son los bytes y los strings.

#### Bytes (bytes):

Es un tipo de datos que se utiliza para almacenar secuencias de bytes. Solidity proporciona bytes como una forma de almacenar datos binarios arbitrarios.

Pueden ser de dos tipos: estáticos o dinámicos.

**Estáticos (bytesN)**: Donde 'N' puede ser cualquier número entero entre 1 y 32. Estos son tipos de bytes de longitud fija. Por ejemplo, **`bytes1`**, **`bytes2`**, **`bytes32`**, etc. Cada uno de estos almacena una secuencia de bytes de longitud exactamente 'N'. Son útiles cuando sabes la longitud exacta de los datos que necesitas almacenar, como hashes o identificadores.

```solidity
// Definiendo una variable de tipo bytes32
bytes32 public exampleBytes32;
```

**Dinámicos (bytes)**: Este es un tipo de datos dinámico que puede almacenar una secuencia de bytes de longitud variable. Es similar a un array de **`bytes1`**, pero su longitud no está fijada y puede cambiar. Este tipo es más flexible pero también más costoso en términos de gas.

**`bytes`** es especialmente útil para almacenar datos que no tienen una longitud fija predecible, como cadenas de texto arbitrarias o datos binarios.

```solidity
contract Example {
// Definiendo una variable de tipo bytes (dinámica)
bytes public dynamicBytes;

function setBytesValue() public {
    // Asignando un valor a la variable dynamicBytes
    // Puede ser de cualquier longitud
    dynamicBytes = "Hello, Solidity!";
}
}
```

🥸 Su valor por defecto en formato entero es 0, en formato hexadecimal es 0x00.

#### Cadenas (string):

Este tipo está diseñado específicamente para almacenar cadenas de caracteres. Los string en Solidity son dinámicos y pueden cambiar de tamaño. Representa secuencias de caracteres Unicode de longitud variable.

Si la cadena es de menos de 32 bytes, es más eficiente guardarla un formato estático de bytes como **`bytes32`** . Esto simplifica el procesamiento dado que la memoria se asigna de forma anticipada. Por el contrario, \*\*`string`\*\*y **`bytes`** asignan la memoria de forma dinámica dependiendo del tamaño de la cadena.

```solidity

contract StringExample {
//Definiendo una variable de tipo string
    string public exampleString;

// y una función que le asignará un valor de cualquier tamaño al string
    function setString(string memory newString) public {
        exampleString = newString;
    }
}
```

🚨 Los string están codificados en UTF-8. Un carácter en UTF-8 puede tener más de un byte, por ejemplo una vocal con tilde, por ello normalmente el compilador nos mostrará un error al utilizar tildes en una cadena de caracteres asignada a una variable string.

Las operaciones con string pueden ser costosas en términos de gas, especialmente cuando se trata de cadenas grandes. Esto se debe a que el almacenamiento y la manipulación de cadenas requieren un uso intensivo de la memoria.

🚨 Solidity no permite la comparación directa de cadenas con **`==`** . En su lugar, debes comparar sus hashes (por ejemplo, usando keccak256).

💡 Puedes declarar variables string en el almacenamiento (para datos persistentes) o en la memoria (para datos temporales). El uso de la memoria es más económico en términos de gas, pero su alcance está limitado al contexto de la función. Por ello normalmente al definir un parámetro string para una función se utiliza la palabra **`memory`** (ver ejemplo anterior), para especificar que el string se guardará en memoria.

🥸 Su valor por defecto es una cadena vacía.


# Funciones

En Solidity, las funciones son las unidades ejecutables de código. Cada función es un procedimiento o método que puede ser llamado interna o externamente para realizar alguna tarea o calcular y devolver valores.

### Visibilidad de funciones

La visibilidad de una función indica desde donde puede ser llamada. Existen cuatro tipos de visibilidades.

* **Public**: Accesible desde dentro y fuera del contrato. Si no se especifica la visibilidad de una función, por defecto es **`public`**.
* **External**: Solo accesible desde fuera del contrato. No pueden ser llamadas internamente (directamente) dentro del mismo contrato. Sin embargo, pueden ser invocadas internamente usando **`this.functionName()`**.

  Su uso correcto puede llevar a ahorros significativos de gas. Si sabemos que la función sólo será llamada desde fuera, es más eficiente utilizar **`external`**. Son ideales para funciones que deben ser expuestas como parte de la interfaz del contrato (ABI - Application Binary Interface).
* **Internal**: Accesible solo desde dentro del contrato y sus contratos derivados. Son útiles para compartir funcionalidades entre contratos en una jerarquía de herencia.
* **Private**: Solo accesible desde dentro del contrato en el que está definida. Son útiles para lógica interna que no se supone que sea accesible para otros contratos o entidades externas.

A continuación se muestra la forma en que se indica la visibilidad de las funciones en un contrato.

```solidity
contract MyContract {
uint private data;

function publicFunction() public {
    // accesible desde cualquier parte
}

function privateFunction() private {
    // solo accesible dentro de este contrato
}

function internalFunction() internal {
    // accesible dentro de este contrato y contratos heredados
}

function externalFunction() external {
    // solo accesible desde fuera del contrato
}

}
```

🚨 La elección adecuada de la visibilidad de las funciones es crucial para la seguridad y la optimización del gas. Por ejemplo, funciones que no necesitan ser expuestas deben marcarse como **`private`** o **`internal`**.

🚨 La visibilidad incorrecta puede llevar a vulnerabilidades. Por ejemplo, una función que modifica el estado del contrato y está marcada erróneamente como **`public`** puede ser explotada.

<figure><img src="/files/Wcn9hPwiIArsL7G1uHmJ" alt=""><figcaption></figcaption></figure>

### Indicadores de mutabilidad o comportamiento

Sirven para indicar si la función hace cambios en el almacenamiento persistente (storage) del contrato, también indica si la función puede recibir Ether.

Se utilizan tres indicadores en este caso:

* **Pure**: Indica que la función no accede ni modifica el storage del contrato.
* **View**: Similar a **`pure`**, pero puede leer el storage del contrato sin modificarlo.
* **Payable**: Permite que la función reciba Ether junto con una llamada a la función.

### Estructura de una función en Solidity

Estos son los elementos que conforman una función:

* **Palabra Clave function:** Toda función comienza con la palabra clave **`function`**, indicando el inicio de una definición de función.
* **Nombre de la Función:** Seguido de **`function`**, se coloca el nombre de la función. Debe ser único dentro del contrato y seguir las convenciones de nomenclatura de Solidity (generalmente camelCase).
* **Parámetros (Opcionales):** Entre paréntesis, se definen los parámetros de entrada de la función, si los hay. Cada parámetro consta de un tipo de dato seguido de un nombre de variable. Los parámetros están separados por comas.
* **Visibilidad de la Función:** A continuación, se especifica la visibilidad de la función (**`public`**, **`private`**, **`internal`**, **`external`**). Esto determina cómo y desde dónde se puede acceder a la función.
* **Modificadores (Opcionales):** Los modificadores son palabras clave o nombres de modificadores personalizados que afectan el comportamiento de la función: .**`pure`,**`view`**,**`payable` .
* **Valores de Retorno (Opcionales):** Después de los modificadores, se puede especificar el tipo de valor o valores que la función devuelve, encerrado entre la palabra clave **`returns`** y paréntesis. Si se devuelven múltiples valores, se separan por comas.
* **Cuerpo de la Función:** El cuerpo de la función está encerrado entre llaves {}. Aquí es donde se escribe la lógica que se ejecutará cuando se llame a la función.

<div><img src="https://prod-files-secure.s3.us-west-2.amazonaws.com/1c4f016a-3ef4-422a-bee4-76cbe41f54a1/ace209d7-33ec-4757-bdb1-cb1b776ae2d5/Untitled.png" alt=""> <figure><img src="/files/V6qMqIVnPKmzwAT611Xj" alt=""><figcaption></figcaption></figure></div>

### Otros aspectos relevantes de las funciones

Las funciones **`payable`** son una característica única de Solidity que permite a los contratos recibir y manejar Ether, la criptomoneda nativa de Ethereum.

💡 Las funciones consumen gas, que es la unidad de medida para el costo computacional en Ethereum. El gas necesario depende de la complejidad de las operaciones realizadas por la función. Por ello es importante realizar una programación eficiente en consumo de gas.

🥸 Solidity soporta la sobrecarga de funciones (overload), lo que significa que puedes tener múltiples funciones con el mismo nombre pero con diferentes parámetros.

```solidity
contract ExampleContract {
    uint private counter;

    // Una función pública que incrementa el contador
    function incrementCounter() public {
        counter++;
    }

    // Una función de vista que devuelve el valor actual del contador
    function getCounter() public view returns (uint) {
        return counter;
    }
}
```

En este ejemplo, **`incrementCounter`** es una función pública que modifica el estado del contrato, y **`getCounter`** es una función de vista que simplemente devuelve el valor del contador sin modificar el estado del contrato.


# Variables

En esta sección hablaremos de la visibilidad de las variables y de los tipos de variables que existen.

### Visibilidad de variables

Al igual que las funciones, las variables también tienen niveles de visibilidad. La visibilidad de una variable determina cómo y desde dónde puede ser accedida en el contrato y en otros contratos.

Hay tres niveles principales de visibilidad para las variables en Solidity:

* **Public**: Las variables marcadas como **`public`** son automáticamente accesibles desde fuera del contrato. Solidity crea automáticamente una función "getter" para cada variable pública, lo que permite a otros contratos y a las llamadas externas leer su valor. Sin embargo, estas variables no pueden ser modificadas directamente desde fuera del contrato; para cambiar su valor, se requiere una función dentro del contrato que lo haga.

  ```solidity
  uint public publicVariable;
  ```
* **Internal**: Las variables **`internal`** son accesibles dentro del contrato donde están declaradas y en contratos que heredan de él. No son accesibles desde fuera del contrato, a menos que haya una función que proporcione acceso a ellas.

  ```solidity
  uint internal internalVariable;
  ```
* **Private**: Las variables **`private`** son las más restrictivas. Solo pueden ser accedidas y modificadas dentro del contrato que las declara. Incluso los contratos que heredan del contrato no pueden acceder a las variables privadas del contrato padre.

  ```solidity
  uint private privateVariable;
  ```

A diferencia de las funciones, no existe la visibilidad **`external`** para las variables.

Veamos un ejemplo en un contrato:

```solidity
contract MyContract {
    uint public publicVar = 10;
    uint internal internalVar = 20;
    uint private privateVar = 30;

    function accessVariables() public view returns (uint, uint, uint) {
        // Puede acceder a todas las variables ya que están dentro del mismo contrato
        return (publicVar, internalVar, privateVar);
    }
}

contract DerivedContract is MyContract {
    function accessInheritedVariables() public view returns (uint, uint) {
        // Puede acceder a publicVar e internalVar, pero no a privateVar
        return (publicVar, internalVar);
    }
}
```

En este ejemplo, **`publicVar`**, **`internalVar`** y **`privateVar`** tienen diferentes niveles de visibilidad, afectando cómo y dónde pueden ser accedidas. La función **`accessVariables`** dentro de **`MyContract`** puede acceder a todas ellas, mientras que **`accessInheritedVariables`** en **`DerivedContract`** puede acceder solo a las variables **`public`** e **`internal`** heredadas, pero no a la **`private`**.

🚨 La elección de la visibilidad adecuada para las variables es crucial para la seguridad y la funcionalidad correcta del contrato. Las variables **`public`** son útiles para proporcionar transparencia y acceso a datos importantes del contrato, mientras que las variables **`internal`**&#x79; **`private`**&#x73;on importantes para proteger el estado interno del contrato de accesos no autorizados o malintencionados.

### Tipos de variables

Las variables se pueden clasificar en tres categorías principales según su alcance y duración: variables de estado, variables locales y variables globales. Cada tipo tiene características y usos específicos:

* **Variables de Estado:** Son variables que se almacenan permanentemente en la blockchain. Para cambiar una variable de estado es necesario ejecutar una transacción en Ethereum. Son accesibles desde cualquier función dentro del contrato y, dependiendo de su visibilidad, pueden ser accesibles desde otros contratos. Las variables **`public`** generan automáticamente una función “getter”.

```solidity
contract MyContract {
uint public stateVariable;  // Variable de estado
}
```

* **Variables Locales:** Son variables que se usan dentro de funciones y no se guardan en la blockchain de manera permanente. Su vida útil es solo durante la ejecución de la función. Solo son accesibles dentro de la función en la que se declaran. Generalmente se almacenan en la memoria (memory) o en la pila, y no en el almacenamiento permanente del contrato.

```solidity
contract MyContract {
function myFunction() public {
uint localVariable = 5;  // Variable local
}
}
```

* **Variables globales:** Son variables predefinidas en Solidity que proporcionan información sobre la blockchain y las transacciones. No se necesita declararlas, ya que Solidity las proporciona automáticamente. Están disponibles globalmente en cualquier parte del contrato.

  Esta es la lista de variables globales de Ethereum

| Variable global                               | Descripción                                                                                                         |
| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| blockhash(uint blockNumber) returns (bytes32) | Hash del bloque indicado cuando blocknumber es uno de las 256 bloques más recientes, de lo contrario devuelve cero. |
| block.basefee (uint)                          | Tarifa base del bloque actual.                                                                                      |
| block.chainid (uint)                          | Id de la blockchain.                                                                                                |
| block.coinbase (address payable)              | Dirección del minero (ahora validador) del bloque actual.                                                           |
| block.difficulty (uint)                       | Dificultad del bloque actual (EVM < Paris). Actualmente deprecado.                                                  |
| block.gaslimit (uint)                         | Límite de gas del bloque actual                                                                                     |
| block.number (uint)                           | Número de bloque actual                                                                                             |
| block.prevrandao (uint)                       | Número aleatorio proporcionado por la beacon chain (EVM >= Paris)                                                   |
| block.timestamp (uint)                        | Marca de tiempo del bloque actual como segundos a partir de la época unix (00:00:00 UTC del 1 de enero de 1970).    |
| gasleft() returns (uint256)                   | Cantidad de gas restante.                                                                                           |
| msg.data (bytes calldata)                     | calldata de la llamada actual.                                                                                      |
| msg.sender (address)                          | Dirección del remitente de la llamada actual.                                                                       |
| msg.sig (bytes4)                              | Los primeros cuatro bytes de la calldata (identificador de función).                                                |
| msg.value (uint)                              | Cantidad de wei enviados con la llamada.                                                                            |
| tx.gasprice (uint)                            | Precio de gas de la transacción.                                                                                    |
| tx.origin (address)                           | Dirección del remitente original de la transacción.                                                                 |

Para terminar veamos un ejemplo de cómo declarar los diferentes tipos de variables.

```solidity
contract MyContract {
uint public stateVariable;  // Variable de estado
function myFunction() public {
    uint localVariable = 5;  // Variable local
    address sender = msg.sender;  // Variable global
}
}
```

En este ejemplo, **`stateVariable`** es una variable de estado, **`localVariable`** es una variable local, y **`sender`** es una variable global.


# Ejercicio 1

Es hora de poner en práctica los conceptos anteriores creando un contrato que tenga una variable de estado privada de tipo string llamada **`storedInfo`** y dos funciones:

* Una primera función llamada **`setInfo`** con visibilidad externa que se utilizará para cambiar el valor de la variable **`storedInfo`**
* Una segunda función denominada **`getInfo`** de visibilidad externa y que sólo leerá y retornará el contenido de **`storedInfo`**

**Pasos a seguir:**

1. Programe el contrato en Remix,
2. Despliéguelo en una red de prueba de Ethereum como Sepolia,
3. Publique y verifique el contrato utilizando un explorador de bloques
4. Interactúe con el contrato a través del explorador de bloques modificando dos veces el valor de **`setInfo`**

```solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.19;
/// @title Storage string
/// @author Solange Gueiros
contract Register {
string private storedInfo;
/// Store `myInfo`
/// @dev stores the string in the state variable `storedInfo`
/// @param myInfo the new string to store
function setInfo(string memory myInfo) external {
    storedInfo = myInfo;
}

/// Return the stored string
/// @dev retrieves the string of the state variable `storedInfo`
/// @return the stored string
function getInfo() external view returns (string memory) {
    return storedInfo;
}
}
```

> La mayoría de ejercicios de este curso están basados en el GitHub <https://github.com/solangegueiros/register-learn-solidity> de Solange Gueiros, destacada educadora blockchain brasileña, a quien agradecemos por su apoyo continuo a nuestro programa 🙏🏻.


# Operadores

Los operadores de Solidity permiten realizar operaciones matemáticas, lógicas, de comparación, de asignación entre otras. Comprender estos operadores es fundamental para el desarrollo efectivo de contratos inteligentes.

### Aritméticos

| Operación      | Operador | Descripción                                  |
| -------------- | -------- | -------------------------------------------- |
| Suma           | +        | Suma dos operandos                           |
| Resta          | -        | Resta el segundo operando del primero        |
| Multiplicación | \*       | Multiplica los operandos                     |
| División       | /        | Divide el numerador por el denominador       |
| Módulo         | %        | Da como resultado el residuo de una división |
| Incremento     | ++       | Incrementa un valor entero en uno            |
| Decremento     | —        | Reduce un valor entero en uno                |

Ejemplo

```solidity
// SPDX-License-Identifier: MIT 
pragma solidity ^0.8.13; 
contract OperatorDemo {
	 // Inicializar variables
	 uint16 public first = 10;
	 uint16 public second = 30;
	 // Inicializar una variable con el operador de suma
	 uint public addition  = first + second;
	 // Inicializar una variable con el operador de resta
	 uint public subtraction  = second - first; 
		// Inicializar una variable con una multiplicación
	 uint public multiplication  = first * second;
	 // Inicializar una variable con el cociente de una división
	 uint public division = first / second; 
		// Inicializando una variable con módulo
		uint public modulus = first % second; 
		// Inicializar una variable con un valor reducido en uno
		uint public decrement = --second;
		// Inicializar una variable con un valor incrementado en uno
		uint public increment = ++first; 
}
```

### Relacionales

Utiliza estos operadores para comparar dos valores.

| Relación          | Operador | Descripción                                                                                           |
| ----------------- | -------- | ----------------------------------------------------------------------------------------------------- |
| Igual             | ==       | Compara si los valores son iguales. Si lo son, devuelve verdadero (true).                             |
| Diferente         | !=       | Compara si los valores son diferentes. Si lo son, devuelve verdadero (true).                          |
| Mayor que         | >        | Determina si el valor de la izquierda es mayor que el de la derecha. Devuelve true si es así.         |
| Menor que         | <        | Determina si el valor de la izquierda es menor que el de la derecha. Devuelve true si es así.         |
| Mayor o igual que | >=       | Determina si el valor de la izquierda es mayor o igual que el de la derecha. Devuelve true si es así. |
| Menor o igual que | <=       | Determina si el valor de la izquierda es menor o igual que el de la derecha. Devuelve true si es así. |

Ejemplo

```solidity
SPDX-License-Identifier: MIT 
pragma solidity ^0.8.13; 
contract OperatorDemo {
		// Inicializar variables
		 uint16 public first = 10;
		 uint16 public second = 30;
	  // Inicializar una variable (bool) con el resultado de una comparación igual  
			bool public equal = first == second; 
		// Inicializar una variable (bool) con el resultado de una comparación diferente 
		 bool public not_equal = first != second;
		// Inicializar una variable (bool) con el resultado de una comparación mayor
		bool public greater = second > first; 
		// Inicializar una variable (bool) con el resultado de una comparación menor
		bool public less = first < second; 
		// Inicializar una variable (bool) con el resultado de una comparación mayor o igual que
		bool public greaterThanEqualTo = second >= first; 
		/// Inicializar una variable (bool) con el resultado de una comparación menor o igual que
		bool public lessThanEqualTo = first <= second; 
}
```

### Lógicos

Combinan condiciones para determinar un valor lógico resultante.

| Función lógica | Operador | Descripción                                                                                             |
| -------------- | -------- | ------------------------------------------------------------------------------------------------------- |
| AND            | &&       | Devuelve verdadero (true) si ambas condiciones son verdaderas y falso (false) si al menos una es falsa. |
| OR             |          |                                                                                                         |
| NOT            | !        | Devuelve verdadero si la condición no se ha satisfecho.                                                 |

Ejemplo

```solidity
// SPDX-License-Identifier: MIT 
pragma solidity ^0.8.13; 
contract OperatorDemo { 
		// Inicializar variables
		 bool public first = true; 
		 bool public second = false; 
		// Inicializar una variable con el resultado de un AND
		 bool public and = first&&second; 
		// Inicializar una variable con el resultado de un OR
		 bool public or = first||second;
		// Inicializar una variable con el resultado de un NOT
		 bool public not = !second; 
}
```

### De asignación

Permiten asignar un valor a una variable. Al lado derecho del operador se ubica un valor y al lado izquierdo una variable.

| Tipo                      | Operador | Descripción                                                                                                     |
| ------------------------- | -------- | --------------------------------------------------------------------------------------------------------------- |
| Asignación simple         | =        | Se asigna el valor de la derecha a la variable a la izquierda del operador.                                     |
| Asignación suma           | +=       | Añade el operando de la derecha al de la izquierda, luego asigna el resultado al operando de la izquierda.      |
| Asignación resta          | -=       | Resta el operando de la derecha al de la izquierda, luego asigna el resultado al operando de la izquierda.      |
| Asignación multiplicación | \*=      | Multiplica ambos operandos, luego asigna el resultado al operando de la izquierda.                              |
| Asignación división       | /=       | Divide el operando de la izquierda por el de la derecha, luego asigna el resultado al operando de la izquierda. |
| Asignación módulo         | %=       | Divide el operando de la izquierda por el de la derecha, luego asigna el residuo al operando de la izquierda.   |

Ejemplo

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
 contract OperatorDemo {
		// Inicializa variable de estado 
		uint  public first = 10; 
		//Asignación simple
		 function simpleAssignment() public {
		 first = 20; 
			} 
		//Asignación suma
		 function addAssignment() public {
			first += 10; 
			} 
		//Asignación resta
		function substractAssignment() public {
		 first -= 10; 
			}
		 //Asignación multiplicación
		 function multiplyAssignment() public {
		 first *= 5; 
			} 
		 //Asignación división
		function divideAssignment() public {
		 first /= 3;
			} 
		//Asignación módulo
		function modulusAssignment() public {
		 first %= 3; 
			} 
}
```

Sugerencia: Lleva este ejemplo a Remix y fíjate qué pasa con el valor de \*\*`first`\*\*cuando ejecutas cada una de las funciones.

### Bitwise

Son operadores utilizados para realizar operaciones a nivel de bit.

| Tipo                     | Operador | Descripción                                                                                                            |
| ------------------------ | -------- | ---------------------------------------------------------------------------------------------------------------------- |
| Bitwise AND              | &        | Se aplica un operador lógico AND a los operandos enteros a nivel de cada bit.                                          |
| Bitwise OR               |          |                                                                                                                        |
| Bitwise XOR              | ^        | Se aplica un operador lógico XOR a los operandos enteros a nivel de cada bit.                                          |
| Bitwise NOT              | \~       | Se aplica un operador lógico NOT al operando a nivel de cada bit.                                                      |
| Desplazamiento izquierdo | <<       | Los bits del primer operando se desplazan hacia la izquierda un número de posiciones indicada por el segundo operando. |
| Desplazamiento derecho   | >>       | Los bits del primer operando se desplazan hacia la derecha un número de posiciones indicada por el segundo operando.   |

Para entender estas funciones veamos unos ejemplos de cómo se hacen operaciones a nivel de bit o binario.

Para expresar un número en binario sólo utilizamos 0 y 1. Cada posición del número binario representa una potencia de 2. Así un 1 en la primera posición de la derecha es 2^0 = 1, en la segunda 2^1 = 2, en la tercera 2^2 = 4, en la cuarta 2^3 = 8, y así sucesivamente.

Supongamos que tenemos dos valores x e y:

x = 12, y = 5, que debemos expresar en binario para hacer operaciones bitwise.

x = 12 = 8 + 4 + 0 + 0 = 1100 (binario)

y = 5 = 0 + 4 + 0 + 1 = 0101 (binario)

Si realizamos la operación AND a nivel de bit tendríamos lo siguiente:

x & y = 0100 = 0 + 4 + 0 + 0 = 4

Si realizamos la operación OR a nivel de bit:

x | y = 1101 = 8 + 4 + 0 + 1 = 13

Si realizamos la operación XOR a nivel de bit, que es uno cuando uno de los operandos es 1:

x ^ y = 1001 = 8 + 0 + 0 + 1 = 9

Si realizamos la operación NOT a x a nivel de bit, lo que implica cambiar los ceros por uno y viceversa:

NOT x = 0011 = 0 + 0 + 2 + 1 = 3

Si desplazamos x hacia la izquierda dos posiciones:

x = 12 = 1100 (binario)

x << 2 = 110000 = 48

Si desplazamos x hacia la derecha dos posiciones:

x = 12 = 1100 (binario)

x >> 2 = 0011 = 3

Ejemplo. Despliega este contrato y prueba si los valores para x = 12, y = 5 coinciden con los resultados anteriores. Prueba con otros valores también.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract BitwiseOps {
    
    function and(uint x, uint y) external pure returns (uint) {
        return x & y;
    }
    function or(uint x, uint y) external pure returns (uint) {
        return x | y;
    }

    function xor(uint x, uint y) external pure returns (uint) {
        return x ^ y;
    }

    function not(uint8 x) external pure returns (uint8) {
        return ~x;
    }

    function shiftLeft(uint x, uint bits) external pure returns (uint) {
        return x << bits;
    }

    function shiftRight(uint x, uint bits) external pure returns (uint) {
        return x >> bits;
    }
}
```

### Condicional

Es un operador ternario que evalúa inicialmente una expresión y ejecuta una acción si es verdadera u otra si es falsa.

El formato del operador ternario es el siguiente:

```solidity
<condición> ? <si es verdadera> : <si es falsa>
```

Ejemplo

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract Conditional {

    function ternary(uint _x) public pure returns (uint) {
        // Si _x es menor que 10, que la función devuelva 1, sino que devuelva 2
        return _x < 10 ? 1 : 2;
    }
}
```


# Ejercicio 2

En este ejercicio haremos unas modificaciones al contrato anterior para poder contar el número de veces que modificamos la variable **`storedInfo` .** Para ello utilizaremos un contador denominado **`countChanges`**&#x71;ue será una variable de tipo uint que debe ser visible de forma pública.

**Pasos a seguir:**

1. Programe el contrato en Remix,
2. Despliéguelo en una red de prueba de Ethereum como Sepolia,
3. Publique y verifique el contrato utilizando un explorador de bloques
4. Interactúe con el contrato a través del explorador de bloques modificando tres veces el valor de **`setInfo`** y verificando que así lo hizo con el contador **`countChanges`**

```solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.19;
 
/// @title Concepts: variable uint with public visibility
/// @author Solange Gueiros
contract ChangeCounter {
    string private storedInfo;
    uint public countChanges = 0;

    /**
    * Store `myInfo`
    * Increase the counter which manage how many times storedInfo is updated
    * @dev stores the string in the state variable `storedInfo` and update the counter
    * @param myInfo the new string to store
    */
    function setInfo(string memory myInfo) external {
        storedInfo = myInfo;
        countChanges++;
    }

    /**
    * Return the stored string
    * @dev retrieves the string of the state variable `storedInfo`
    * @return the stored string
    */
    function getInfo() external view returns (string memory ) {
        return storedInfo;
    }
}
```


# Constructor

El constructor es una función especial que se ejecuta únicamente cuando se despliega un contrato inteligente en Ethereum. Los constructores son utilizados para inicializar las variables de estado del contrato, establecer configuraciones iniciales o realizar cualquier configuración preliminar necesaria.

En versiones antiguas de Solidity, el constructor tenía el mismo nombre que el contrato. Sin embargo, en versiones más recientes (0.4.22 y superiores), se utiliza la palabra clave **`constructor`** para definirlo, lo que mejora la claridad y reduce la posibilidad de errores.

El constructor se ejecuta solo una vez, en el momento en que el contrato es desplegado en la blockchain, y no puede ser llamado nuevamente una vez que el contrato está desplegado.

**Visibilidad:** Los constructores pueden ser declarados como public o internal. Un constructor public es aquel que cualquiera puede desplegar, mientras que un constructor internal solo puede ser desplegado a través de mecanismos internos, como por ejemplo, a través de otro contrato.

**Parámetros:** Al igual que otras funciones, los constructores pueden tener parámetros, lo que permite personalizar la configuración inicial del contrato durante el despliegue.

**Ausencia de Valor de Retorno:** Los constructores no tienen un valor de retorno.

Ejemplo de un Constructor en Solidity:

```solidity
pragma solidity ^0.8.0;

contract MyContract {
uint256 public myNumber;
address public owner;
constructor(uint256 _myNumber) {
    myNumber = _myNumber;
    owner = msg.sender;
}
}
```

En este ejemplo, el constructor del contrato MyContract establece el valor inicial de la variable myNumber y asigna el propietario del contrato a la dirección que lo despliega. Estas operaciones se realizan una sola vez, en el momento del despliegue del contrato.


# Ejercicio 3

En este ejercicio utilizaremos un constructor para inicializar el valor de la variable **`storedInfo` .** Para ello utilizaremos un contador denominado **`countChanges`**&#x71;ue será una variable de tipo uint que debe ser visible de forma pública.

**Pasos a seguir:**

1. Programe el contrato en Remix,
2. Despliéguelo en una red de prueba de Ethereum como Sepolia,
3. Publique y verifique el contrato utilizando un explorador de bloques
4. Interactúe con el contrato a través del explorador de bloques y verifique que el constructor inicializó la variable **`storedInfo`.**

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

/// @title Concepts: Constructor
/// @author Solange Gueiros
contract FirstConstructor {
    string private storedInfo;
    uint public countChanges = 0;

    /**
    * Usamos el constructor para inicializar la variable stored Info
    */
    constructor() {
        storedInfo = "Hello world";
        // Considera el contador esta inicialización?
        // A revisar en clase
    }
    

    function setInfo(string memory myInfo) external {
        storedInfo = myInfo;
        countChanges++;
    }
    
    function getInfo() external view returns (string memory) {
        return storedInfo;
    }  
}
```


# Convenciones de nomenclatura

A continuación, presentamos algunas de las convenciones más comunes utilizadas en la comunidad de Solidity:

1. **Contratos**: Los nombres de los contratos suelen estar en UpperCamelCase (también conocido como PascalCase). Cada palabra en el nombre del contrato comienza con una letra mayúscula sin espacios. Ejemplo: **`MySmartContract`**.
2. **Funciones**: Las funciones suelen nombrarse usando lowerCamelCase. La primera palabra comienza con una minúscula y las siguientes palabras comienzan con mayúsculas. Ejemplo: **`myFunction`**.
3. **Variables de Estado**: Las variables de estado también suelen usar lowerCamelCase. Estas son las variables que se almacenan permanentemente en el almacenamiento del contrato. Ejemplo: **`myVariable`**.
4. **Variables Locales y Parámetros**: Al igual que las variables de estado, las variables locales y los parámetros de las funciones también se nombran usando lowerCamelCase. Ejemplo: **`localVariable`**, **`functionParameter`**.
5. **Constantes**: Las constantes se nombran con todas las letras en mayúsculas y palabras separadas por guiones bajos (snake\_case en mayúsculas). Ejemplo: **`MAX_COUNT`**, **`TOTAL_SUPPLY`**.
6. **Enums**: Los nombres de los enumerados (enums) suelen seguir UpperCamelCase, al igual que los nombres de los contratos. Ejemplo: **`TokenState`**.
7. **Eventos**: Los eventos generalmente siguen UpperCamelCase. Ejemplo: **`TransferCompleted`**.
8. **Modificadores**: Para los modificadores se utiliza lowerCamelCase, similar a las funciones. Ejemplo: **`onlyOwner`**.
9. **Structs**: Los nombres de structs generalmente se escriben en UpperCamelCase, siguiendo la misma convención que los contratos y enums. Ejemplo: **`PlayerInfo`**.

Estas convenciones no son obligatorias pero son ampliamente adoptadas en la comunidad de desarrollo de Solidity. Seguir estas prácticas estándar ayuda a mantener el código fuente de los contratos inteligentes organizado y fácil de entender para otros desarrolladores que puedan trabajar con tu código o revisarlo. Además, estas convenciones ayudan a distinguir rápidamente entre diferentes tipos de entidades en el código, como contratos, funciones y variables.


# Tipos de almacenamiento para variables

En Solidity, hay tres tipos de ubicaciones de almacenamiento para variables, cada una con sus propias características y usos. Estas ubicaciones son: almacenamiento (storage), memoria (memory) y área de calldata. Comprender la diferencia entre ellas es crucial para un desarrollo eficiente y seguro de contratos inteligentes en Ethereum.

1. **Storage (Almacenamiento)**:
   * **Descripción**: Las variables de almacenamiento son persistentes y se guardan en la blockchain entre transacciones. Son como el "disco duro" de un contrato inteligente.
   * **Uso**: Se utiliza principalmente para variables de estado, es decir, variables que necesitan persistir entre llamadas a funciones y transacciones.
   * **Costo**: Es la forma más costosa de almacenamiento en términos de gas, especialmente en escrituras y modificaciones.
   * **Acceso**: Por defecto, las variables de estado son de almacenamiento.
2. **Memory (Memoria)**:
   * **Descripción**: Las variables en memoria son temporales y solo existen mientras una función está siendo ejecutada. Son borradas entre las llamadas a funciones externas y no se almacenan en la blockchain.
   * **Uso**: Útil para almacenar datos temporales durante la ejecución de una función. Por ejemplo, variables dentro de funciones o argumentos pasados a funciones internas.
   * **Costo**: Mucho más barato que el almacenamiento en términos de gas. No tiene un costo de gas permanente ya que los datos se eliminan al final de la ejecución de la función.
   * **Acceso**: Debes especificar explícitamente **`memory`** para variables de función y parámetros (excepto para tipos de referencia internos como mappings).
3. **Calldata**:
   * **Descripción**: Calldata es una ubicación de almacenamiento no modificable y temporal que contiene los datos de entrada de las transacciones y llamadas a funciones. Es similar a **`memory`**, pero solo se utiliza para datos de entrada de funciones externas.
   * **Uso**: Se utiliza para argumentos de función en funciones **`external`**. Es la forma más eficiente de pasar datos en términos de gas, especialmente para arrays y estructuras complejas.
   * **Costo**: Es generalmente más barato en términos de gas que **`memory`** porque los datos en **`calldata`** no se copian sino que se leen directamente de la transacción o llamada.
   * **Acceso**: Es de solo lectura y no puede modificarse.

<figure><img src="/files/QPmg9Ou7G0T980ZjvwCL" alt=""><figcaption></figcaption></figure>

**Ejemplo en Código**:

```solidity
pragma solidity ^0.8.0;

contract StorageTypes {
    // Storage: persistente entre transacciones
    uint public storageVar = 10;

    function exampleFunction(uint[] calldata calldataVar) external {
        // Calldata: datos de entrada en una función external
        uint localVar = calldataVar[0];

        // Memory: temporal durante la ejecución de la función
        uint[] memory memoryArray = new uint[](5);
    }
}
```

En este ejemplo, **`storageVar`** es una variable de almacenamiento, **`calldataVar`** es una variable de calldata (ya que es un argumento en una función **`external`**), y **`memoryArray`** es una variable de memoria.

La comprensión de estas tres ubicaciones de almacenamiento es fundamental para optimizar el uso de gas en contratos inteligentes y evitar errores comunes en Solidity.


# Estructuras de Control

### Condicionales

Las estructuras condicionales permiten que el contrato inteligente realice acciones dependiendo del cumplimiento de condiciones específicas.

* **if-else**: Es la estructura condicional más básica, utilizada para ejecutar bloques de código si una condición es verdadera o falsa.

```solidity

if (condicion) {
    // Código a ejecutar si la condición es verdadera
} else {
    // Código a ejecutar si la condición es falsa
}
```

### Bucles

Los bucles permiten ejecutar un bloque de código repetidamente bajo ciertas condiciones.

* **for**: Utilizado para repetir un bloque de código un número determinado de veces.

```solidity
for (inicialización; condición; incremento) {
    // Código a ejecutar en cada iteración
}
```

* **while**: Ejecuta un bloque de código mientras una condición específica sea verdadera.

```solidity
while (condición) {
    // Código a ejecutar mientras la condición sea verdadera
}
```

* **do-while**: Similar al bucle while, pero garantiza que el bloque de código se ejecute al menos una vez.

```solidity
do {
    // Código a ejecutar
} while (condición);
```

### Control de Flujo

Solidity también incluye declaraciones de control de flujo que afectan cómo se ejecutan los bucles y condicionales.

* **break**: Termina la ejecución del bucle más interno. Se utiliza para salir de un bucle cuando se cumple una condición específica.

  ```solidity
  while (condición) {
      // Código a ejecutar mientras la condición sea verdadera
  		if (expresión){
  			// Detiene la ejecución del bucle si la expresión es verdadera
  			break;
  			}
   }
  ```
* **continue**: Salta el resto del código en el bucle actual e inicia la siguiente iteración del bucle.

  ```solidity
  while (condición) {
      // Código a ejecutar mientras la condición sea verdadera
  		if (expresión){
  			// Sale de esta iteración del bucle si la expresión es verdadera
  			continue;
  			}
   }
  ```

### Manejo de Errores

Existen también estructuras para manejar errores y revertir transacciones si es necesario.

* **require**: Se utiliza para validar condiciones y revertir la transacción si la condición no se cumple, opcionalmente con un mensaje de error.

```solidity
require(condición, "Mensaje de error");
```

* **revert**: Revierte la transacción. Puede ser utilizado con un mensaje de error específico.

```solidity
revert("Mensaje de error");
```

* **assert**: Utilizado para realizar comprobaciones internas y de invariantes. Si la condición evaluada es falsa, la transacción se revierte y consume todo el gas disponible, indicando un error grave.

```solidity
assert(condición);
```


# Ejercicio 4

En este ejercicio haremos una variante respecto al ejercicio 3 pues verificaremos si quien envía la transacción para asignar una valor a la variable **`storedInfo`** es el dueño (owner) del contrato. Solo el dueño puede modificar el valor de **`storedInfo` .**

**Pasos a seguir:**

1. Programe el contrato en Remix,
2. Despliéguelo en una red de prueba de Ethereum como Sepolia,
3. Publique y verifique el contrato utilizando un explorador de bloques
4. Interactúe con el contrato a través del explorador de bloques y verifique si puede modificar el valor de la variable **`storedInfo`.**
5. Intente modificar el valor de **`storedInfo`** conectándose con otra wallet.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

/// @title Concepts: Type address, msg.sender, owner, if
/// @author Solange Gueiros
contract Owner {
    string private storedInfo;
    address public owner;
		// El constructor define como dueño a quien despliega el contrato.
    constructor() {
        owner = msg.sender;
    }

    /**
    * La función setInfo verifica si quien envía la transacción
    * es el dueño del contrato.
    * Si es así, modifica el valor de la variable. Sino no hace nada.
    */
    function setInfo(string memory myInfo) external {
        if (msg.sender == owner) {
            storedInfo = myInfo;
        }
    }

    function getInfo() external view returns (string memory) {
        return storedInfo;
    }
}
```

¿Cómo modificaría la función **`setInfo`** en el contrato anterior para utilizar la estructura de control **`require`** en lugar de **`if`** ?

Respuesta:

```solidity
 	function setInfo(string memory myInfo) external {
        require(msg.sender == owner, "Only owner");
        storedInfo = myInfo;
    }
```


# Modificadores

Los modificadores en Solidity son una característica poderosa que permite a los desarrolladores cambiar o ampliar la semántica de las funciones en contratos inteligentes. Proporcionan una forma reutilizable y flexible de controlar el comportamiento de las funciones. Los modificadores pueden ser utilizados para añadir requisitos previos a la ejecución de una función o para modificar su comportamiento de alguna manera.

**Propósito de los Modificadores**

El principal propósito de los modificadores es añadir una lógica común a varias funciones de un contrato inteligente, lo que ayuda a reducir la redundancia del código y mejora su mantenibilidad. Por ejemplo, se pueden usar para:

* Restringir el acceso a funciones específicas solo a ciertos usuarios (como el propietario del contrato).
* Validar entradas a las funciones.
* Guardar condiciones o estados específicos antes y después de la ejecución de una función.

**Cómo Funcionan los Modificadores**

Un modificador es definido de manera similar a una función, pero se utiliza para envolver la lógica de otra función. Dentro del cuerpo del modificador, el código especial **`_;`** indica dónde se debe insertar el código de la función modificada. Cuando una función se llama, primero se ejecuta el código del modificador, hasta que se encuentra el **`_;`**, momento en el cual se ejecuta el código de la función, y después, si es necesario, el código restante del modificador.

**Ejemplo Básico**

Consideremos un modificador simple que restringe el acceso a una función solo al dueño (owner) del contrato:

```solidity
pragma solidity ^0.8.0;

contract MyContract {
    address public owner;

    constructor() {
        owner = msg.sender; // Establece el dueño del contrato al ser desplegado
    }

    // Definición del modificador
    modifier onlyOwner() {
        require(msg.sender == owner, "Solo el propietario puede ejecutar esta función.");
        _; // Continúa con la ejecución de la función modificada
    }

    // Uso del modificador en una función
    function myRestrictedFunction() public onlyOwner {
        // Lógica de la función aquí
    }
}
```

**Características Importantes**

* **Composición**: Los modificadores pueden ser aplicados a una función en secuencia, permitiendo componer diferentes comportamientos y restricciones de manera flexible.
* **Parámetros**: Al igual que las funciones, los modificadores pueden recibir parámetros. Esto permite crear modificadores más dinámicos y reutilizables que pueden operar basándose en argumentos pasados durante la llamada a la función.
* **Visibilidad**: Los modificadores pueden ser declarados como **`public`** o **`internal`**. Esto afecta cómo pueden ser utilizados dentro del mismo contrato o en contratos derivados.


# Eventos

Los eventos en Solidity son una de las características clave que permiten la comunicación entre los contratos inteligentes y las interfaces de usuario o los servidores backend en la blockchain de Ethereum. Proporcionan una manera eficiente de emitir registros sobre la ejecución de los contratos que pueden ser escuchados y capturados por aplicaciones externas, facilitando así una reacción en tiempo real a las acciones que ocurren dentro de la blockchain.

**Propósito de los Eventos**

Los eventos sirven principalmente para dos propósitos en el desarrollo de aplicaciones descentralizadas (DApps):

1. **Registrar y Auditar**: Permiten registrar acciones específicas que ocurren en un contrato inteligente, como transferencias de tokens, cambios de estado, o cualquier otro evento significativo. Estos registros son almacenados en el log de transacciones de la blockchain, proporcionando una forma transparente y permanente de seguimiento.
2. **Interfaz de Comunicación**: Facilitan la comunicación entre los contratos inteligentes y las interfaces de usuario. Por ejemplo, una aplicación puede escuchar eventos específicos emitidos por un contrato y actualizar la interfaz de usuario en consecuencia, sin necesidad de realizar costosas llamadas de lectura constantemente.

**Cómo Funcionan los Eventos**

Para utilizar eventos en Solidity, primero se deben definir dentro del contrato inteligente, especificando los tipos de datos que se van a emitir. Luego, dentro de las funciones del contrato, se pueden emitir estos eventos con los datos específicos que se desean registrar o comunicar.

**Ejemplo Básico de Uso de Eventos**

A continuación, se presenta un ejemplo simple de cómo definir y emitir un evento en Solidity:

```solidity
pragma solidity ^0.8.0;

contract MyContract {
    // Definición del evento
    event MyEvent(address indexed sender, uint256 value);

    function triggerEvent() public {
        // Lógica de la función aquí...

        // Emitir el evento con los valores específicos
        emit MyEvent(msg.sender, 100);
    }
}
```

**Características Importantes de los Eventos**

* **Indexed Parameters**: Los parámetros de un evento pueden ser marcados como **`indexed`**, lo que permite que estos sean buscables dentro del log de transacciones. Hasta tres parámetros por evento pueden ser indexados, lo que facilita la filtración de eventos por estos campos específicos.
* **Eficiencia de Gas**: Emitir un evento es considerablemente más eficiente en términos de gas que almacenar valores directamente en el estado del contrato. Esto los hace ideales para registrar información sin afectar significativamente los costos de transacción.
* **Lectura Externa**: Aunque los eventos se registran en la blockchain, no pueden ser accedidos desde dentro de los contratos. Su propósito es ser leídos por aplicaciones externas.


# Ejercicio 5

Incorporaremos en nuestro contrato un evento que informará cuando se ha cambiado el valor de la variable **`storedInfo`** indicando cuál era el valor original y cuál es el nuevo valor.

**Pasos a seguir:**

1. Programe el contrato en Remix,
2. Despliéguelo en una red de prueba de Ethereum como Sepolia,
3. Publique y verifique el contrato utilizando un explorador de bloques
4. Interactúe con el contrato a través del explorador de bloques modificando el valor de **`storedInfo` .**
5. Verifique en las transacciones del contrato en el explorador de bloques que se ha ejecutado la función **`setInfo`** y en la pestaña de eventos verifique que se ha generado un evento.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

/// @title Concepts: event
/// @author Solange Gueiros
contract EventEmitter {
    string private storedInfo;
		// Define un evento para informar un cambio en storedInfo
    event InfoChange(string oldInfo, string newInfo);

    // Emite el evento cuando el valor de storedInfo va a ser cambiado
    
    function setInfo(string memory myInfo) external {
        emit InfoChange (storedInfo, myInfo);
        storedInfo = myInfo;
    } 

    function getInfo() external view returns (string memory) {
        return storedInfo;
    }   
}
```


# Tipos de Referencia


# Arrays

Son una manera de almacenar datos de un mismo tipo. Pueden ser de dos tipos: estáticos y dinámicos. Los arrays estáticos tienen un tamaño fijo que se define en el momento de su declaración, mientras que los arrays dinámicos pueden cambiar de tamaño durante la ejecución del contrato.

**Declaración de Arrays**

Para declarar un array, especificas el tipo de los elementos que contendrá, seguido de corchetes **`[]`**. Si los corchetes están vacíos, el array es dinámico; si contienen un número, es estático y el número indica su tamaño.

```solidity
pragma solidity ^0.8.0;

contract EjemploArrays {
    // Array estático con espacio para 5 enteros
    uint[5] public arrayEstatico;

    // Array dinámico que puede contener un número arbitrario de enteros
    uint[] public arrayDinamico;
}
```

**Operaciones Básicas**

Acceso a Elementos

Puedes acceder a elementos de un array utilizando su índice, empezando por el 0.

```solidity
uint valor = arrayEstatico[0]; // Accede al primer elemento del array estático
```

Cambiar Tamaño (Solo Arrays Dinámicos)

Para cambiar el tamaño de un array dinámico, puedes usar la función **`push()`** para agregar un nuevo elemento al final del array o **`pop()`** para eliminar el último elemento.

```solidity
arrayDinamico.push(10); // Añade el valor 10 al final del array dinámico
arrayDinamico.pop(); // Elimina el último elemento del array dinámico
```

Longitud del Array

La propiedad **`length`** te permite obtener la longitud de un array, ya sea estático o dinámico.

```solidity
uint longitud = arrayDinamico.length; // Obtiene la longitud del array dinámico
```

A continuación un ejemplo de un contrato que inicializa un array con valores enteros utilizando \*\*`push()`\*\*y que puede también eliminar elementos del array utilizando **`pop()`** .

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13; 

contract ArraysDS { 
		// Se declara un array dinámico de tipo entero 
		int[] numbers; 
		// función para añadir valores a un array  
		function store() public returns (int[] memory){ 
			numbers.push(5);
			numbers.push(10);
			numbers.push(20);
			return  numbers; 
		}  

		//función para retirar el ultimo valor del array
		function deleteLastElement() public returns(int[] memory){
		  numbers.pop(); 
			return numbers;  
		} 
} 
```

**Consideraciones de Uso**

* **Costo de Gas:** Las operaciones que cambian el tamaño de un array dinámico, como **`push`** y **`pop`**, incurren en costos de gas que pueden variar según la complejidad de la operación.
* **Arrays Multidimensionales:** Solidity también soporta arrays multidimensionales, pero ten en cuenta que el manejo de estos puede aumentar rápidamente los costos de gas debido a la complejidad adicional.
* **Limitaciones en Arrays Estáticos:** Aunque los arrays estáticos pueden parecer limitantes, su tamaño fijo puede ser beneficioso para optimizar el uso de gas en ciertos casos, ya que el coste de las operaciones es predecible y no varía.


# Ejercicio 6

En este ejercicio utilizaremos el concepto de arrays dinámicos para almacenar una lista de datos que iremos almacenando. También veremos cómo modificar los valores de posiciones específicas del array y cómo recuperar la lista completa de valores del array.

**Pasos a seguir:**

1. Programe el contrato en Remix,
2. Despliéguelo en una red de prueba de Ethereum como Sepolia,
3. Publique y verifique el contrato utilizando un explorador de bloques
4. Interactúe con el contrato a través del explorador de bloques añadiendo al menos cuatro valores al array **`storedInfos` .**
5. Modifique posiciones específicas de **`storedInfos`** usando la función **`updateInfo` .**
6. Lea el valor de posiciones espefícas de **`storedInfos`** usando la función **`getOneInfo` .**

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

/// @title Concepts: array = list of infos stored
/// @author Solange Gueiros
contract FirsArray {
    string[] private storedInfos;

    /**
    * Graba nuevos valores en el array usando push()
    * index devuelve la posición en la que se grabó el valor 
    */
    function addInfo(string memory myInfo) external returns (uint index) {
        storedInfos.push(myInfo);
        index = storedInfos.length -1;
    }

    /**
    * Modifica el valor de la posición index del array con un nuevo valor
    * Verifica que la posición sea válida, sino devuelve un error
    */
    function updateInfo(uint index, string memory newInfo) external {
        require (index < storedInfos.length, "invalid index");
        storedInfos[index] = newInfo;
    }

    /**
    * Devuelve el valor almacenado en la posición index del array
    * Verifica que la posición sea válida, sino devuelve un error
    */
    function getOneInfo(uint index) external view returns (string memory) {
        require (index < storedInfos.length, "invalid index");
        return storedInfos[index];
    }

    // Devuelve todos los valores contenidos en el array
    function listAllInfo() external view returns (string[] memory) {
        return storedInfos;
    } 
}
```


# Mappings

Los mappings en Solidity son una estructura de datos de tipo “key - value” (clave-valor) que proporciona una forma eficiente y flexible de almacenar y acceder a datos dentro de contratos inteligentes. Son comparables a los diccionarios o mapas en otros lenguajes de programación, donde cada clave única se asocia con un valor. Los mappings son particularmente útiles en el desarrollo de aplicaciones descentralizadas (DApps) para gestionar conjuntos de datos como balances de cuentas, relaciones entre entidades, y otros tipos de datos estructurados.

**Características de los Mappings**

* **Claves Únicas**: Cada clave en un mapping debe ser única y se utiliza para acceder a un valor específico. Las claves pueden ser de cualquier tipo primitivo como **`address`**, **`uint`**, **`bytes`**, etc. No se permiten tipos de datos complejos como arrays o structs como claves.
* **Valores Dinámicos**: Los valores almacenados en un mapping pueden ser de cualquier tipo, incluyendo tipos primitivos, arrays, structs, e incluso otros mappings.
* **Inicialización por Defecto**: Los mappings no tienen un tamaño fijo y no se puede obtener su longitud. Todos los posibles valores están virtualmente presentes y se inicializan por defecto al valor por defecto de su tipo (por ejemplo, **`0`** para **`uint`**, **`false`** para **`bool`**, etc.).
* **Privacidad**: No se puede iterar sobre los mappings ni obtener una lista de sus claves directamente en Solidity. Cada acceso a un valor debe hacerse mediante una clave conocida.

**Declaración**

Un mapping se declara especificando el tipo de las claves y el tipo de los valores. La sintaxis general es:

```solidity
mapping(tipoClave => tipoValor) public nombreMapping;
```

**Ejemplo Básico**

```solidity
pragma solidity ^0.8.0;

contract MyContract {
    // Declaración de un mapping que asocia direcciones con balances
    mapping(address => uint) public balances;

    // Actualiza el balance de la address que solicita la actualización
    function updateBalance(uint newBalance) public {
        balances[msg.sender] = newBalance;
    }

    // Lee el balance de una dirección
    function getBalance(address user) public view returns (uint) {
        return balances[user];
    }
}
```

En el ejemplo anterior creamos un mapping denominado **`balances`** conformado por pares donde la clave es un address y el valor es un entero sin signo.

Para actualizar el balance de una address específica utilizamos la expresión

**Uso común de Mappings en contratos inteligentes**

* **Balances de Tokens**: Para llevar un registro de los balances de criptomonedas o tokens ERC-20 de cada dirección.
* **Permisos y Roles**: Para gestionar permisos o roles específicos asignados a diferentes direcciones.
* **Relaciones entre Entidades**: Para mapear relaciones uno-a-uno o uno-a-muchos entre diferentes entidades, como usuarios y sus activos o tareas.

**Consideraciones de Seguridad**

Al trabajar con mappings, es crucial gestionar correctamente el acceso y las actualizaciones a los datos para prevenir vulnerabilidades de seguridad. Por ejemplo, cuando se actualiza un valor en un mapping, asegúrate de que solo las entidades autorizadas puedan realizar dicha actualización, utilizando modificadores de acceso o verificaciones de permisos adecuadas.


# Ejercicio 7

Crearemos una whitelist para que solo las personas que estén en ella puedan modificar el valor de la variable **`storedInfo` .** El dueño del contrato será el único que podrá añadir o retirar personas de la whitelist.

**Pasos a seguir:**

1. Programe el contrato en Remix,
2. Despliegue el contrato en Remix.
3. Añada addresses en la whitelist utilizando las que Remix le proporciona.
4. Verifique que solo las personas incluidas en la whitelist pueden modificar el valor de **`storedInfo` .**

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

/// @title Concepts: mapping and access control: whiteList
/// @author Solange Gueiros
contract Whitelist {
    string private storedInfo;
    address public owner;
    mapping (address => bool) public whiteList;
    // El constructor inicializa al owner y lo incluye en la whitelist
    constructor() {
        owner = msg.sender;
        whiteList[msg.sender] = true;
        storedInfo = "Hello world";
    }
    
    modifier onlyOwner {
        require(msg.sender == owner,"Only owner");
        _;
    }
    // Se requiere que quien envíe la transacción esté en la whitelist
    modifier onlyWhitelist {
        require(whiteList[msg.sender] == true, "Only whitelist");
        _;
    }

    // setinfo solo accesible para quien esté en la whitelist
    function setInfo(string memory myInfo) external onlyWhitelist {
        storedInfo = myInfo;
    }

    // addMember sólo accesible para el dueño del contrato
    function addMember (address member) external onlyOwner {
        whiteList[member] = true;
    }

     // delMember sólo accesible para el dueño del contrato   
    function delMember (address member) external onlyOwner {
        whiteList[member] = false;
    }

    function getInfo() external view returns (string memory) {
        return storedInfo;
    }
}
```


# Structs

Son estructuras de datos complejas que permiten agrupar varias variables, posiblemente de diferentes tipos, bajo una única entidad. Esto los hace particularmente útiles para modelar objetos o conceptos con múltiples atributos dentro de los contratos inteligentes. Al igual que en otros lenguajes de programación como C, C++, o JavaScript, los **`structs`** ofrecen una forma de organizar datos que están lógicamente relacionados en una estructura comprensible y manejable.

**Características de los Structs**

* **Tipos de Datos Compuestos**: Permiten combinar varios tipos de datos, incluidos otros **`structs`**, **`arrays`**, y **`mappings`**, en una sola unidad.
* **Personalizables**: Los desarrolladores pueden definir **`structs`** según las necesidades específicas de su aplicación, eligiendo los tipos de datos y nombres de variables que mejor se ajusten a su caso de uso.
* **Flexibles**: Se pueden utilizar dentro de **`arrays`** y **`mappings`** para crear estructuras de datos complejas y dinámicas.

**Declaración**

Para declarar un **`struct`**, se utiliza la palabra clave **`struct`**, seguida del nombre de la estructura y un bloque de código que define los miembros de la estructura. Cada miembro puede tener un tipo de dato diferente.

```solidity
struct Persona {
    string nombre;
    uint edad;
    bool estaActivo;
}
```

**Uso**

Una vez declarado, el **`struct`** se puede utilizar para crear variables de ese tipo dentro del contrato, permitiendo almacenar y gestionar datos estructurados de manera eficiente.

```solidity
contract MiContrato {
    // Instancia de un struct
    Persona public persona;

    // Inicializar una instancia del struct
    function crearPersona(string memory _nombre, uint _edad) public {
        persona = Persona(_nombre, _edad, true);
    }

    // Actualizar un campo específico del struct
    function actualizarEdad(uint _edad) public {
        persona.edad = _edad;
    }
}
```

**Ejemplos de Uso Común**

Los **`structs`** se utilizan ampliamente en aplicaciones descentralizadas para representar entidades complejas, como:

* **Usuarios o Perfiles**: Agrupar información relevante de usuarios, como nombre, dirección de correo electrónico, y balance de tokens.
* **Productos o Servicios**: Modelar productos en un marketplace, incluyendo su precio, descripción, y disponibilidad.
* **Operaciones o Transacciones**: Registrar detalles de transacciones, como el remitente, receptor, cantidad y estado.

**Consideraciones**

* **Gestión de Memoria**: En Solidity, los **`structs`** pueden almacenarse en **`storage`**, **`memory`**, o **`calldata`**, dependiendo de su uso y ciclo de vida. Es importante elegir el lugar adecuado de almacenamiento para optimizar el uso de gas y la eficiencia del contrato.
* **Límites y Restricciones**: Aunque los **`structs`** son poderosos, su uso incorrecto puede llevar a un aumento en el costo del gas o a dificultades en la gestión de datos. Por ejemplo, los **`structs`** anidados o las estructuras de datos muy complejas pueden aumentar la complejidad y el costo de las operaciones.


# Ejercicio 8

Haremos un ejercicio integrador que utilizará arrays, structs, mappings y enums.

Cada address tendrá asignado un array de structs. En cada struct se puede guardar un string, un color y se llevará la cuenta de las veces en que se modificó el string.

**Pasos a seguir:**

1. Programe el contrato en Remix,
2. Despliegue el contrato en Remix.
3. Con una address añada información en su correspondiente array para al menos dos posiciones (2 structs). Use la función **`addInfo`** para esto.
4. Haga lo mismo utilizando utilizando otra address.
5. Ejecute las funciones **`setInfo` , `setColor`, `getOneInfo`, `getMyInfoAtIndex`** y **`listAllInfo`.**

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

/// @title Concepts: All together 3
/// @author Solange Gueiros
// One array of structs per address = mapping, struct and array
// countChanges per address
contract AllTogether {

    //enum Colors {Undefined = 0, Blue = 1, Red = 2}
    enum Colors {Undefined, Blue, Red}

    struct InfoStruct {
        string info;
        Colors color;
        uint countChanges;
    }
    mapping (address => InfoStruct[]) public storedInfos;

    constructor() {
        InfoStruct memory auxInfo = InfoStruct ({
            info: "Hello world",
            color: Colors.Undefined,
            countChanges: 0
        });
        storedInfos[msg.sender].push(auxInfo);
    }

    event InfoChange(address person, uint countChanges, string oldInfo, string newInfo);

    // Añade un struct en la última posición del array asociado a quien envía la transacción 
    function addInfo(Colors myColor, string memory myInfo) public returns (uint index) {
        InfoStruct memory auxInfo = InfoStruct ({
            info: myInfo,
            color: myColor,
            countChanges: 0
        });
        storedInfos[msg.sender].push(auxInfo);
        index = storedInfos[msg.sender].length -1;
    }
    
    // Modifica la info dentro del struct de una posición específica del array
    function setInfo(uint index, string memory newInfo) public {
        storedInfos[msg.sender][index].countChanges++;
        emit InfoChange (msg.sender, storedInfos[msg.sender][index].countChanges, storedInfos[msg.sender][index].info, newInfo);
        storedInfos[msg.sender][index].info = newInfo;
    }

    // Modifica el color dentro del struct de una posición específica del array
    function setColor(uint index, Colors myColor) public {
        storedInfos[msg.sender][index].color = myColor;
        storedInfos[msg.sender][index].countChanges++;
    }

    // Devuelve el struct de una posición específica del array asociado a una address
    function getOneInfo(address account, uint index) public view returns (InfoStruct memory) {
        require (index < storedInfos[account].length, "invalid index");
        return storedInfos[account][index];
    }

    // Devuelve el struct de una posición específica del array asociado a quien envía la transacción
    function getMyInfoAtIndex(uint index) external view returns (InfoStruct memory) {
        return getOneInfo(msg.sender, index);
    }

    // Lista el array de structs asociado a una address
    function listAllInfo(address account) external view returns (InfoStruct[] memory) {
        return storedInfos[account];
    }
   
}
```


# Address Payable

La distinción entre **`address`** y **`address payable`** es una característica importante en Solidity para mejorar la seguridad y claridad del código, asegurando que solo las direcciones explícitamente marcadas puedan manejar y recibir Ether.

### **Address**

Una variable de tipo **`address`** puede almacenar una dirección Ethereum de 20 bytes. Este tipo se utiliza para representar direcciones de contratos o direcciones externas (cuentas de usuario) dentro de los contratos inteligentes. Sin embargo, una variable de tipo **`address`** por sí sola no puede recibir Ether directamente a través de transacciones de contrato porque no tiene el modificador **`payable`**.

### **Address payable**

Una dirección **`address payable`**, en cambio, es una dirección especial que puede recibir Ether. Al requerir que una dirección sea explícitamente marcada como **`payable`**, Solidity asegura que solo las direcciones destinadas a manejar Ether puedan hacerlo, evitando así transferencias accidentales o no autorizadas de fondos.

**Cómo Convertir una address a address payable**

En algunos casos, puede ser necesario convertir una **`address`** a **`address payable`**. Esto se puede hacer utilizando la conversión explícita en Solidity, como se muestra a continuación:

```solidity
address addr = msg.sender;
address payable payableAddr = payable(addr);
```

Este mecanismo de conversión es seguro y sigue las prácticas de programación recomendadas en Solidity, asegurando que solo las direcciones que el desarrollador desea que sean **`payable`** puedan recibir Ether.

**Uso de address y address payable**

* **`address`**: Utilizado para la mayoría de las interacciones que no involucran transferencias de Ether, como la consulta de balances de tokens ERC-20 o la interacción con contratos que no requieren el envío de Ether.
* **`address payable`**: Necesario cuando se desea enviar Ether a una dirección desde un contrato, utilizando métodos como **`transfer`**, **`send`**, o llamadas de bajo nivel.


# Cómo reciben Ether los contratos y funciones

En Solidity, los conceptos de funciones **`payable`**, **`receive`**, y **`fallback`** juegan roles cruciales en la interacción de los contratos inteligentes con Ether (ETH) y en la gestión de llamadas de función inesperadas o datos enviados a un contrato. Vamos a desglosar cada uno de estos conceptos para entender su importancia y uso en el desarrollo de contratos inteligentes.

### **Funciones payable**

Una función marcada como **`payable`** es capaz de recibir Ether junto con la llamada a la función. Este modificador es necesario para que cualquier función acepte transacciones de Ether; sin él, si un contrato recibe Ether en una llamada a una función que no es **`payable`**, la transacción será rechazada.

```solidity
function deposit() public payable {
    // Lógica para manejar el depósito de Ether
}
```

El uso de **`payable`** es esencial en contratos que necesitan manejar fondos de Ether, como billeteras, juegos, o cualquier sistema que requiera pagos o donaciones.

### **Función receive**

Solidity permite definir una función **`receive()`** especial dentro de un contrato inteligente. Esta función no puede tener argumentos, no devuelve nada y debe tener visibilidad externa y el modificador **`payable`**. Se ejecuta en transacciones de Ether que no incluyen datos (es decir, transacciones puras de Ether) y no puede ser llamada directamente.

```solidity
receive() external payable {
    // Lógica específica para manejar Ether recibido
}
```

Si un contrato recibe Ether y no tiene una función **`receive()`** definida (o si la transacción incluye datos pero no coincide con ninguna función), se intentará ejecutar la función **`fallback()`** si está presente.

### **Función fallback**

La función **`fallback()`** en Solidity se invoca cuando un contrato recibe Ether sin datos o si ninguna de sus funciones coincide con la firma de la función llamada. Es una función de seguridad que permite al contrato manejar Ether o llamadas de función arbitrarias. Al igual que **`receive()`**, **`fallback()`** no puede tener argumentos, no devuelve nada, y debe tener visibilidad externa. Sin embargo, a diferencia de **`receive()`**, **`fallback()`** puede ser **`payable`** o no.

```solidity
fallback() external payable {
    // Lógica para manejar llamadas inesperadas o Ether recibido sin datos
}
```

Si **`fallback()`** es **`payable`**, el contrato puede recibir Ether a través de esta función. Si no es **`payable`**, el contrato todavía puede procesar llamadas de función que no coinciden, pero rechazará cualquier intento de enviar Ether.

**Consideraciones de Uso y Seguridad**

* **Manejo de Fondos**: Es crucial implementar adecuadamente la lógica de manejo de fondos en **`payable`**, **`receive()`**, y **`fallback()`** para evitar la pérdida de Ether y asegurar que el contrato solo realice acciones esperadas.
* **Seguridad de `fallback()`**: Dado que **`fallback()`** se ejecuta en llamadas de función no esperadas o en el envío de Ether sin datos, es importante limitar su complejidad y las operaciones que realiza para prevenir vulnerabilidades, como ataques de reentrancia.
* **Gas y `fallback()`**: Las llamadas a **`fallback()`** tienen un límite de gas más bajo que las llamadas a funciones normales. Por lo tanto, cualquier operación costosa en gas podría fallar.

<figure><img src="/files/t7v6fPNScbRXJyxyljeF" alt="" width="375"><figcaption></figcaption></figure>


# Transferencias de Ether

Solidity proporciona varias formas de enviar Ether de un contrato a una dirección externa, cada una con sus propias implicaciones en términos de seguridad, gas y comportamiento en caso de fallo. Estas son **`transfer`**, **`send`**, y llamadas de bajo nivel (**`call`**).

### **Transfer**

El método **`transfer`** es una forma segura de enviar Ether ya que automáticamente revierte toda la transacción si la transferencia falla por cualquier motivo. Es la manera recomendada de enviar Ether cuando se desea que la transacción entera falle en caso de que no se pueda completar la transferencia de Ether.

```solidity
address payable receiver = payable(0x123...);
receiver.transfer(amount);
```

Si la transferencia falla, la ejecución se detiene y se revierte.

### **Send**

El método **`send`** es similar a **`transfer`**, pero en lugar de revertir automáticamente, devuelve un valor booleano (**`true`** o **`false`**) indicando el éxito o fracaso de la operación. Esto permite al contrato manejar el fallo de la transferencia de una manera más flexible.

```solidity
address payable receiver = payable(0x123...);
bool sent = receiver.send(amount);
if (!sent) {
    // Manejar el fallo
}
```

**`send`** es menos usado debido a la necesidad de manejar manualmente el caso de fallo, pero puede ser útil cuando se quiere una lógica de manejo de errores específica.

### **Call**

El método **`call`** es aún más flexible y se recomienda en las versiones más recientes de Solidity para enviar Ether. **`call`** devuelve un booleano indicando el éxito o fracaso, y permite especificar datos adicionales para la llamada, lo que lo hace compatible con la ejecución de funciones en el contrato receptor de Ether. Sin embargo, esta flexibilidad viene con la responsabilidad de manejar correctamente la seguridad.

```solidity
(address payable receiver).call{value: amount}("");
```

Es importante tener precauciones de seguridad al usar **`call`** para transferir Ether, especialmente para evitar reentrancias, un tipo de vulnerabilidad donde un atacante puede forzar al contrato a ejecutar ciertas funciones de manera recursiva.

**Consideraciones de seguridad**

* **Prevención de Reentrancia**: Al transferir Ether, especialmente usando **`call`**, es crucial proteger el contrato contra ataques de reentrancia. Patrones como el chequeo-efectos-interacción y el uso de modificadores de reentrancia pueden ayudar a mitigar este riesgo.
* **Verificar el Éxito de la Transferencia**: Siempre se debe verificar el resultado de una transferencia de Ether y manejar adecuadamente el caso de fallo, especialmente cuando se usan **`send`** y **`call`**.
* **Gas para Llamadas Externas**: Al enviar Ether, se proporciona una cantidad fija de gas (2300 gas al usar **`transfer`** o **`send`**) que es suficiente para eventos de registro pero no para ejecutar código en el contrato receptor. Con **`call`**, es posible especificar una cantidad mayor de gas, lo cual debe hacerse con cuidado.

**Ejemplo**

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract ReceiveEther {
    

    // Function to receive Ether. msg.data must be empty
    receive() external payable {}

    // Fallback function is called when msg.data is not empty
    fallback() external payable {}

    function getBalance() public view returns (uint) {
        return address(this).balance;
    }
}

contract SendEther {
    function sendViaTransfer(address payable _to) public payable {
        // This function is no longer recommended for sending Ether.
        _to.transfer(msg.value);
    }

    function sendViaSend(address payable _to) public payable {
        // Send returns a boolean value indicating success or failure.
        // This function is not recommended for sending Ether.
        bool sent = _to.send(msg.value);
        require(sent, "Failed to send Ether");
    }

    function sendViaCall(address payable _to) public payable {
        // Call returns a boolean value indicating success or failure.
        // This is the current recommended method to use.
        (bool sent, bytes memory data) = _to.call{value: msg.value}("");
        require(sent, "Failed to send Ether");
    }
}
```


# Conceptos Avanzados


# Codificación ABI

### Codificación ABI

Application Binary Interface (ABI) es un componente que actúa como la capa de interacción entre dos programas binarios, en este caso, entre contratos inteligentes y el mundo exterior (como aplicaciones frontend, otras herramientas de blockchain o incluso otros contratos inteligentes). El ABI especifica cómo codificar y decodificar datos para que puedan ser leídos por la Máquina Virtual de Ethereum (EVM). Esencialmente, el ABI es un conjunto de reglas que dictan cómo convertir las llamadas de funciones y sus parámetros en una forma que el contrato inteligente puede entender, y viceversa, cómo los datos de salida deben ser convertidos de vuelta para el mundo exterior.

Solidity proporciona funciones integradas para codificar y decodificar datos según estas reglas, conocidas colectivamente como funciones **`abi`**.

#### **Funciones abi**

1. **`abi.encode(...) returns (bytes memory)`**

   Codifica los argumentos dados en una secuencia de bytes según las reglas ABI. Es útil para preparar datos para, por ejemplo, una llamada de función de bajo nivel.

   **Ejemplo**

   ```solidity
   bytes memory encodedData = abi.encode(arg1, arg2);
   ```

   En el ejemplo **`arg1, arg2`**: Son los argumentos que se pasan a la función **`abi.encode`**. Estos argumentos pueden ser de cualquier tipo soportado por Solidity, como **`uint`**, **`address`**, **`string`**, etc. La función **`abi.encode`** toma estos argumentos, los codifica en un formato binario único y devuelve este dato codificado como un arreglo de bytes.
2. **`abi.encodePacked(...) returns (bytes memory)`**

Similar a **`abi.encode`**, pero codifica los argumentos de una manera más compacta, sin rellenar, lo que puede ser útil para ciertas operaciones criptográficas. Sin embargo, la falta de relleno puede llevar a ambigüedades en ciertas situaciones, así que debe usarse con precaución.

**Ejemplo**:

```solidity
bytes memory packedData = abi.encodePacked(arg1, arg2);
```

3. **`abi.encodeWithSelector(bytes4 selector, ...) returns (bytes memory)`**

**Uso**: Codifica los argumentos dados con un selector de función específico. Útil para llamadas a funciones en contratos externos cuando se conoce el selector de la función.

**Ejemplo**:

```solidity
bytes memory dataWithSelector = abi.encodeWithSelector(selector, arg1, arg2);
```

El selector de función es esencialmente la firma de la función codificada en 4 bytes. Esta funcionalidad es particularmente útil cuando se realizan llamadas de bajo nivel o se interactúa con contratos cuyas interfaces pueden no estar completamente definidas en tiempo de compilación.

Incluyamos un ejemplo de cómo usar **`abi.encodeWithSelector`** en Solidity para preparar datos para una llamada de contrato de bajo nivel:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Receiver {
    event Received(uint256 indexed value, address sender);

    // Una función simple que emite un evento con el valor y el remitente
    function receiveValue(uint256 value) public {
        emit Received(value, msg.sender);
    }
}

contract Caller {
    // Función que llama a `receiveValue` en el contrato Receiver usando abi.encodeWithSelector
    function callReceiveValue(address _receiver, uint256 _value) public {
        // Primero, calculamos el selector de la función.
        // La firma de la función es "receiveValue(uint256)"
        bytes4 selector = bytes4(keccak256("receiveValue(uint256)"));

        // Luego, codificamos el selector junto con los argumentos de la función.
        bytes memory data = abi.encodeWithSelector(selector, _value);

        // Realizamos la llamada de bajo nivel.
        (bool success, ) = _receiver.call(data);
        require(success, "La llamada fallo.");
    }
}
```

En este ejemplo:

* **Receiver**: Es un contrato que tiene una función **`receiveValue`**, la cual emite un evento cuando se llama. Esta función podría ser cualquier lógica que quieras invocar en otro contrato.
* **Caller**: Es un contrato que realiza una llamada al contrato **`Receiver`**. Utiliza **`abi.encodeWithSelector`** para preparar los datos de la llamada. Esto incluye el selector de la función, que identifica qué función llamar en el contrato **`Receiver`**, y los argumentos para esa función.
  * **`bytes4 selector = bytes4(keccak256("receiveValue(uint256)"));`** calcula el selector de la función basado en su firma. La firma es simplemente el nombre de la función seguido de los tipos de sus parámetros entre paréntesis, codificado en 4 bytes.
  * **`abi.encodeWithSelector(selector, _value)`** codifica estos datos en un formato que el contrato **`Receiver`** puede decodificar y ejecutar.

4. **`abi.encodeWithSignature(string memory signature, ...) returns (bytes memory)`**

Similar a **`abi.encodeWithSelector`**, pero en lugar de proporcionar el selector de función como un valor **`bytes4`**, se proporciona la firma de la función como una cadena. Solidity calcula el selector de función correspondiente.

**Ejemplo**:

```solidity
bytes memory dataWithSignature = abi.encodeWithSignature("functionName(uint256,address)", arg1, arg2);
```

En este ejemplo, **`functionName(uint256,address)`** es la signature o firma.

5. **`abi.decode(bytes memory data, (type1, type2, ...)) returns (type1, type2, ...)`**

Decodifica los datos codificados según las reglas ABI en los tipos especificados. Es útil para interpretar los datos de salida de las llamadas a funciones de bajo nivel o las respuestas de las llamadas a otros contratos.

**Ejemplo**:

```solidity
(type1 var1, type2 var2) = abi.decode(encodedData, (type1, type2));
```


# Hashing

El hashing se utiliza comúnmente para diversos propósitos, como verificar la integridad de los datos, crear identificadores únicos para los conjuntos de datos, y más. Solidity proporciona funciones de hash incorporadas que implementan algoritmos de hash seguros y eficientes.

Cabe destacar dos funciones: **`keccak256`** y **`sha256`.**

El algoritmo **`keccak256`** es una versión del SHA-3 (Secure Hash Algorithm 3) y es ampliamente utilizado en el ecosistema Ethereum, por ejemplo, para calcular direcciones Ethereum, para generar identificadores únicos, crear pruebas de existencia de datos (a través de merkle trees), y en la implementación de mecanismos de compromiso.

**`sha256`** es parte de la familia de algoritmos SHA-2 y es ampliamente utilizado fuera de Ethereum para garantizar la integridad de los datos. Aunque menos común en Ethereum que **`keccak256`**, **`sha256`** se utiliza cuando se necesita interoperabilidad con sistemas que utilizan SHA-256 como estándar de hashing.

#### Funciones de hashing

| Funciones                                                                | Resultado                                                                                                                   |
| ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------- |
| keccak256(bytes memory) returns (bytes32)                                | Calcula el hash Keccak-256 de la entrada                                                                                    |
| sha256(bytes memory) returns (bytes32)                                   | Calcula el hash SHA-256 de la entrada                                                                                       |
| ripemd160(bytes memory) returns (bytes20                                 | Calcula el hash RIPEMD-160 de la entrada                                                                                    |
| ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address) | Utilizando una firma de curva elíptica, devuelve la dirección asociada con la clave pública, o si hay error, devuelve cero. |

**Ejemplo**

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract HashingExample {
    // Función para calcular el hash de una cadena de caracteres
    function calculateHash(string memory _input) public pure returns (bytes32) {
        return keccak256(abi.encodePacked(_input));
    }
}
```


# This

En Solidity, **`this`** se refiere a la instancia actual del contrato en el que se está ejecutando el código. Es similar al uso de **`this`** en otros lenguajes de programación orientados a objetos, donde se usa para referirse a la instancia actual del objeto o clase. Sin embargo, en el contexto de Solidity, **`this`** tiene características y usos específicos debido a la naturaleza de los contratos inteligentes y la EVM.

#### **USO PRINCIPAL DE  `this` EN SOLIDITY**

**Referencia al Contrato Actual**

**`this`** se utiliza como una referencia explícita al contrato actual. Proporciona una forma de acceder a las funciones y variables del contrato desde dentro de sus propias funciones.

**Conversión a Dirección**

Cuando se utiliza **`this`** en un contexto donde se espera una dirección (por ejemplo, al interactuar con otros contratos o al enviar Ether), **`this`** se convierte automáticamente en la dirección del contrato actual. Esto es útil para operaciones que requieren la dirección del contrato, como transferencias de tokens o llamadas a funciones de otros contratos.

#### **EJEMPLOS DE USO**

```solidity
pragma solidity ^0.8.0;

contract MyContract {
    function sendEther(address payable recipient) public payable {
        // Enviar todo el Ether enviado a esta función al destinatario
        recipient.transfer(msg.value);
    }

    function contractBalance() public view returns (uint) {
        // Acceder al balance de Ether del contrato usando `this`
        return address(this).balance;
    }
}
```

En este ejemplo, **`address(this).balance`** se utiliza para obtener el balance de Ether almacenado en el contrato. **`this`** se convierte a una dirección mediante **`address(this)`** para que se pueda acceder a la propiedad **`balance`**.

**Interacción entre Contratos**

```solidity
pragma solidity ^0.8.0;

interface OtherContract {
    function someFunction(address caller) external;
}

contract MyContract {
    OtherContract otherContract;

    constructor(address _otherContractAddress) {
        otherContract = OtherContract(_otherContractAddress);
    }

    function callOtherContractFunction() public {
        // Pasar la dirección de este contrato a otra función de contrato
        otherContract.someFunction(address(this));
    }
}
```

Aquí, **`address(this)`** se usa para pasar la dirección del contrato actual **`MyContract`** a una función en otro contrato **`OtherContract`**. Esto puede ser útil para que el otro contrato verifique quién lo llamó o interactúe de nuevo con el contrato llamante.

Aunque **`this`** es una herramienta útil para referirse al contrato actual, es importante recordar que las interacciones entre contratos y las transferencias de Ether pueden introducir vulnerabilidades y riesgos de seguridad. Por ejemplo, al enviar Ether o llamar a funciones en otros contratos, siempre se debe manejar la posibilidad de fallos o ataques de reentrancia.


# Herencia

La herencia es un principio fundamental de la programación orientada a objetos (OOP) que también se encuentra en Solidity. La herencia permite que un contrato herede propiedades y comportamientos (variables de estado y funciones) de uno o más contratos "padres", promoviendo así la reutilización de código y la creación de relaciones jerárquicas entre contratos.

**Características de la Herencia en Solidity**

* **Reutilización de Código**: La herencia permite que los contratos inteligentes reutilicen el código de otros contratos. Esto no solo reduce la redundancia sino que también fomenta la práctica del principio DRY (Don't Repeat Yourself).
* **Jerarquía de Contratos**: Solidity permite la creación de una jerarquía de contratos, donde un contrato hijo puede heredar de múltiples contratos padres, siguiendo un patrón de herencia múltiple.
* **Sobrescritura de Funciones**: Los contratos hijos pueden sobrescribir funciones heredadas de sus contratos padres, lo que permite personalizar o extender la funcionalidad base. Para esto se utilizará el atributo **`override`** en la función.
* **Modificadores de Visibilidad**: Solidity usa modificadores de visibilidad (**`public`**, **`internal`**, **`private`**, y **`external`**) para controlar el acceso a las funciones y variables de estado. Las reglas de visibilidad también aplican a los contratos heredados.

**Ejemplo Básico de Herencia**

<div><img src="https://prod-files-secure.s3.us-west-2.amazonaws.com/1c4f016a-3ef4-422a-bee4-76cbe41f54a1/dbe51a31-2623-4831-b4d9-ccfa928a00c9/Untitled.png" alt=""> <figure><img src="/files/dja2K6UZTPbbTmbtqkmm" alt="" width="361"><figcaption></figcaption></figure></div>

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// Contrato padre
contract Base {
    uint public data;

    constructor(uint _data) {
        data = _data;
    }

    function setData(uint _data) public {
        data = _data;
    }
}

// Contrato hijo que hereda de Base
contract Derived is Base {
    constructor(uint _initialData) Base(_initialData) {
        // Inicializa el contrato padre con _initialData
    }

    // Sobrescritura de la función setData
    function setData(uint _data) public override {
        data = _data + 10; // Cambia la implementación para sumar 10 antes de almacenar
    }
}
```

En este ejemplo, **`Derived`** hereda de **`Base`**. Esto significa que **`Derived`** tiene acceso a la variable **`data`** y puede usar o sobrescribir la función **`setData`**. En la función **`setData`** sobrescrita, modificamos la implementación para sumar 10 al **`_data`** antes de almacenarlo.

**Herencia multinivel**

La herencia multinivel es un concepto donde un contrato hereda de otro contrato, que a su vez hereda de otro, creando así una jerarquía de herencia "multinivel". Este patrón permite la construcción de relaciones jerárquicas complejas y la reutilización de código a través de varios niveles de contratos.

<figure><img src="/files/Oc1aBVm7eQ4k7lwOI05f" alt="" width="375"><figcaption></figcaption></figure>

Aquí tienes un ejemplo de herencia multinivel en Solidity, que ilustra cómo un contrato puede heredar propiedades y comportamientos de varios contratos padres situados en diferentes niveles de la jerarquía:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// Contrato de nivel base
contract Grandparent {
    uint public grandparentValue;

    constructor(uint _value) {
        grandparentValue = _value;
    }

    function setGrandparentValue(uint _value) public {
        grandparentValue = _value;
    }
}

// Primer nivel de herencia
contract Parent is Grandparent {
    uint public parentValue;

    constructor(uint _grandparentValue, uint _parentValue) Grandparent(_grandparentValue) {
        parentValue = _parentValue;
    }

    function setParentValue(uint _value) public {
        parentValue = _value;
    }
}

// Segundo nivel de herencia
contract Child is Parent {
    uint public childValue;

    constructor(uint _grandparentValue, uint _parentValue, uint _childValue) Parent(_grandparentValue, _parentValue) {
        childValue = _childValue;
    }

    function setChildValue(uint _value) public {
        childValue = _value;
    }
}
```

#### **Explicación del código:**

* **`Grandparent`**: Este es el contrato de nivel base que define una variable **`grandparentValue`** y una función **`setGrandparentValue`** para modificarla. También tiene un constructor que inicializa **`grandparentValue`**.
* **`Parent`**: Este contrato hereda de **`Grandparent`**. Añade su propia variable **`parentValue`** y una función **`setParentValue`** para modificarla. Su constructor llama al constructor de **`Grandparent`** para inicializar **`grandparentValue`**, y también inicializa **`parentValue`**.
* **`Child`**: Este es el contrato de nivel más bajo que hereda de **`Parent`** (y, por lo tanto, indirectamente de **`Grandparent`**). Añade una variable **`childValue`** y una función **`setChildValue`**. Su constructor inicializa las variables de todos los niveles de la jerarquía llamando al constructor de **`Parent`**, que a su vez llama al constructor de **`Grandparent`**.

Este ejemplo muestra cómo se pueden crear y utilizar relaciones jerárquicas en Solidity mediante la herencia multinivel, permitiendo a los contratos hijos acceder y sobrescribir propiedades y funciones de sus ancestros, promoviendo la reutilización y la modularidad del código.

**Herencia jerárquica**

La herencia jerárquica en Solidity se refiere a un patrón donde múltiples contratos hijos heredan de un único contrato padre, creando una estructura jerárquica en "forma de árbol" con un nodo raíz común. Este patrón permite compartir lógica común y propiedades entre varios contratos derivados, manteniendo al mismo tiempo diferencias específicas en cada uno de ellos.

<figure><img src="/files/lHqTzBQjEgWPI5etr8WX" alt="" width="375"><figcaption></figcaption></figure>

Aquí te muestro un ejemplo de herencia jerárquica en Solidity:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// Contrato padre común
contract Vehicle {
    string public brand;
    string public model;

    constructor(string memory _brand, string memory _model) {
        brand = _brand;
        model = _model;
    }

    function getVehicleInfo() public view returns (string memory, string memory) {
        return (brand, model);
    }
}

// Primer contrato hijo que hereda de Vehicle
contract Car is Vehicle {
    uint public carMaxSpeed;

    constructor(string memory _brand, string memory _model, uint _maxSpeed)
        Vehicle(_brand, _model) {
        carMaxSpeed = _maxSpeed;
    }

    function getMaxSpeed() public view returns (uint) {
        return carMaxSpeed;
    }
}

// Segundo contrato hijo que hereda de Vehicle
contract Truck is Vehicle {
    uint public truckLoadCapacity;

    constructor(string memory _brand, string memory _model, uint _loadCapacity)
        Vehicle(_brand, _model) {
        truckLoadCapacity = _loadCapacity;
    }

    function getLoadCapacity() public view returns (uint) {
        return truckLoadCapacity;
    }
}
```

**Explicación del Código:**

* **`Vehicle`**: Este es el contrato base o "padre" que define propiedades comunes (**`brand`** y **`model`**) y una función (**`getVehicleInfo`**) aplicable a cualquier tipo de vehículo. Funciona como el nodo raíz común en la herencia jerárquica.
* **`Car` y `Truck`**: Estos son contratos "hijos" que heredan del contrato **`Vehicle`**. Cada uno de ellos extiende la funcionalidad base incluyendo propiedades específicas (**`carMaxSpeed`** para **`Car`** y **`truckLoadCapacity`** para **`Truck`**) y funciones adicionales para interactuar con esas propiedades (**`getMaxSpeed`** y **`getLoadCapacity`**, respectivamente). Aunque comparten algunas características comunes definidas en **`Vehicle`**, **`Car`** y **`Truck`** se especializan en diferentes aspectos de los vehículos que representan.

**Características de la Herencia Jerárquica:**

* **Especialización**: Permite que los contratos hijos se especialicen añadiendo o modificando funcionalidades específicas.
* **Organización Clara**: Facilita una estructura clara y lógica para los contratos, reflejando relaciones del mundo real y facilitando la comprensión y el mantenimiento del código.

Este patrón de herencia es especialmente útil en situaciones donde diferentes entidades comparten características comunes pero también necesitan sus propias implementaciones y características específicas.

**Herencia múltiple**

La herencia múltiple permite que un contrato herede comportamientos y características de múltiples contratos padres.

<figure><img src="/files/26ZMC4FqiLfSlnNpv13j" alt="" width="344"><figcaption></figcaption></figure>

La herencia múltiple puede ser muy poderosa pero también puede introducir complejidad y ambigüedades. Es importante diseñar cuidadosamente la arquitectura de los contratos para evitar problemas comunes como la colisión de nombres o la dependencia circular entre contratos.

Ejemplo:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract A {
    function foo() public pure returns(string memory) {
        return "A";
    }
}

contract B {
    function bar() public pure returns(string memory) {
        return "B";
    }
}

contract C is A, B {
    function fooBar() public pure returns(string memory) {
        return string(abi.encodePacked(foo(), bar()));
    }
}
```

En este ejemplo, el contrato **`C`** hereda de ambos, **`A`** y **`B`**, y tiene acceso a sus funciones **`foo`** y **`bar`**, respectivamente. El contrato **`C`** introduce una nueva función **`fooBar`** que combina las salidas de las funciones heredadas.


# Abstract

El concepto de contratos abstractos se usa para crear contratos que sirven como plantillas base para otros contratos, pero que no están destinados a ser desplegados por sí mismos. Un contrato se considera abstracto si al menos una de sus funciones no tiene implementación completa dentro del contrato. Esta falta de implementación indica que el contrato está destinado a ser extendido por otros contratos que completarán estas implementaciones. Los contratos abstractos son una herramienta fundamental en lSolidity, permitiendo a los desarrolladores definir interfaces y comportamientos comunes que pueden ser compartidos y extendidos por múltiples contratos.

**Características de los Contratos Abstractos**

1. **Funciones sin implementar**: La principal característica de un contrato abstracto es que contiene al menos una función sin una implementación completa. Esto se indica mediante la ausencia del cuerpo de la función (las llaves **`{}`**) y finalizando la declaración de la función con un punto y coma (**`;`**).
2. **Herencia**: Los contratos abstractos están diseñados para ser heredados por otros contratos. Un contrato que hereda de un contrato abstracto debe implementar todas las funciones sin implementar, a menos que dicho contrato también sea declarado como abstracto.
3. **Palabra clave `abstract`**: Se utiliza para para declarar explícitamente un contrato como abstracto. La introducción de esta palabra clave permite a los desarrolladores comunicar sus intenciones de manera más clara y evitar el despliegue accidental de contratos incompletos.
4. **Uso de `virtual` y `override`**: En el contexto de contratos abstractos, las funciones sin implementar se pueden marcar como **`virtual`**, y los contratos que heredan estas funciones deben usar la palabra clave **`override`** al proporcionar una implementación, siguiendo el sistema de herencia y polimorfismo de Solidity.

Veamos un ejemplo a continuación.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

abstract contract Animal {
    function makeSound() public virtual returns (string memory);
}

contract Dog is Animal {
    function makeSound() public pure override returns (string memory) {
        return "Woof";
    }
}

contract Cat is Animal {
    function makeSound() public pure override returns (string memory) {
        return "Meow";
    }
}
```

En este ejemplo, **`Animal`** es un contrato abstracto porque tiene una función, **`makeSound`**, sin implementar. La palabra clave **`virtual`** indica que esta función está destinada a ser sobrescrita por los contratos herederos. Los contratos **`Dog`** y **`Cat`** heredan de **`Animal`** y proporcionan sus propias implementaciones de la función **`makeSound`**, utilizando la palabra clave **`override`** para indicar que están sobrescribiendo la función abstracta.


# Interface

Una interface o interfaz es una colección de declaraciones de funciones que actúa como un contrato formal entre diferentes partes del código. Una interface define funciones sin implementarlas, estableciendo un conjunto de funcionalidades que otros contratos deben cumplir. Las interfaces son una herramienta poderosa para garantizar la modularidad y la interoperabilidad entre contratos en la blockchain de Ethereum.

**Características principales de las interfaces**

1. **Funciones externas**: Todas las funciones declaradas en una interface deben ser externas. Esto significa que solo pueden ser llamadas desde fuera del contrato, no desde dentro de él.
2. **Sin variables de estado**: Las interfaces no pueden tener variables de estado. Su propósito es definir funciones, no almacenar datos.
3. **Sin constructor**: Las interfaces no pueden tener un constructor porque no se pueden desplegar por sí mismas. Su función es ser implementadas por otros contratos.
4. **Herencia**: Las interfaces pueden heredar de otras interfaces, y los contratos pueden implementar múltiples interfaces, lo que permite una forma de herencia múltiple.
5. **Compatibilidad**: Las interfaces facilitan la interacción entre contratos al definir un conjunto común de funciones públicas sin imponer una estructura interna. Esto permite que contratos diferentes interactúen entre sí siempre que cumplan con la interfaz especificada.

Para declarar una interface en Solidity, se utiliza la palabra clave **`interface`**. A continuación, se muestra un ejemplo simple de cómo se puede definir una:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IGreeter {
    function greet(string calldata _name) external returns (string memory);
}
```

En este ejemplo, **`IGreeter`** es una interface que declara una función **`greet`**, la cual cualquier contrato que implemente esta interface debe definir.

**Implementación de una Interface**

Un contrato implementa una interface al heredar de ella y proporcionar implementaciones concretas para todas sus funciones. Aquí hay un ejemplo de cómo un contrato puede implementar la interface **`IGreeter`** definida anteriormente:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./IGreeter.sol";

contract Greeter is IGreeter {
    function greet(string calldata _name) external pure override returns (string memory) {
        return string(abi.encodePacked("Hello, ", _name));
    }
}
```

En este caso, el contrato **`Greeter`** implementa la función **`greet`** especificada en la interface **`IGreeter`**. Note que la palabra clave **`override`** se usa para indicar que **`Greeter`** está proporcionando la implementación específica de una función declarada en una interface o contrato base.

**Uso de Interfaces**

Las interfaces son especialmente útiles en Solidity para definir estándares comunes y permitir la interacción entre contratos. Por ejemplo, la ERC-20 y la ERC-721 son interfaces que definen los estándares para los tokens fungibles y no fungibles en Ethereum, respectivamente. Al implementar estas interfaces, los contratos de tokens aseguran la interoperabilidad con wallets, exchanges, y otros contratos que esperan tokens que siguen estas especificaciones.

Las interfaces promueven la separación entre la definición de una funcionalidad y su implementación, permitiendo a los desarrolladores de contratos inteligentes crear sistemas modulares y extensibles que pueden interactuar de manera eficiente y segura en el ecosistema de Ethereum.


# Llamadas entre contratos

Llamar a un contrato desde otro contrato en Solidity es una práctica común que permite la interacción entre diferentes contratos inteligentes en la blockchain de Ethereum. Este proceso se puede realizar de varias maneras, dependiendo de si conoces la interfaz del contrato al que deseas llamar de antemano. A continuación, se describen dos métodos comunes para realizar estas llamadas: mediante Interfaces y mediante llamadas de bajo nivel.

**Método 1: Uso de Interfaces**

Si conoces la interfaz del contrato al que deseas llamar, puedes definir una interface en Solidity que declare los métodos del contrato externo. Luego, puedes usar esta interface para interactuar con el contrato externo como si estuvieras llamando a métodos locales.

1. **Definir la Interface del Contrato Externo**

Primero, defines una interface con las firmas de las funciones del contrato externo que deseas llamar.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface ContratoExterno {
    function funcionExterna(string calldata mensaje) external returns (bool);
}
```

1. **Interactuar con el Contrato Externo**

Luego, en tu contrato, puedes utilizar esta interface para crear una instancia del contrato externo y llamar a sus funciones.

```solidity
contract MiContrato {
    function llamarFuncionExterna(address direccionExterna, string memory mensaje) public returns (bool) {
        ContratoExterno contrato = ContratoExterno(direccionExterna);
        return contrato.funcionExterna(mensaje);
    }
}
```

**Método 2: Llamadas de Bajo Nivel**

Las llamadas de bajo nivel (**`call`**, **`delegatecall`**, y **`staticcall`**) ofrecen más flexibilidad pero son menos seguras ya que no proporcionan verificación de tipos y pueden fallar silenciosamente si la llamada no tiene éxito. Debes utilizarlas solo si entiendes bien las implicancias.

```solidity
contract MiContrato {
    function llamarFuncionExternaBajoNivel(address direccionExterna, string memory mensaje) public returns (bool, bytes memory) {
        (bool exito, bytes memory respuesta) = direccionExterna.call(
            abi.encodeWithSignature("funcionExterna(string)", mensaje)
        );
        return (exito, respuesta);
    }
}
```

En este ejemplo, **`call`** se utiliza para llamar a **`funcionExterna`** del contrato en **`direccionExterna`**. **`abi.encodeWithSignature`** se usa para codificar la llamada a la función, incluyendo el nombre de la función y los parámetros que se pasan. La función devuelve dos valores: un booleano que indica si la llamada fue exitosa y los datos de respuesta.

Al llamar a otro contrato, es crucial tener en cuenta las implicaciones de seguridad, especialmente al aceptar direcciones de contratos como entrada de usuarios no confiables. Las llamadas a contratos no confiables pueden exponer tu contrato a riesgos de reentrancy u otros vectores de ataque. Utiliza patrones de seguridad como los checks-effects-interactions y considera el uso de **`reentrancy guards`** si tu contrato actualiza estados o transfiere fondos en respuesta a llamadas externas.


# EVM

La Ethereum Virtual Machine (EVM) es el componente central del ecosistema Ethereum que permite la ejecución de código en la red. Funciona como un entorno de ejecución descentralizado que ejecuta el código de los contratos inteligentes de manera completamente aislada de la red, el sistema de archivos y otros procesos del sistema. La EVM está diseñada para ser completamente sandboxed, es decir, un entorno de ejecución seguro y controlado.

La EVM es una pieza fundamental del ecosistema Ethereum, ya que permite la ejecución segura y descentralizada de contratos inteligentes. Gracias a la EVM, Ethereum puede funcionar como una plataforma global para aplicaciones descentralizadas (dApps), abriendo un amplio rango de posibilidades en finanzas descentralizadas (DeFi), juegos, identidad digital, y más. La constante evolución de la EVM asegura que Ethereum pueda adaptarse a las necesidades cambiantes de su comunidad de desarrolladores y usuarios.

#### **Funcionamiento de la EVM**

La EVM es Turing-completa, lo que significa que, en teoría, puede ejecutar cualquier algoritmo, dado suficiente tiempo y recursos. Cada nodo de la red Ethereum ejecuta una instancia de la EVM, permitiendo el despliegue y la ejecución de contratos inteligentes. Cuando se ejecuta un contrato inteligente, cada nodo de la red procesa el código de manera independiente y llega al mismo resultado. Esto asegura la integridad y la consistencia de los datos en la blockchain de Ethereum.

#### **Gas y Limitaciones de la EVM**

La ejecución de operaciones en la EVM requiere un recurso denominado "gas", que es necesario para evitar bucles infinitos y asegurar que los recursos de la red se utilicen eficientemente. El gas es esencialmente una unidad de medida que cuantifica la cantidad de trabajo computacional requerido para realizar operaciones específicas, como cálculos, almacenamiento de datos, y transacciones. Los usuarios deben pagar gas para ejecutar operaciones en la red Ethereum, y el precio del gas varía según la demanda de recursos de la red.


# ABI

El Application Binary Interface (ABI) en Ethereum es un estándar crucial para interactuar con los contratos inteligentes en la blockchain de Ethereum. Es esencialmente un formato de interfaz que permite a los contratos inteligentes comunicarse con aplicaciones externas y con otros contratos en la blockchain. El ABI define cómo se pueden llamar las funciones de un contrato inteligente, incluidos los parámetros que se requieren y los tipos de datos de retorno, lo que facilita la interacción entre el código binario de bajo nivel que se ejecuta en la Ethereum Virtual Machine (EVM) y el código de alto nivel escrito por desarrolladores y usuarios.

**Características principales del ABI**

1. **Definición de funciones**: El ABI especifica las funciones del contrato inteligente, incluyendo nombres, tipos de parámetros de entrada y salida, y si son funciones de vista o funciones que alteran el estado en la blockchain.
2. **Codificación de datos**: Proporciona reglas para la codificación (transformación de datos de alto nivel a su representación binaria) y decodificación (el proceso inverso) de los argumentos de las funciones y los valores devueltos. Esto asegura que las llamadas a funciones y las transacciones puedan ser correctamente interpretadas por los contratos inteligentes.
3. **Eventos**: El ABI también define eventos que los contratos inteligentes pueden emitir. Estos eventos permiten que las aplicaciones externas reciban notificaciones sobre cambios o acciones específicas que ocurren dentro de los contratos inteligentes.

A continuación, veamos un ejemplo de un ABI de ejemplo para un contrato inteligente. Imagina que este contrato inteligente tiene como propósito gestionar una lista simple de tareas, permitiendo a los usuarios añadir tareas y marcarlas como completadas. El contrato podría incluir funciones como **`addTask`**, que añade una nueva tarea, y **`markTaskCompleted`**, que marca una tarea como completada.

El código Solidity del contrato podría verse algo así:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract TodoList {
    struct Task {
        string description;
        bool isCompleted;
    }

    Task[] public tasks;

    function addTask(string memory _description) public {
        tasks.push(Task(_description, false));
    }

    function markTaskCompleted(uint _taskId) public {
        tasks[_taskId].isCompleted = true;
    }

    function getTask(uint _taskId) public view returns (string memory, bool) {
        Task storage task = tasks[_taskId];
        return (task.description, task.isCompleted);
    }
}
```

Para este contrato, un ABI de ejemplo que describa las funciones disponibles podría verse así:

```json
[
    {
        "inputs": [
            {
                "internalType": "string",
                "name": "_description",
                "type": "string"
            }
        ],
        "name": "addTask",
        "outputs": [],
        "stateMutability": "nonpayable",
        "type": "function"
    },
    {
        "inputs": [
            {
                "internalType": "uint256",
                "name": "_taskId",
                "type": "uint256"
            }
        ],
        "name": "markTaskCompleted",
        "outputs": [],
        "stateMutability": "nonpayable",
        "type": "function"
    },
    {
        "inputs": [
            {
                "internalType": "uint256",
                "name": "_taskId",
                "type": "uint256"
            }
        ],
        "name": "getTask",
        "outputs": [
            {
                "internalType": "string",
                "name": "",
                "type": "string"
            },
            {
                "internalType": "bool",
                "name": "",
                "type": "bool"
            }
        ],
        "stateMutability": "view",
        "type": "function"
    }
]
```

Este ABI describe las tres funciones del contrato **`TodoList`**, incluyendo los tipos de los parámetros de entrada y salida. La propiedad **`stateMutability`** indica si la llamada a la función modifica el estado del contrato (**`nonpayable`** para **`addTask`** y **`markTaskCompleted`**, que no aceptan ETH pero modifican el estado) o simplemente lee datos (**`view`** para **`getTask`**, que no modifica el estado del contrato).

Este ABI permite a las aplicaciones cliente saber cómo interactuar con el contrato inteligente, invocando sus funciones y leyendo sus respuestas. Al integrar este ABI en una aplicación (por ejemplo, usando Web3.js o ethers.js), los desarrolladores pueden realizar llamadas a funciones del contrato de manera sencilla, pasando los argumentos necesarios y procesando los valores devueltos.

**Uso del ABI**

Para interactuar con un contrato inteligente, una aplicación externa necesita conocer su ABI. Este requisito se debe a que el código de los contratos inteligentes se compila en bytecode antes de ser desplegado en la blockchain, y el bytecode por sí solo no es suficiente para determinar cómo interactuar con el contrato de manera efectiva.

Cuando un desarrollador utiliza herramientas como Remix o Hardhat para compilar y desplegar contratos inteligentes, estas herramientas generan automáticamente el ABI basándose en el código fuente del contrato. Luego, el desarrollador puede integrar este ABI en aplicaciones cliente (por ejemplo, una aplicación web que interactúa con el contrato) para facilitar la comunicación.

**Ejemplo de Uso del ABI**

Imagina que tienes un contrato inteligente en Solidity que incluye una función para obtener el nombre de un usuario. Para llamar a esta función desde una aplicación web, necesitarías el ABI del contrato. Con el ABI, puedes utilizar una librería de Ethereum como web3.js o ethers.js para crear una instancia del contrato en tu aplicación y hacer llamadas a sus funciones como si fueran funciones JavaScript.

```jsx
const contractABI = /* ABI del contrato inteligente aquí */;
const contractAddress = '0x...'; // La dirección del contrato desplegado
const web3 = new Web3('<http://localhost:8545>');
const myContract = new web3.eth.Contract(contractABI, contractAddress);

// Llamando a una función del contrato
myContract.methods.nombreDeLaFuncion().call()
    .then(result => {
        console.log(result);
    });
```


# Bytecode

El bytecode es una forma de instrucciones que es ejecutable por una computadora o una máquina virtual. En el contexto de Ethereum y la Ethereum Virtual Machine (EVM), el bytecode se refiere al código de bajo nivel que la EVM puede interpretar y ejecutar directamente. Este bytecode se genera a partir del código fuente de un contrato inteligente escrito en un lenguaje de alto nivel, como Solidity o Vyper, mediante un proceso de compilación.

**Características del Bytecode en Ethereum**

1. **Compilado desde código de alto nivel**: Los desarrolladores escriben contratos inteligentes en lenguajes de alto nivel diseñados específicamente para Ethereum, como Solidity. Luego, estos contratos son compilados en bytecode para que puedan ser desplegados y ejecutados en la EVM.
2. **Ejecutable por la EVM**: La EVM está diseñada para ejecutar bytecode. Cada nodo de la red Ethereum tiene una instancia de la EVM, lo que permite que los contratos inteligentes se ejecuten de manera descentralizada en toda la red.
3. **Determinista**: El bytecode ejecutado en la EVM produce resultados deterministas. Esto significa que, dadas las mismas entradas y estado de la blockchain, la ejecución del bytecode siempre producirá los mismos resultados. Esta característica es crucial para el consenso y la integridad de la red Ethereum.
4. **Consumo de gas**: La ejecución de operaciones en el bytecode consume "gas", que es una medida del poder computacional requerido. Cada operación en el bytecode tiene un costo de gas asociado, y los usuarios deben pagar este gas para ejecutar operaciones y cambiar el estado de la blockchain.

**Proceso de compilación**

El proceso de compilación transforma el código fuente del contrato inteligente en bytecode. Este proceso generalmente también produce un ABI, que como vimos anteriormente, define cómo llamar a las funciones del contrato, pero el bytecode es el código que se ejecuta realmente en la blockchain.

Imaginemos que tienes un contrato inteligente simple escrito en Solidity que almacena un mensaje. La versión en Solidity podría verse algo así:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract SimpleStorage {
    string public message;

    constructor(string memory initialMessage) {
        message = initialMessage;
    }

    function setMessage(string memory newMessage) public {
        message = newMessage;
    }

    function getMessage() public view returns (string memory) {
        return message;
    }
}
```

Una vez compilado, este contrato se convierte en bytecode, que es una cadena de bytes que representa las instrucciones que la EVM ejecutará. El bytecode para este contrato es la larga secuencia de caracteres hexadecimales que se muestra a continuación, que sería difícil de leer o interpretar para los humanos, pero que la EVM puede ejecutar de manera eficiente.

608060405234801562000010575f80fd5b5060405162000c4838038062000c488339818101604052810190620000369190620001d3565b805f908162000046919062000459565b50506200053d565b5f604051905090565b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b620000af8262000067565b810181811067ffffffffffffffff82111715620000d157620000d062000077565b5b80604052505050565b5f620000e56200004e565b9050620000f38282620000a4565b919050565b5f67ffffffffffffffff82111562000115576200011462000077565b5b620001208262000067565b9050602081019050919050565b5f5b838110156200014c5780820151818401526020810190506200012f565b5f8484015250505050565b5f6200016d6200016784620000f8565b620000da565b9050828152602081018484840111156200018c576200018b62000063565b5b620001998482856200012d565b509392505050565b5f82601f830112620001b857620001b76200005f565b5b8151620001ca84826020860162000157565b91505092915050565b5f60208284031215620001eb57620001ea62000057565b5b5f82015167ffffffffffffffff8111156200020b576200020a6200005b565b5b6200021984828501620001a1565b91505092915050565b5f81519050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f60028204905060018216806200027157607f821691505b6020821081036200028757620002866200022c565b5b50919050565b5f819050815f5260205f209050919050565b5f6020601f8301049050919050565b5f82821b905092915050565b5f60088302620002eb7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82620002ae565b620002f78683620002ae565b95508019841693508086168417925050509392505050565b5f819050919050565b5f819050919050565b5f620003416200033b62000335846200030f565b62000318565b6200030f565b9050919050565b5f819050919050565b6200035c8362000321565b620003746200036b8262000348565b848454620002ba565b825550505050565b5f90565b6200038a6200037c565b6200039781848462000351565b505050565b5b81811015620003be57620003b25f8262000380565b6001810190506200039d565b5050565b601f8211156200040d57620003d7816200028d565b620003e2846200029f565b81016020851015620003f2578190505b6200040a62000401856200029f565b8301826200039c565b50505b505050565b5f82821c905092915050565b5f6200042f5f198460080262000412565b1980831691505092915050565b5f6200044983836200041e565b9150826002028217905092915050565b620004648262000222565b67ffffffffffffffff81111562000480576200047f62000077565b5b6200048c825462000259565b62000499828285620003c2565b5f60209050601f831160018114620004cf575f8415620004ba578287015190505b620004c685826200043c565b86555062000535565b601f198416620004df866200028d565b5f5b828110156200050857848901518255600182019150602085019450602081019050620004e1565b8683101562000528578489015162000524601f8916826200041e565b8355505b6001600288020188555050505b505050505050565b6106fd806200054b5f395ff3fe608060405234801561000f575f80fd5b506004361061003f575f3560e01c8063368b877214610043578063ce6d41de1461005f578063e21f37ce1461007d575b5f80fd5b61005d60048036038101906100589190610314565b61009b565b005b6100676100ad565b60405161007491906103d5565b60405180910390f35b61008561013c565b60405161009291906103d5565b60405180910390f35b805f90816100a991906105f8565b5050565b60605f80546100bb90610422565b80601f01602080910402602001604051908101604052809291908181526020018280546100e790610422565b80156101325780601f1061010957610100808354040283529160200191610132565b820191905f5260205f20905b81548152906001019060200180831161011557829003601f168201915b5050505050905090565b5f805461014890610422565b80601f016020809104026020016040519081016040528092919081815260200182805461017490610422565b80156101bf5780601f10610196576101008083540402835291602001916101bf565b820191905f5260205f20905b8154815290600101906020018083116101a257829003601f168201915b505050505081565b5f604051905090565b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b610226826101e0565b810181811067ffffffffffffffff82111715610245576102446101f0565b5b80604052505050565b5f6102576101c7565b9050610263828261021d565b919050565b5f67ffffffffffffffff821115610282576102816101f0565b5b61028b826101e0565b9050602081019050919050565b828183375f83830152505050565b5f6102b86102b384610268565b61024e565b9050828152602081018484840111156102d4576102d36101dc565b5b6102df848285610298565b509392505050565b5f82601f8301126102fb576102fa6101d8565b5b813561030b8482602086016102a6565b91505092915050565b5f60208284031215610329576103286101d0565b5b5f82013567ffffffffffffffff811115610346576103456101d4565b5b610352848285016102e7565b91505092915050565b5f81519050919050565b5f82825260208201905092915050565b5f5b83811015610392578082015181840152602081019050610377565b5f8484015250505050565b5f6103a78261035b565b6103b18185610365565b93506103c1818560208601610375565b6103ca816101e0565b840191505092915050565b5f6020820190508181035f8301526103ed818461039d565b905092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f600282049050600182168061043957607f821691505b60208210810361044c5761044b6103f5565b5b50919050565b5f819050815f5260205f209050919050565b5f6020601f8301049050919050565b5f82821b905092915050565b5f600883026104ae7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82610473565b6104b88683610473565b95508019841693508086168417925050509392505050565b5f819050919050565b5f819050919050565b5f6104fc6104f76104f2846104d0565b6104d9565b6104d0565b9050919050565b5f819050919050565b610515836104e2565b61052961052182610503565b84845461047f565b825550505050565b5f90565b61053d610531565b61054881848461050c565b505050565b5b8181101561056b576105605f82610535565b60018101905061054e565b5050565b601f8211156105b05761058181610452565b61058a84610464565b81016020851015610599578190505b6105ad6105a585610464565b83018261054d565b50505b505050565b5f82821c905092915050565b5f6105d05f19846008026105b5565b1980831691505092915050565b5f6105e883836105c1565b9150826002028217905092915050565b6106018261035b565b67ffffffffffffffff81111561061a576106196101f0565b5b6106248254610422565b61062f82828561056f565b5f60209050601f831160018114610660575f841561064e578287015190505b61065885826105dd565b8655506106bf565b601f19841661066e86610452565b5f5b8281101561069557848901518255600182019150602085019450602081019050610670565b868310156106b257848901516106ae601f8916826105c1565b8355505b6001600288020188555050505b50505050505056fea2646970667358221220577ca3d9adfba3f05e9828c37d17d5d53f8c4c319bee672d3c5472c50b010b3364736f6c63430008180033


# Opcodes

En el contexto de Ethereum y Solidity, los **opcodes** (código de operación) son instrucciones de bajo nivel que la Ethereum Virtual Machine (EVM) puede ejecutar. Cada opcode representa una operación específica, como realizar cálculos matemáticos, leer y escribir en el almacenamiento, manipular el stack, realizar saltos condicionales e incondicionales, gestionar llamadas a otros contratos, y más. Los opcodes son los elementos fundamentales del bytecode que se ejecuta en la EVM.

Los opcodes de Ethereum se codifican como un byte (2 caracteres hexadecimales). Cada opcode tiene un significado específico. Por ejemplo, el opcode ADD se utiliza para sumar dos valores, el opcode SUB se utiliza para restar dos valores y el opcode EQ se utiliza para comparar dos valores.

Si empezamos a desmenuzar el bytecode de la sección anterior encontraremos al principio los siguientes opcodes:

* **`60`** es el opcode para **`PUSH1`**, lo que indica que el siguiente byte (u octeto) debe ser colocado en el stack.
* **`80`** sería el dato empujado por el **`PUSH1`**.
* **`60`** nuevamente indica otro **`PUSH1`**.
* **`40`** es el dato para este segundo **`PUSH1`**.
* **`52`** corresponde al opcode **`MSTORE`**, que guarda la palabra en memoria.

La EVM tiene un conjunto de 144 opcodes. Los opcodes se dividen en las siguientes categorías:

* **Operaciones aritméticas.** Se utilizan para realizar operaciones básicas, como sumar, restar, multiplicar y dividir.
* **Operaciones lógicas.** Se utilizan para realizar operaciones lógicas, como AND, OR y NOT.
* **Operaciones de comparación.** Se utilizan para comparar dos valores.
* **Operaciones de asignación.** Se utilizan para asignar un valor a una variable.
* **Operaciones de control de flujo.** Se utilizan para controlar el flujo de ejecución de un contrato inteligente.
* **Operaciones de memoria.** Se utilizan para manipular la memoria de la EVM.
* **Operaciones de llamadas.** Se utilizan para llamar a funciones de contratos inteligentes.
* **Operaciones de creación de contratos.** Se utilizan para crear nuevos contratos inteligentes.

**Características de los Opcodes**

* **Bajo Nivel**: Los opcodes operan a un nivel más bajo que el código Solidity. Mientras que los desarrolladores escriben contratos inteligentes en Solidity (u otros lenguajes de alto nivel), estos contratos son compilados a bytecode que consiste en una secuencia de opcodes.
* **Determinismo**: Cada opcode realiza una función específica y determinista, lo que es crucial para asegurar que todas las copias de la EVM en diferentes nodos de la red Ethereum ejecuten el mismo conjunto de instrucciones de manera idéntica y lleguen al mismo estado final.
* **Consumo de Gas**: Ejecutar opcodes en la EVM consume una cantidad específica de gas.

**Ejemplos de Opcodes**

Aquí hay algunos ejemplos de opcodes en Ethereum y sus funciones:

* **ADD, SUB, MUL, DIV**: Realizan operaciones aritméticas básicas como sumar, restar, multiplicar y dividir.
* **PUSH1, PUSH2, ..., PUSH32**: Colocan una constante de 1 a 32 bytes en el stack.
* **CALL**: Realiza una llamada a otro contrato.
* **STORE, LOAD**: Escriben y leen datos del almacenamiento persistente del contrato.
* **JUMP, JUMPI**: Realizan saltos incondicionales y condicionales, respectivamente, lo que permite la ejecución de bucles y condicionales.
* **STOP, RETURN, REVERT**: Controlan la terminación de la ejecución, devolviendo datos o revirtiendo la transacción.

Aunque los desarrolladores de Solidity generalmente no trabajan directamente con opcodes (ya que escriben código en un nivel más alto de abstracción), entender cómo el código Solidity se compila a opcodes puede ser útil para optimizar contratos inteligentes, especialmente en términos de consumo de gas. Además, la seguridad de los contratos inteligentes puede depender de comprender las implicaciones de bajo nivel de las operaciones de la EVM.

Una muy buena referencia para entender el funcionamiento de los opcodes es la página [EVM Opcodes](https://www.evm.codes/) donde puedes ver la relación entre el código de un smart contracts y sus opcodes.

Finalmente veamos la relación entre ABI, bytecode y opcodes.

<figure><img src="/files/yXEsgSNmRkA9W5P806bq" alt=""><figcaption></figcaption></figure>

A través de la compilación se obtiene el ABI y el bytecode.

Cuando se quiere ejecutar alguna función del smart contract que está en la blockchain bajo la forma de bytecode, se llama a esta función utilizando el ABI.

Con la información del ABI se identificará dentro del bytecode la función que luego se descompondrá en sus opcodes que a su vez serán procesados por la EVM.


# Estándares, Librerías y Patrones

**Objetivo:** Profundizar en estándares como ERC-20, ERC-721 y otros. Conocer las principales librerías y buenas prácticas de diseño.

**Duración:** 9 horas (3 clases de 3 horas cada una).

<figure><img src="/files/shJvzWzKkVjfsw6bqEVi" alt=""><figcaption></figcaption></figure>

Toda la información [aquí](https://ethkipu.notion.site/Est-ndares-librer-as-y-patrones-934170ce0a054c8b88d095b08a40df02).

{% hint style="info" %}
Este documento es una herramienta pedagógica que actualizamos constantemente. Creemos en la importancia de contenidos open-source. Si quieres mejorar los contenidos, únete a nuestro programa de [Kipu Explorers](/contribuye/kipu-explorer).
{% endhint %}


# Buenas Prácticas de Diseño

El diseño de contratos inteligentes en Solidity requiere un enfoque cuidadoso para asegurar la seguridad, eficiencia y mantenibilidad del código. Aquí hay algunas buenas prácticas de diseño en Solidity que te ayudarán a desarrollar contratos inteligentes más robustos y fiables:

### 1. Empezar por la seguridad 🔑

* **Implementar control de accesos:** Protege funciones críticas de accesos no autorizados. Implementa un control de acceso basado en roles para asegurar que solo las personas autorizadas puedan ejecutar determinadas funciones.
* **Validar entradas externas**: Siempre valida los inputs de las funciones para prevenir condiciones inesperadas o maliciosas. Implemente mecanismos de validación de entrada adecuados, como verificaciones de rango, verificación de longitud de entrada y validación de tipo de datos. Asegurándose de la validez de las entradas de los usuarios, puede prevenir el acceso no autorizado, la corrupción de datos y otras vulnerabilidades de seguridad.
* **Gestionar los errores correctamente**: Usa **`require`**, **`revert`**, y **`assert`** para manejar condiciones inválidas y asegurar que el contrato se comporte como se espera.
* **Evitar la Reentrancia**: Utiliza el patrón de chequeo-efectos-interacción y considera el uso de modificadores de estado como **`nonReentrant`** para prevenir ataques de reentrancia. Los ataques de reentrada ocurren cuando un contrato llama a otro contrato antes de terminar su propia ejecución, permitiendo que el contrato llamado manipule el estado de formas inesperadas. Para proteger tus contratos contra ataques de reentrada, implementa guardas de reentrada y gestiona cuidadosamente el orden de las operaciones en tus funciones. Además, favorece el enfoque de "pull" sobre el enfoque de "push" al enviar tokens o activos, reduciendo el riesgo de enviar tokens a direcciones incorrectas o caer presa de vulnerabilidades de reentrada.
* **Especificar claramente la visibilidad de funciones y variables de estado:** Usa **`public`, `internal`, `external`** o\*\*`private`\*\* para hacer que tu código sea más comprensible y evitar el acceso no intencionado. Esta práctica mejora la seguridad y la claridad de tu código.

### 2. Minimizar los costos de gas

* **Optimizar el uso de almacenamiento**: Las variables de estado son costosas. Reutiliza el espacio de almacenamiento cuando sea posible y considera agrupar variables para aprovechar el almacenamiento de slots completos. Elige memoria sobre storage cuando sea posible.
* **Limitar el Uso de Loops**: Los loops pueden aumentar significativamente el costo del gas, especialmente si operan sobre estructuras de datos dinámicas. Trata de limitar su uso o diseñar tus contratos de manera que los costos no escalen con el tamaño del conjunto de datos.
* **Proporcionar estimaciones de gas:** Utiliza las estimaciones de Solidity respecto al consumo de gas de las transacciones para informar a los usuarios sobre los costos potenciales antes de ejecutar una función, asegurando transparencia y confianza del usuario.
* **Usar tipos valor en lugar de tipos referencia:** Los tipos valor, como uint256 y bool, son más eficientes en términos de gas que los tipos de referencia como los arrays o structs.
* **Utilizar funciones view y pure:** Declara funciones como \*\*`view`\*\*o **`pure`** siempre que sea posible para indicar que no modifican el estado, reduciendo el consumo de gas.

### 3. Mantener la claridad y la mantenibilidad

* **Seguir una estructura clara**: Organiza tu código de manera lógica, agrupando funciones relacionadas y utilizando comentarios donde sea necesario para explicar el propósito y la lógica del contrato. Esto no solo ayuda a otros desarrolladores a entender tu código, sino que también es útil en auditorías y depuración. Proporcionar una explicación exhaustiva de la lógica, funciones y variables de tu código es una buena práctica para mantener el código.
* **Utilizar nombres descriptivos**: Elige nombres significativos y descriptivos para funciones, variables y contratos para mejorar la legibilidad del código.
* **Evitar la lógica compleja**: Descompone las funciones complejas en funciones más pequeñas y manejables. Esto no solo hace que el código sea más fácil de entender y mantener, sino que también puede ayudar a identificar y reutilizar patrones comunes. “Menos es más”.
* **Limitar el tamaño del contrato:** En lugar de hacer un contrato inteligente demasiado grande es mejor separarlo en contratos más pequeños que interactúen.

### 4. Prepararse para la actualización

* **Diseñar para la actualización:** Considera la posibilidad de usar contratos proxy o patrones similares que permitan actualizar la lógica del contrato sin perder el estado o los datos almacenados. Sin embargo ten en cuenta que esto introduce complejidad y riesgos de seguridad en los contratos.

### 5. Hacer pruebas exhaustivas

* **Prueba tu código**: Realiza pruebas unitarias y de integración que cubran no solo el flujo principal de uso, sino también casos límite y comportamientos inesperados. Escribir pruebas unitarias completas es vital para asegurar que tu contrato inteligente funciona como se espera. Herramientas como Foundry y Hardhat pueden ayudar a automatizar el proceso de prueba, lo que facilita la detección temprana de errores y asegura la fiabilidad del código.
* **Utilizar herramientas de análisis**: Aplica herramientas de análisis estático y dinámico y frameworks de testing como Foundry, Hardhat, o MythX para detectar vulnerabilidades y errores en tu código.
* **Usar herramientas de cobertura de código:** Mida la cobertura de prueba de su código utilizando herramientas como Solidity Coverage para identificar áreas no probadas o insuficientemente probadas.

### 6. Revisión y auditoría

* **Revisiones de código**: Realiza revisiones de código con otros desarrolladores para identificar posibles problemas o mejoras en la lógica, seguridad y eficiencia del contrato.
* **Auditorías de seguridad profesionales**: Antes de desplegar contratos que manejen fondos significativos o tengan una importancia crítica, considera la posibilidad de obtener una auditoría de seguridad profesional.
* **Realizar revisiones de código entre pares:** Haz que otros desarrolladores revisen tu código para identificar posibles problemas, mejorar la legibilidad y proporcionar comentarios.

### 7. Utiliza librerías

* **No reinventes la rueda:** Utiliza librerías como las de Open Zeppelin que ya han sido probadas por mucho tiempo.

### 8. Visibiliza la actividad del contrato

* **Emite eventos:** Registra las actividades clave del contrato utilizando eventos. Los eventos son como una pantalla que muestra todo el proceso y todos los pasos, lo que aumenta la transparencia y por consiguiente la confianza en el proceso.

### 9. Utiliza las convenciones para los nombres y formatos en Solidity

* **Usa camelCase:** Comienza las variables y los nombres de funciones con una letra minúscula y pon en mayúscula la primera letra de cada palabra subsiguiente.
* **Evita las abreviaturas poco claras:** Utiliza palabras completas en lugar de abreviaturas para mantener claridad.
* **Las constantes en mayúsculas:** Las constantes deben escribirse en mayúsculas con guiones bajos separando las palabras (por ejemplo, VALOR\_MAXIMO).
* **Utiliza prefijos en los booleanos:** Coloca prefijos como "is" o "has" en las variables booleanas. Esto ayuda a clarificar su propósito (por ejemplo, isOwner, hasAccess).
* **Utiliza una sangría consistente:** Típicamente, en Solidity se utilizan dos o cuatro espacios para la sangría. Mantén la consistencia en todo tu código.
* **Mantén una longitud de línea consistente:** Limita las líneas a una longitud razonable (por ejemplo, 80 caracteres) para prevenir el desplazamiento horizontal y mejorar la legibilidad.
* **Utiliza el espacio en blanco de manera efectiva:** Añade líneas en blanco entre los bloques lógicos de código para mejorar la separación visual y la claridad.


# Patrones de Diseño

En el desarrollo de contratos inteligentes con Solidity, aplicar patrones de diseño probados puede mejorar significativamente la seguridad, eficiencia y mantenibilidad de tu código. Estos patrones ayudan a resolver problemas comunes en el desarrollo de software y son particularmente importantes en Ethereum, donde los errores pueden tener consecuencias costosas y la optimización del gas es crucial. A continuación, se detallan algunos patrones de diseño importantes en Solidity:

### 1. Patrón de Chequeo-Efectos-Interacción (Checks-Effects-Interactions)

Este patrón ayuda a prevenir ataques de reentrancia al asegurar que las llamadas a contratos externos se realicen al final de una función. Se estructura de la siguiente manera:

* **Chequeo**: Primero, verifica todas las condiciones y valida las entradas.
* **Efectos**: Luego, actualiza el estado del contrato.
* **Interacción**: Finalmente, realiza interacciones con otros contratos.

A continuación un ejemplo de este patrón:

<pre class="language-solidity"><code class="lang-solidity">// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract SafeWithdrawal {
    mapping(address => uint) public balances;
    
<strong>    // Función para depositar Ether en el contrato
</strong>    function deposit() public payable {
        balances[msg.sender] += msg.value;
    }
    
    // Función para retiro, siguiendo el patrón de Chequeo-Efectos-Interacción
    function withdraw(uint _amount) public {
        // Chequeo: Verifica si remitente tiene suficiente balance para retirar
        require(balances[msg.sender] >= _amount, "Saldo insuficiente");
    
        // Efecto: Actualiza el balance antes de la interacción
        balances[msg.sender] -= _amount;
    
        // Interacción: Transfiere Ether al remitente
        (bool sent, ) = msg.sender.call{value: _amount}("");
        require(sent, "Fallo al enviar Ether");
    }
    
    // Función para consultar el balance del contrato
    function getBalance() public view returns (uint) {
        return address(this).balance;
    }
}
</code></pre>

### **2. Patrón de Retiro (Withdrawal Pattern)**

En lugar de enviar Ether directamente a los usuarios (por ejemplo, con **`send`** o **`transfer`**), este patrón permite que los usuarios retiren fondos por sí mismos. Reduce el riesgo de errores y ataques de reentrancia.

```solidity
contract WithdrawalContract {
    mapping(address => uint) public balances;

    function withdraw() public {
        uint amount = balances[msg.sender];
        require(amount > 0);

        balances[msg.sender] = 0;
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success);
    }
}
```

### **3. Patrón de Acceso Restringido (Access Restriction)**

Este patrón se utiliza para restringir el acceso a ciertas funciones del contrato a ciertos usuarios. Es comúnmente implementado a través de modificadores de función.

```solidity
contract RestrictedAccess {
    address public owner = msg.sender;

    modifier onlyOwner() {
        require(msg.sender == owner);
        _;
    }

    function restrictedAction() public onlyOwner {
        // Lógica restringida
    }
}
```

De la misma forma que en este caso se restringe el acceso para que solo el Owner pueda acceder a la función restringida, se puede crear modificadores para otros perfiles como administradores o personas autorizadas.

### **4. Patrón de Parada de Emergencia (Emergency Stop)**

Este patrón también conocido como "Circuit Breaker" permite pausar la ejecución de ciertas funciones críticas en caso de emergencia o cuando se detecta un comportamiento anómalo. Este patrón es particularmente útil para prevenir daños mayores, como la pérdida de fondos o la explotación de vulnerabilidades, hasta que se pueda investigar y resolver el problema.

Para implementar este patrón, se suele utilizar una variable de estado que indica si el contrato está en modo "pausado" o no. Las funciones que podrían ser vulnerables a ataques o fallos se modifican para que su ejecución dependa del estado de esta variable. Además, se implementan funciones para cambiar el estado de esta variable, permitiendo activar o desactivar el "modo de emergencia". Estas funciones de control deben ser restringidas a direcciones autorizadas para evitar mal uso.

A continuación un ejemplo simple de cómo implementarlo:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract EmergencyStopExample {
    address private owner;
    bool private paused;

    modifier onlyOwner() {
        require(msg.sender == owner, "No eres el propietario");
        _;
    }

    modifier whenPaused() {
        require(paused, "El contrato no está pausado");
        _;
    }

    event Paused();
    event Unpaused();
		event FundsWithdrawn(address owner, uint amount);
		
    constructor() {
        owner = msg.sender;
        paused = false;
    }

    function pause() public onlyOwner {
        paused = true;
        emit Paused();
    }

    function unpause() public onlyOwner {
        paused = false;
        emit Unpaused();
    }

    function emergencyWithdraw() public onlyOwner whenPaused {
        // Suponiendo que esta función retira todos los fondos del contrato a la dirección del propietario
        uint balance = address(this).balance;
        (bool sent, ) = owner.call{value: balance}("");
        require(sent, "Fallo al enviar Ether");
        emit FundsWithdrawn(owner, balance);
    }

    // Otras funciones del contrato deben también verificar el estado de `paused` usando el modificador `whenNotPaused`
}
```

#### **Consideraciones:**

* **Autorización:** Es crucial restringir quién puede activar o desactivar el modo de emergencia, generalmente el propietario del contrato o un conjunto de direcciones confiables.
* **Transparencia:** La capacidad de poner el contrato en modo de emergencia debe ser comunicada claramente a los usuarios, explicando en qué circunstancias se utilizará.
* **Recuperación:** Debe haber un plan claro sobre cómo se resolverán los problemas que llevaron a activar el modo de emergencia y cómo se reanudará la normalidad.

### **5. Patrón de Fábrica de Contratos (Factory Pattern)**

Este patrón permite la creación de nuevos contratos desde otro contrato. Este patrón es especialmente útil cuando se necesita crear múltiples instancias de un contrato con configuraciones similares o cuando se desea centralizar la lógica de creación de contratos para facilitar el mantenimiento y la actualización. La "fábrica" actúa como un creador centralizado que puede generar instancias de contratos a petición.

A continuación, se muestra un ejemplo simple de cómo implementar el patrón Factory Contract. En este ejemplo, el contrato **`ChildContract`** será el contrato que la fábrica produce, y **`FactoryContract`** actuará como la fábrica que crea instancias de **`ChildContract`**.

#### **Contrato Hijo**

Primero, definimos el contrato que queremos producir. Este contrato puede ser cualquier cosa, pero para fines de este ejemplo, será un simple contrato que almacena un número.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// Contrato hijo que nuestra fábrica producirá
contract ChildContract {
    uint public number;

    // Constructor que inicializa el contrato con un número específico
    constructor(uint _number) {
        number = _number;
    }
}
```

**Contrato Fábrica**

El contrato fábrica se encargará de crear nuevas instancias del contrato hijo. Mantendrá un registro de todas las instancias creadas para poder interactuar con ellas más tarde si es necesario.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// Referencia al contrato hijo
import "./ChildContract.sol";

contract FactoryContract {
    // Array para almacenar las direcciones de los contratos hijos creados
    ChildContract[] public children;

    event ChildCreated(uint number, address childAddress);

    // Función para crear un nuevo contrato hijo
    function createChild(uint _number) public {
        ChildContract child = new ChildContract(_number);
        children.push(child);
        emit ChildCreated(_number, address(child));
    }

    // Función para obtener la dirección de un contrato hijo en el array
    function getChild(uint _index) public view returns (ChildContract) {
        require(_index < children.length, "Índice fuera de límites");
        return children[_index];
    }
}
```

En este ejemplo, **`FactoryContract`** tiene una función **`createChild`** que despliega una nueva instancia de **`ChildContract`** con un número específico. Cada nueva instancia de **`ChildContract`** se almacena en el array **`children`**, y se emite un evento **`ChildCreated`** que registra el número asignado y la dirección del nuevo contrato hijo.

Este patrón es poderoso porque permite la creación de contratos de manera programática, lo que facilita la gestión de múltiples instancias de contratos y la centralización de la lógica de creación de contratos. Además, al tener un registro de todos los contratos creados, el contrato fábrica puede interactuar con estos o proporcionar funcionalidades adicionales como la gestión o actualización de los contratos hijos.

### **6. Patrón de Máquina de Estados (State Machine)**

Este patrón es una forma eficaz de gestionar el ciclo de vida de un contrato, modelando explícitamente los diferentes estados por los que puede pasar un contrato y las transiciones permitidas entre estos estados. Este patrón es particularmente útil para contratos con lógicas de negocio complejas que necesitan manejar diferentes fases o etapas de manera clara y segura, como contratos de votación, de subastas, o de crowdfunding.

#### **Implementación básica del patrón State Machine**

Para implementar una máquina de estados en un contrato inteligente, puedes seguir estos pasos:

1. **Definir los estados:** Utiliza un **`enum`** para definir todos los posibles estados del contrato.
2. **Almacenar el estado actual:** Utiliza una variable para almacenar el estado actual del contrato.
3. **Restringir funciones a estados específicos:** Usa modificadores para permitir que ciertas funciones se ejecuten solo en estados específicos.
4. **Cambiar de estado:** Implementa funciones que cambien el estado del contrato de manera controlada.

Veamos un ejemplo simplificado de un contrato de subasta que utiliza el patrón State Machine:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract AuctionStateMachine {
    enum Stages {
        AcceptingBlindBids,
        RevealBids,
        Finished
    }

    // Variable para almacenar el estado actual del contrato
    Stages public stage = Stages.AcceptingBlindBids;

    // Variable para almacenar el tiempo en el que el contrato avanza al siguiente estado
    uint public nextStageTime = block.timestamp + 1 days;

    // Modificador para controlar el acceso a funciones según el estado actual
    modifier atStage(Stages _stage) {
        require(stage == _stage, "Función no permitida en el estado actual.");
        _;
    }

    // Modificador para avanzar al siguiente estado basado en el tiempo
    modifier transitionNext() {
        if (block.timestamp >= nextStageTime) {
            nextStage();
        }
        _;
    }

    // Función para avanzar al siguiente estado
    function nextStage() internal {
        stage = Stages(uint(stage) + 1);
        nextStageTime = block.timestamp + 1 days;
    }

    // Ejemplo de función que puede ser llamada en un estado específico
    function acceptBlindBid() public atStage(Stages.AcceptingBlindBids) transitionNext {
        // Lógica para aceptar una puja a ciegas
    }

    function revealBids() public atStage(Stages.RevealBids) transitionNext {
        // Lógica para revelar las pujas
    }

    // Lógica para finalizar la subasta, se puede implementar en el cambio a `Finished`
}
```

En este contrato de subasta, hay tres estados definidos: **`AcceptingBlindBids`** (aceptando pujas a ciegas), **`RevealBids`** (revelando pujas), y **`Finished`** (finalizada). El contrato comienza en el estado **`AcceptingBlindBids`** y avanza secuencialmente a través de los estados basado en el paso del tiempo (cada día avanza al siguiente estado). Se utilizan modificadores para asegurar que ciertas funciones solo puedan ejecutarse en sus respectivos estados, y hay una función **`nextStage`** que gestiona la transición entre estados.

#### **Beneficios del patrón State Machine**

* **Claridad:** Facilita la comprensión del flujo lógico del contrato y sus distintas fases.
* **Seguridad:** Ayuda a prevenir ejecuciones no autorizadas de funciones y asegura que el contrato solo realice operaciones válidas en su estado actual.
* **Flexibilidad:** Permite gestionar complejidades en contratos con múltiples fases o etapas de manera estructurada.

### **7. Patrón para obtener datos de un oráculo**

El uso de oráculos en contratos inteligentes de Ethereum permite acceder a datos externos a la blockchain, lo cual es esencial para muchas aplicaciones descentralizadas (DApps) que necesitan interactuar con el mundo real. Sin embargo, Ethereum y otras blockchains similares no pueden acceder directamente a datos externos debido a su naturaleza aislada y determinista. Aquí es donde entran en juego los oráculos, actuando como intermediarios que traen información del exterior hacia la blockchain.

Un oráculo es un servicio (generalmente operado por un tercero) que envía datos del mundo real a la blockchain. Estos datos pueden ser cualquier cosa, desde precios de activos y resultados deportivos hasta la temperatura de una ciudad. Los oráculos juegan un papel crucial en el funcionamiento de muchos tipos de DApps, como las financieras (DeFi), las de seguros, y las de juegos de azar, entre otras.

#### **Implementación Básica**

La implementación de un oráculo en Solidity generalmente sigue estos pasos:

1. **Solicitud de Datos:** El contrato inteligente envía una solicitud de datos al oráculo. Esta solicitud puede ser el resultado de una acción del usuario o de otra función del contrato.
2. **Obtención de Datos:** El oráculo recibe la solicitud, obtiene los datos necesarios del mundo real y los envía de vuelta a la blockchain.
3. **Procesamiento de Datos:** El contrato inteligente recibe los datos y ejecuta la lógica correspondiente con esta información.

#### **Ejemplo con Chainlink**

Chainlink es uno de los servicios de oráculo más populares y ampliamente utilizados en el ecosistema Ethereum. A continuación, se muestra un ejemplo básico de cómo un contrato inteligente puede interactuar con Chainlink para obtener el precio de ETH en USD.

Primero, asegúrate de tener importadas las dependencias de Chainlink en tu archivo Solidity, lo cual generalmente se hace a través de importaciones de NPM o directamente con URLs.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";

contract PriceConsumerV3 {

    AggregatorV3Interface internal priceFeed;

    /**
     * Network: Ethereum Mainnet
     * Aggregator: ETH/USD
     * Address: 0x... (Dirección del contrato de Chainlink para ETH/USD)
     */
    constructor() {
        priceFeed = AggregatorV3Interface(0x...); // Dirección del oráculo de Chainlink para ETH/USD
    }

    /**
     * Retorna el precio más reciente de ETH/USD
     */
    function getLatestPrice() public view returns (int) {
        (
            /* uint80 roundID */,
            int price,
            /* uint startedAt */,
            /* uint timeStamp */,
            /* uint80 answeredInRound */
        ) = priceFeed.latestRoundData();
        return price; // El precio se devuelve con 8 decimales
    }
}
```

En este ejemplo, el contrato **`PriceConsumerV3`** utiliza la interfaz **`AggregatorV3Interface`** de Chainlink para interactuar con un contrato oráculo de Chainlink que proporciona el precio actual de ETH en USD. La función **`getLatestPrice`** consulta el último dato de precio disponible y lo devuelve.

Cuando se trabaja con oráculos, es crucial tener en cuenta la confiabilidad y la seguridad del proveedor de datos. Dependiendo de un único oráculo puede introducir un punto de fallo centralizado. Para mitigar esto, algunas aplicaciones utilizan múltiples oráculos o servicios de oráculos descentralizados como Chainlink, que agregan datos de varias fuentes para proporcionar una medida más confiable y resistente a la manipulación.

### **8. Patrón de Actualización de Contratos (Upgradeable Contracts)**

Este patrón comúnmente conocido como el patrón "Proxy" o "Upgradeable Contracts", es una técnica avanzada que permite a los desarrolladores cambiar el código de un contrato inteligente después de haber sido desplegado en la blockchain. Dado que el código de un contrato inteligente es inmutable una vez desplegado, este patrón proporciona una forma flexible de actualizar la lógica del contrato sin perder el estado o los datos almacenados, ni cambiar la dirección del contrato.

#### **Cómo Funciona el Patrón Proxy**

El patrón se basa en dos componentes principales: el contrato Proxy y el contrato de Implementación (o lógica).

* **Contrato Proxy:** Es el contrato que los usuarios interactúan directamente. Mantiene el estado del contrato y delega llamadas a un contrato de implementación que contiene la lógica del negocio. La dirección del contrato proxy permanece constante, incluso a través de las actualizaciones.
* **Contrato de Implementación:** Contiene la lógica del negocio y el código que puede ser actualizado. Cuando se actualiza la lógica del contrato, se despliega un nuevo contrato de implementación, y el proxy es actualizado para apuntar a la nueva dirección.

A continuación, se muestra un ejemplo simplificado de cómo se podría implementar un patrón Proxy.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// Contrato de Implementación
contract LogicContractV1 {
    uint public counter;

    function increment() public {
        counter += 1;
    }
}

// Contrato Proxy
contract Proxy {
    address public implementation;

    constructor(address _logic) {
        implementation = _logic;
    }

    function upgrade(address _newImplementation) external {
        implementation = _newImplementation;
    }

    fallback() external payable {
        address _impl = implementation;
        require(_impl != address(0));
        assembly {
            let ptr := mload(0x40)
            calldatacopy(ptr, 0, calldatasize())
            let result := delegatecall(gas(), _impl, ptr, calldatasize(), 0, 0)
            let size := returndatasize()
            returndatacopy(ptr, 0, size)
            switch result
            case 0 { revert(ptr, size) }
            default { return(ptr, size) }
        }
    }
}
```

En este ejemplo, el **`Proxy`** delega todas las llamadas al **`LogicContractV1`**. Si queremos actualizar la lógica, desplegaríamos un nuevo contrato de implementación (**`LogicContractV2`**, por ejemplo) y luego llamaríamos a **`upgrade`** en el **`Proxy`** para cambiar la dirección de la implementación.

#### **Consideraciones de Seguridad**

* **Almacenamiento consistente:** Es crucial asegurarse de que la estructura del almacenamiento permanezca consistente entre las versiones de los contratos de implementación para evitar problemas de corrupción de datos.
* **Transparencia de las actualizaciones:** Las actualizaciones deben ser manejadas con cuidado para mantener la confianza de los usuarios, idealmente mediante un proceso de gobernanza claro o un período de tiempo de aviso antes de realizar cambios significativos.
* **Control de acceso:** El contrato Proxy debe tener controles de acceso robustos para asegurar que solo las entidades autorizadas puedan actualizar el contrato de implementación.

### **9. Patrón de Mapa Iterable**

Implementar un mapa iterable en Solidity permite combinar las ventajas de los mapas (acceso rápido a los datos por clave) con las de los arrays (capacidad de iterar sobre los elementos). Dado que Solidity no ofrece de forma nativa una estructura de datos que sea al mismo tiempo un mapa y que permita la iteración sobre sus elementos, se debe diseñar una estructura que cumpla con ambos requisitos.

A continuación veamos una implementación detallada del patrón de mapa iterable, el cual se compone de un mapa para almacenar los datos y un array para mantener el orden de las claves y permitir la iteración.

#### **Implementación**

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract IterableMapping {
    // Estructura para almacenar los datos junto con un flag para marcar si el elemento está en el array
    struct MapData {
        uint value;
        bool exists;
    }

    // Mapa para almacenar los datos asociados a una clave
    mapping(address => MapData) public dataMap;

    // Array para mantener el orden de las claves
    address[] public keys;

    // Función para añadir o actualizar un valor
    function set(address key, uint value) public {
       // Si es la primera vez que se añade, también se agrega la clave al array
        if (!dataMap[key].exists) {
            keys.push(key);
            dataMap[key] = MapData(value, true);
        } else {
            // Si ya existía, solo actualizamos el valor
            dataMap[key].value = value;
        }
    }

    // Función para obtener un valor
    function get(address key) public view returns (uint) {
        require(dataMap[key].exists, "La clave no existe");
        return dataMap[key].value;
    }

    // Función para eliminar un elemento
    function remove(address key) public {
        require(dataMap[key].exists, "La clave no existe");

        // Eliminar la clave del mapa
        delete dataMap[key];

        // Encontrar el índice de la clave en el array y eliminarlo
        for (uint i = 0; i < keys.length; i++) {
            if (keys[i] == key) {
                // Mover el último elemento al lugar del elemento eliminado y luego eliminar el último elemento
                keys[i] = keys[keys.length - 1];
                keys.pop();
                break;
            }
        }
    }

    // Función para obtener el tamaño del mapa
    function size() public view returns (uint) {
        return keys.length;
    }

    // Función para comprobar si una clave existe en el mapa
    function exists(address key) public view returns (bool) {
        return dataMap[key].exists;
    }

    // Función para obtener una clave en un índice específico
    function getKeyAtIndex(uint index) public view returns (address) {
        require(index < keys.length, "Índice fuera de rango");
        return keys[index];
    }
}
```

#### **Características y consideraciones**

* **Flexibilidad:** Este patrón permite tanto la recuperación de valores por clave como la iteración sobre todas las entradas.
* **Gestión de estado:** Se mantiene la consistencia entre el mapa y el array, asegurando que las operaciones de añadir, actualizar y eliminar se reflejen en ambos.
* **Eficiencia de Gas:** La eliminación y la adición de elementos, especialmente en un conjunto grande, pueden consumir una cantidad significativa de gas debido a las operaciones sobre el array. La eficiencia debe ser considerada al diseñar el contrato, especialmente para operaciones que modifican el array.

Este patrón es especialmente útil en situaciones donde se necesita tanto un acceso rápido a los datos mediante claves como la capacidad de iterar sobre todas las entradas almacenadas.

### **10. Patrón de Lista de Direcciones**

Este patrón se utiliza para mantener una lista curada de direcciones por el propietario. Esto se requiere por ejemplo cuando se necesita una lista de direcciones en una whitelist que estén autorizadas para ejecutar determinadas funciones en un contrato. En este patrón, solo el dueño del contrato puede añadir y retirar direcciones de la lista.

Veamos un ejemplo.

```solidity
contract AddressList is Ownable {
		 mapping(address => bool) internal map;
		 function add(address _address) public onlyOwner {
		        map[_address] = true;
		    }

		 function remove(address _address) public onlyOwner {
		        map[_address] = false;
		    }

		 function isExists(address _address) public view returns (bool) {
		        return map[_address];
    }
}
```

### **11. Patrón de Comparación de Strings**

En Solidity, los strings son un tipo de dato especial que representa secuencias de caracteres. Sin embargo, Solidity no proporciona una forma directa de comparar strings debido a su naturaleza de bajo nivel y su enfoque en la eficiencia de gas. Comparar strings implica comparar el contenido de los datos en lugar de las direcciones de memoria, lo cual requiere una lógica específica dada la forma en que se almacenan los strings en la EVM.

Una forma común de comparar strings en Solidity es convertirlos primero a **`bytes`** y luego comparar estos **`bytes`** usando **`keccak256`**. Si dos strings son idénticos, sus hashes **`keccak256`** también serán idénticos. Este método es eficiente y efectivo para la comparación de strings en contratos inteligentes.

A continuación, se muestra cómo implementar la comparación de strings usando **`keccak256`**:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract StringComparison {
    // Función para comparar dos strings
    function compareStrings(string memory string1, string memory string2) public pure returns (bool) {
        return keccak256(abi.encodePacked(string1)) == keccak256(abi.encodePacked(string2));
    }
}
```

* **`abi.encodePacked(...)`**: Esta función toma cualquier número de argumentos de cualquier tipo y los codifica en un único **`bytes`** continuo. Se usa aquí para convertir los strings en **`bytes`**.
* **`keccak256(...)`**: Calcula el hash KECCAK-256 de los datos. En este contexto, se usa para obtener el hash del **`bytes`** resultante de cada string.
* **`==`**: Compara los hashes de los dos strings. Si los strings son iguales, sus hashes también lo serán, resultando en **`true`**. De lo contrario, el resultado será **`false`**.

#### **Consideraciones**

* **Eficiencia de Gas**: Usar **`keccak256`** para comparar strings es generalmente eficiente en términos de gas, especialmente comparado con métodos que implicarían iterar sobre cada carácter de los strings.
* **Colisiones**: Teóricamente, las funciones hash pueden tener colisiones (diferentes entradas produciendo el mismo hash), aunque la probabilidad de esto es extremadamente baja con **`keccak256`**.
* **Limitaciones**: Este método compara el contenido completo de los strings. Si necesitas realizar comparaciones más complejas (como comprobaciones de prefijo o patrones), necesitarás una lógica adicional.


# EIP y ERC

### **EIP: Ethereum Improvement Proposal**

Un EIP, o Propuesta de Mejora de Ethereum, es un documento de diseño que proporciona información a la comunidad de Ethereum o describe una nueva característica, procesos o cambios en el entorno. Los EIPs son el principal mecanismo para proponer nuevas características, recoger comentarios de la comunidad y documentar los cambios en Ethereum de una manera formal. Los EIPs cubren cambios técnicos como modificaciones en el protocolo core de Ethereum, estándares para contratos inteligentes, y cambios en las prácticas de desarrollo.

Los EIPs se dividen en tres categorías principales:

1. **EIPs Estándar:** Propuestas que afectan de manera directa el protocolo de Ethereum, incluyendo cambios en el funcionamiento de la blockchain y estándares para contratos inteligentes (como los tokens ERC).
2. **EIPs Informativos:** Documentos que proporcionan directrices generales o información a la comunidad de Ethereum, pero no proponen cambios en el protocolo per se.
3. **EIPs Meta:** Propuestas que describen procesos o cambios que no necesariamente son técnicos, como procedimientos y pautas para la gobernanza de Ethereum.

Algunos ejemplo de EIP recientes de alto impacto son los siguientes:

* **EIP-1559:** Propone una reforma significativa del mecanismo de tarifas en Ethereum, introduciendo un precio base por gas y quemando parte del ether utilizado en las tarifas de transacción. Este cambio busca mejorar la predictibilidad de las tarifas y reducir su volatilidad. Ha modificado cómo los usuarios pagan por las transacciones y cómo se calculan las tarifas, además de introducir un mecanismo de quema de ETH que afecta la emisión y, potencialmente, el valor de ETH a largo plazo (Ultra Sound Money).
* **EIP-4844:** Conocido comúnmente como "Proto-Danksharding", es una propuesta que tiene como objetivo mejorar la escalabilidad de Ethereum mediante la introducción de blobs de datos. Estos blobs son grandes bloques de datos que se pueden añadir a la blockchain sin requerir el mismo nivel de procesamiento por parte de los nodos, como sería necesario para los datos de transacción normales. Proto-Danksharding está diseñado para ser un paso intermedio hacia la implementación completa del Danksharding. Su implementación pretende aliviar los cuellos de botella de escalabilidad , haciendo a Ethereum más escalable y reduciendo los costos de transacción en las L2s.
* **EIP-4833:** Propone una solución de Account Abstraction que permite una mayor flexibilidad en la gestión de cuentas en Ethereum. La abstracción de cuentas es un concepto que busca hacer que las cuentas controladas por contratos inteligentes (cuentas de contrato) y las cuentas externamente controladas (EOA) sean más interoperables y flexibles. Básicamente, permite que las cuentas de usuario se comporten más como contratos inteligentes, habilitando capacidades como la autorización de transacciones mediante reglas de contrato inteligente, pago de tarifas de gas en tokens diferentes a ETH, y la creación de sistemas de recuperación de cuentas más robustos.

### **ERC: Ethereum Request for Comments**

Un ERC, o Solicitud de Comentarios de Ethereum, es un subconjunto de los EIPs que se enfoca en definir nuevos estándares, principalmente para contratos inteligentes. Los ERCs establecen reglas claras y específicas que deben seguir los contratos inteligentes para asegurar la interoperabilidad entre aplicaciones en el ecosistema Ethereum. Esto incluye, por ejemplo, estándares para tokens fungibles y no fungibles, mecanismos de intercambio, y más.

Algunos de los ERCs más conocidos incluyen:

* **ERC-20:** El estándar de facto para tokens fungibles en Ethereum, permitiendo la interoperabilidad entre tokens. Define un conjunto común de reglas para los tokens dentro de contratos inteligentes, facilitando su implementación y compatibilidad.
* **ERC-721:** Un estándar para tokens no fungibles (NFTs), permitiendo la representación de activos únicos y la propiedad en la blockchain.
* **ERC-1155:** Un estándar más avanzado que permite la creación tanto de tokens fungibles como no fungibles dentro del mismo contrato, optimizando las transacciones y el almacenamiento de datos.

Los ERCs son esenciales para el desarrollo de aplicaciones descentralizadas (DApps) en Ethereum, proporcionando un marco estandarizado que asegura la compatibilidad y la funcionalidad a través de diferentes proyectos y plataformas.


# ERC-20

El estándar ERC-20 es un estándar de tokens en la blockchain de Ethereum que define un conjunto común de reglas para los tokens emitidos mediante contratos inteligentes. Los tokens ERC-20 son activos digitales que pueden representar cualquier cosa, desde monedas virtuales hasta derechos de voto, pasando por certificados de propiedad y más. Este estándar permite la interoperabilidad entre diferentes aplicaciones y contratos en el ecosistema Ethereum, facilitando a los desarrolladores la implementación de tokens que funcionarán de manera consistente en una amplia gama de servicios y billeteras.

Un contrato inteligente ERC-20 debe implementar las siguientes funciones y eventos:

### **Funciones Requeridas:**

* **`totalSupply()`**: Devuelve la cantidad total de tokens existentes.
* **`balanceOf(address account)`**: Muestra la cantidad de tokens que tiene una dirección específica.
* **`transfer(address recipient, uint256 amount)`**: Permite a una dirección enviar una cantidad de tokens a otra.
* **`allowance(address owner, address spender)`**: Muestra la cantidad de tokens que el dueño permite a otra dirección gastar en su nombre.
* **`approve(address spender, uint256 amount)`**: Permite a un gastador retirar tokens hasta una cantidad máxima.
* **`transferFrom(address sender, address recipient, uint256 amount)`**: Permite a un gastador transferir tokens en nombre de otra dirección, dentro del límite establecido por **`approve`**.

### **Eventos Requeridos:**

* **`Transfer(address indexed from, address indexed to, uint256 value)`**: Debe emitirse cuando los tokens pasen de una dirección a otra.
* **`Approval(address indexed owner, address indexed spender, uint256 value)`**: Debe emitirse cuando una dirección aprueba a otra para gastar tokens en su nombre.

Aquí tienes un ejemplo simplificado de un contrato ERC-20:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract SimpleERC20Token {
    string public constant name = "SimpleERC20Token";
    string public constant symbol = "SET";
    uint8 public constant decimals = 18;

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);

    mapping(address => uint256) balances;
    mapping(address => mapping(address => uint256)) allowed;
    uint256 totalSupply_;

    constructor(uint256 total) {
        totalSupply_ = total;
        balances[msg.sender] = totalSupply_;
    }

    function totalSupply() public view returns (uint256) {
        return totalSupply_;
    }

    function balanceOf(address tokenOwner) public view returns (uint256) {
        return balances[tokenOwner];
    }

    function transfer(address receiver, uint256 numTokens) public returns (bool) {
        require(numTokens <= balances[msg.sender]);
        balances[msg.sender] = balances[msg.sender] - numTokens;
        balances[receiver] = balances[receiver] + numTokens;
        emit Transfer(msg.sender, receiver, numTokens);
        return true;
    }

    function approve(address delegate, uint256 numTokens) public returns (bool) {
        allowed[msg.sender][delegate] = numTokens;
        emit Approval(msg.sender, delegate, numTokens);
        return true;
    }

    function allowance(address owner, address delegate) public view returns (uint) {
        return allowed[owner][delegate];
    }

    function transferFrom(address owner, address buyer, uint256 numTokens) public returns (bool) {
        require(numTokens <= balances[owner]);
        require(numTokens <= allowed[owner][msg.sender]);

        balances[owner] = balances[owner] - numTokens;
        allowed[owner][msg.sender] = allowed[owner][msg.sender] - numTokens;
        balances[buyer] = balances[buyer] + numTokens;
        emit Transfer(owner, buyer, numTokens);
        return true;
    }
}
```

También puedes ver la implementación de Open Zeppelin en: <https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol>

<figure><img src="/files/OXOdiQhYcCDj0esUEzkY" alt=""><figcaption></figcaption></figure>


# ERC-721

El estándar ERC-721, conocido como el estándar para tokens no fungibles (NFTs) en Ethereum, introduce un mecanismo para representar la propiedad y la transferencia de activos únicos. A diferencia de los tokens ERC-20, que son fungibles y cada token es idéntico a los demás, cada token ERC-721 es único, permitiendo así la representación digital de activos únicos y la propiedad de estos en la blockchain.

### **Características Clave del ERC-721**

* **Unicidad:** Cada token tiene un identificador único (a menudo llamado "tokenId") que lo distingue de todos los demás tokens dentro del mismo contrato.
* **Propiedad:** El contrato lleva un registro de quién es el propietario de cada token, permitiendo la propiedad digital verificable de activos únicos.
* **Transferibilidad:** Los tokens pueden ser transferidos entre cuentas, lo que permite el comercio, la venta y la transferencia de propiedad de activos digitales únicos.
* **Interoperabilidad:** El estándar asegura que los tokens NFT sean interoperables con el ecosistema más amplio de Ethereum, incluyendo mercados y otras plataformas que admiten el estándar ERC-721.

El estándar ERC-721 define un conjunto de funciones y eventos que un contrato inteligente debe implementar para ser compatible con ERC-721:

### **Funciones Requeridas:**

* **`balanceOf(address _owner)`**: Devuelve el número de tokens que posee una dirección.
* **`ownerOf(uint256 _tokenId)`**: Devuelve el propietario de un token específico.
* **`transferFrom(address _from, address _to, uint256 _tokenId)`**: Transfiere la propiedad de un token de una dirección a otra.
* **`approve(address _approved, uint256 _tokenId)`**: Permite a una dirección transferir un token específico en nombre del propietario.
* **`getApproved(uint256 _tokenId)`**: Devuelve la dirección autorizada para transferir un token específico.
* **`setApprovalForAll(address _operator, bool _approved)`**: Permite o prohíbe a un operador gestionar todos los tokens de un propietario.
* **`isApprovedForAll(address _owner, address _operator)`**: Consulta si un operador está autorizado a gestionar todos los tokens de un propietario.

### **Eventos Requeridos:**

* **`Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId)`**: Emitido cuando se transfiere la propiedad de un token.
* **`Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId)`**: Emitido cuando se autoriza a una dirección para transferir un token específico.
* **`ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved)`**: Emitido cuando se autoriza o revoca la autorización a un operador para gestionar todos los tokens de un propietario.

<figure><img src="/files/JkAZPlIldk93bhmEFc5G" alt=""><figcaption></figcaption></figure>

### **Implementación y Uso**

Los tokens ERC-721 han ganado popularidad en aplicaciones que requieren la representación de activos únicos y la propiedad verificable en la blockchain, como arte digital, coleccionables, bienes inmuebles virtuales y más. La implementación del estándar ERC-721 permite a los desarrolladores crear contratos inteligentes que gestionan la propiedad, transferencia y acceso a activos digitales únicos, facilitando la creación de un mercado vibrante y diverso de NFTs en Ethereum.


# Open Zeppelin

OpenZeppelin es un proyecto de código abierto que ofrece una suite de herramientas seguras y reutilizables para el desarrollo de contratos inteligentes en la blockchain de Ethereum y otras blockchains compatibles con la EVM. Es ampliamente reconocido y utilizado por desarrolladores de todo el mundo para construir, desplegar y operar aplicaciones descentralizadas (DApps) y tokens digitales, incluyendo tokens fungibles (como los que siguen el estándar ERC-20) y no fungibles (NFTs, siguiendo el estándar ERC-721 y ERC-1155).

### **Principales Características de las Librerías de OpenZeppelin**

* **Seguridad:** Las librerías de OpenZeppelin son revisadas y auditadas minuciosamente por la comunidad y profesionales en seguridad blockchain, lo que ayuda a prevenir errores comunes y vulnerabilidades de seguridad en los contratos inteligentes.
* **Reusabilidad:** Proporcionan módulos y contratos inteligentes predefinidos que cubren funcionalidades comunes y patrones de diseño en el desarrollo de DApps y tokens, como la emisión de tokens, votaciones, y gestión de acceso.
* **Comunidad:** OpenZeppelin tiene una comunidad activa y en crecimiento, lo que garantiza que las herramientas estén constantemente actualizadas y mejoradas basándose en las necesidades y retroalimentación de sus usuarios.

### **Componentes de OpenZeppelin**

### **1. Tokens**

* **ERC20:** Implementaciones estándar del popular token fungible ERC-20, incluyendo versiones básicas, detalladas, minteables, quemables, con capacidades de pausa y más.
* **ERC721:** Implementaciones para tokens no fungibles (NFTs), siguiendo el estándar ERC-721, con soporte para enumeración, metadata extendida, y transferencias seguras.
* **ERC1155:** Un contrato más avanzado que soporta la creación de tokens tanto fungibles como no fungibles dentro de un único contrato, optimizando la gestión y transferencia de múltiples tipos de tokens.

### **2. Acceso**

* **Ownable:** Un contrato simple para proporcionar un control básico de acceso basado en un único dueño, útil para operaciones administrativas.
* **AccessControl:** Un sistema de control de acceso más flexible y completo que permite definir roles y permisos complejos para diferentes acciones dentro de tu contrato.

### **3. Seguridad**

* **Pausable:** Permite pausar y despausar ciertas funcionalidades del contrato, útil para detener operaciones en caso de emergencia o mantenimiento.
* **ReentrancyGuard:** Ayuda a prevenir ataques de reentrancia, uno de los vectores de ataque más comunes, mediante el uso de un modificador simple.

### **4. Gobernanza**

* **Governor:** Es el corazón del sistema de gobernanza de OpenZeppelin. Proporciona una base sólida sobre la cual se pueden construir mecanismos de votación y gobernanza, ofreciendo funcionalidades como la creación y gestión de propuestas, votación, y ejecución de decisiones aprobadas. Este contrato puede ser extendido y personalizado para adaptarse a las necesidades específicas de diferentes proyectos.
* **Timelock:** Añade un elemento de seguridad y transparencia al proceso de gobernanza al introducir un período de bloqueo entre la aprobación de una propuesta y su ejecución. Esto da tiempo a los participantes para revisar las decisiones aprobadas y, si es necesario, actuar en consecuencia antes de que se implementen cambios significativos.
* **Votes:** Proporciona funcionalidades para el conteo y delegación de votos, permitiendo a los titulares de tokens participar en el proceso de gobernanza. Esto incluye soporte para tokens con capacidades de voto, como los tokens ERC20 con extensiones que permiten la votación.
* **Compounded Votes:** Permite a los votantes acumular poder de voto a lo largo del tiempo o en función de otros factores, añadiendo una dimensión dinámica y estratégica al proceso de votación.

### **5. Finanzas**

* **PaymentSplitter:** Permite dividir los ingresos de ether entre múltiples cuentas, útil para dividir pagos o ingresos de forma transparente y segura.
* **Escrow:** Un contrato de depósito en garantía básico que puede ser extendido para crear mecanismos de pago seguros y confiables entre partes.

### **6. Utilidades**

* **Counters:** Proporciona una manera segura de incrementar y decrementar contadores, útil para llevar un registro de IDs de tokens, cantidades de elementos, entre otros.
* **Address:** Ofrece funciones para realizar operaciones seguras con direcciones de Ethereum, incluyendo la verificación de que una dirección es un contrato.
* **MerkleProof:** Se usa para verificar si un elemento pertenece a un árbol de Merkle dado una raíz y una prueba (conjunto de hashes).
* **EnumerableMap:** Cuando se necesita un mapping cuyos elementos puedan ser listados o contados, como el seguimiento de un conjunto de elementos únicos con identificadores.
* **SafeCast:** Cuando se requiere cambiar el tipo de una variable numérica a otro tipo con un tamaño menor de almacenamiento, asegurando que el valor se ajusta al nuevo tipo sin causar errores.

### **7. Proxies**

* **TransparentProxy y UUPS:** Ofrecen infraestructuras para desplegar y administrar contratos inteligentes que pueden ser actualizados, permitiendo cambiar la lógica de un contrato sin cambiar la dirección del contrato.

Esta lista es solo una referencia de los contratos disponibles en Open Zeppelin. Para revisar la lista completa ingresar a: <https://docs.openzeppelin.com/contracts/5.x/> sección API.

### **Cómo Usar OpenZeppelin**

Para utilizar las librerías de OpenZeppelin en tu proyecto, primero debes instalarlas. Si estás utilizando un entorno de desarrollo basado en Node.js, puedes instalarlas fácilmente a través de npm o yarn. Aquí tienes un ejemplo de cómo instalar la librería de contratos OpenZeppelin mediante npm:

```bash
npm install @openzeppelin/contracts
```

Una vez instalada, puedes importar y utilizar los contratos y módulos de OpenZeppelin en tus contratos inteligentes. Por ejemplo, para crear un token ERC-20:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MyToken is ERC20 {
    constructor() ERC20("MyToken", "MTK") {
        _mint(msg.sender, 1000000 * (10 ** uint256(decimals())));
    }
}
```

Desde Remix también puedes acceder a las librerías directamente utilizando la URL de GitHub. Para ello debes incluir una directiva de importación en la parte superior de tu archivo de contrato Solidity que especifique la ruta de la biblioteca en el repositorio de GitHub de OpenZeppelin.

Por ejemplo, para importar la implementación ERC-20 de OpenZeppelin, tu directiva de importación se vería así:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "<https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol>";

contract MyToken is ERC20 {
    constructor() ERC20("MyToken", "MTK") {
        _mint(msg.sender, 1000 * (10 ** uint256(decimals())));
    }
}
```

Asegúrate de que la versión de Solidity especificada en tu contrato sea compatible con la versión utilizada por los contratos de OpenZeppelin que estás importando.

Una forma más sencilla de obtener los contratos ERC20 desde Remix es directamente desde el botón de la pantalla principal de Remix.

<figure><img src="/files/G8qpkh2bLXcq2EuBUqY7" alt=""><figcaption></figcaption></figure>

Para conocer en detalle los contratos y utilidades disponibles en Open Zeppelin te sugerimos revisar [su GitHub](https://github.com/OpenZeppelin). También puedes revisar [este video](https://www.youtube.com/live/mP4jJ8SwtqU?si=fwdxx7A9OwMz8QlP) explicativo en español.


# Crea un Token ERC-20

Para crear un token ERC-20 utilizando la librería de OpenZeppelin en Solidity, debes seguir algunos pasos esenciales que involucran configurar tu entorno de desarrollo, escribir el contrato inteligente utilizando las bibliotecas de OpenZeppelin, desplegarlo en la blockchain y verificar su funcionamiento.

### **Paso 1: Configurar el Entorno de Desarrollo**

Antes de empezar a escribir tu contrato, necesitarás configurar un entorno de desarrollo. Nosotros venimos utilizando Remix, pero también se podría utilizar Hardhat o Truffle. Para este ejemplo, vamos a utilizar Remix por su simplicidad y facilidad de uso.

1. **Instalación de OpenZeppelin**: En Remix, puedes importar directamente de GitHub. Para entornos como Truffle o Hardhat, deberás instalar OpenZeppelin a través de npm:

   ```bash
   npm install @openzeppelin/contracts
   ```

### **Paso 2: Escribir el Contrato ERC-20**

Aquí está un ejemplo básico de cómo definir un token ERC-20 utilizando OpenZeppelin. Este token tendrá un suministro fijo y características básicas de ERC-20.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MyToken is ERC20 {
    constructor(uint256 initialSupply) ERC20("MyToken", "MTK") {
        _mint(msg.sender, initialSupply);
    }
}
```

#### **Explicación del Código:**

* **Importación**: Importamos **`ERC20.sol`** de OpenZeppelin, que contiene la implementación estándar de un token ERC-20.
* **Contrato MyToken**: Extiende **`ERC20`** de OpenZeppelin. El constructor requiere un suministro inicial **`initialSupply`** y llama a **`ERC20`**, el constructor de OpenZeppelin, para establecer el nombre (”MyToken”) y el símbolo del token (”MTK”).
* **Minting**: **`_mint`** es una función que se utiliza para crear la cantidad inicial de tokens. Estos tokens se asignan a la dirección que despliega el contrato (generalmente tu dirección).

### **Paso 3: Desplegar el Contrato**

Usando Remix:

1. **Compila el Contrato**: En Remix, compila el contrato seleccionando la versión correcta de Solidity que coincida con la declaración en tu código.
2. **Desplegar**: Cambia a la pestaña "Deploy & Run Transactions". Selecciona el entorno "Injected Web3" si estás usando Metamask y asegúrate de estar conectado a la red que deseas (por ejemplo, Sepolia).
3. **Ejecuta el Despliegue**: Introduce el suministro inicial en el campo del constructor y haz clic en "deploy".

### **Paso 4: Interactuar con el Contrato**

Una vez desplegado, puedes interactuar con tu token a través de Remix. Bajo la pestaña "Deployed Contracts", encontrarás tu contrato desplegado con todas las funciones de ERC-20 disponibles para interactuar desde la interfaz.

### **Paso 5: Verificar y Probar**

Es crucial verificar que tu token funcione como esperas:

* **Prueba las Funciones**: Utiliza Remix para transferir tokens entre direcciones, consultar balances, y probar las aprobaciones.
* **Verifica y publica el contrato:** Usando Etherscan es recomendable que verifiques y publiques el contrato de forma que quienes quieran ver qué hace tu contrato puedan comprobar que cumple con el estándar.
* **Considera Auditorías**: Si planeas lanzar tu token en mainnet considera realizar una auditoría de seguridad.

Crear un token ERC-20 es relativamente sencillo con OpenZeppelin, pero asegúrate de comprender todos los aspectos legales y de seguridad asociados con la creación y distribución de un nuevo token.


# Almacenamiento Descentralizado: IPFS

El Sistema de Archivos Interplanetario (IPFS, por sus siglas en inglés) es una tecnología diseñada para crear una web más descentralizada, resistente y eficiente. A diferencia del modelo tradicional de la web, donde los archivos se hospedan en servidores centrales, IPFS opera en una red de nodos distribuidos, permitiendo que los archivos sean almacenados y accedidos desde múltiples puntos simultáneamente.

Sus principales características son las siguientes:

* **Descentralización**: En IPFS, los archivos no se almacenan en un único servidor o ubicación, sino en una red de nodos, lo que ayuda a evitar problemas de censura y de puntos de fallo centralizados.
* **Eficiencia**: Los archivos se dividen en pequeños bloques y se distribuyen a través de la red. Cuando un usuario necesita acceder a un archivo, IPFS busca los bloques más cercanos o más rápidos de acceder, reduciendo el ancho de banda necesario y mejorando la velocidad de carga.
* **Persistencia**: Gracias a su naturaleza distribuida, una vez que un archivo se sube a IPFS, puede permanecer accesible incluso si algunos nodos se desconectan o son eliminados de la red. Esto contribuye a la durabilidad y permanencia de los datos.
* **Direcciones basadas en contenido**: IPFS utiliza identificadores únicos (hashes) basados en el contenido de los archivos en lugar de direcciones de servidor basadas en la ubicación. Esto significa que si el contenido de un archivo no cambia, su hash también permanece igual, lo cual facilita la verificación de la integridad de los datos.

#### **Cómo Funciona**

Cuando un archivo se sube a IPFS, se divide en bloques más pequeños, y cada bloque se identifica por un hash único. Estos bloques se distribuyen a través de la red de nodos de IPFS. Cuando alguien quiere acceder a un archivo, IPFS localiza los bloques necesarios a través de la red y los reúne para formar el archivo original. Como los archivos se identifican por su contenido, buscar y recuperar datos en IPFS puede ser más eficiente que en las redes basadas en la ubicación.

#### **Aplicaciones y Uso**

IPFS tiene una amplia gama de aplicaciones potenciales, desde la distribución descentralizada de contenido web hasta sistemas de archivo inmutables para registros legales o científicos. También es una tecnología clave para muchas aplicaciones descentralizadas (dApps) en el ecosistema de blockchain, proporcionando una solución para almacenar datos de manera eficiente y resistente.

### Cómo subir un archivo en IPFS

Subir un archivo a IPFS implica añadirlo a la red distribuida para que esté accesible desde cualquier punto de la misma. Aquí tienes un proceso paso a paso sobre cómo hacerlo, asumiendo que ya tienes IPFS instalado en tu sistema. Si aún no lo has hecho, el primer paso sería descargar e instalar IPFS desde [el sitio web oficial de IPFS](https://ipfs.io/).

#### **Paso 1: Iniciar el Daemon de IPFS**

Antes de subir cualquier archivo, necesitas iniciar el daemon de IPFS, que es un proceso en segundo plano que se encarga de conectar tu nodo (tu computadora) a la red IPFS. Abre una terminal o línea de comandos y escribe:

```
ipfs daemon
```

Este comando arrancará el daemon y tu nodo comenzará a conectarse con otros nodos de la red IPFS. Dejarás este proceso corriendo en el fondo.

#### **Paso 2: Añadir el Archivo a IPFS**

Con el daemon de IPFS corriendo, puedes añadir archivos a la red. Abre una nueva ventana de terminal para ejecutar el siguiente comando (no cierres el terminal con el daemon corriendo). Navega al directorio donde se encuentra el archivo que deseas subir y ejecuta:

```
ipfs add <nombre_del_archivo>
```

Reemplaza `<nombre_del_archivo>` con el nombre y la extensión del archivo que quieres subir. Este comando dividirá tu archivo en bloques más pequeños, calculará un hash único para cada uno y para el conjunto total, y comenzará a distribuir esos bloques a través de la red IPFS.

#### **Paso 3: Acceder al Archivo en IPFS**

Una vez que el archivo se ha añadido a IPFS, recibirás un hash único (CID - Content Identifier) como resultado en tu terminal. Este CID puede ser utilizado para acceder al archivo desde cualquier nodo de la red. Puedes compartir este hash con cualquier persona, y podrán acceder al archivo utilizando la interfaz de línea de comandos de IPFS, una puerta de enlace de IPFS en el navegador, o a través de aplicaciones que soporten IPFS.

Por ejemplo, para acceder a un archivo a través de una puerta de enlace pública de IPFS, puedes usar el siguiente URL en tu navegador:

```
<https://ipfs.io/ipfs/><CID>
```

Reemplaza `<CID>` con el identificador de contenido que recibiste después de añadir tu archivo.

A tener en cuenta:

* **Persistencia**: Para que un archivo permanezca accesible en IPFS, debe ser hospedado (anclado) por al menos un nodo en la red. Puedes mantener tu propio nodo corriendo para asegurar la disponibilidad de tu archivo, o utilizar servicios de anclaje de terceros.
* **Privacidad**: Ten en cuenta que cualquier archivo subido a IPFS es público y accesible por cualquiera que tenga el CID. Si necesitas compartir información sensible, deberías considerar cifrar los archivos antes de subirlos.

### Cómo subir una colección de imágenes a IPFS

Para subir una colección entera de imágenes a IPFS y luego utilizarlas en una colección de NFTs en Ethereum, puedes seguir estos pasos generales. El proceso se puede dividir en dos partes principales: subir las imágenes a IPFS y luego referenciar esos archivos en tus NFTs en la blockchain de Ethereum.

#### **Paso 1: Subir Imágenes a IPFS**

1. **Preparar las Imágenes**: Asegúrate de que todas las imágenes que deseas subir estén optimizadas para la web, para reducir los tiempos de carga sin comprometer la calidad. Organízalas en una carpeta en tu computadora.
2. **Instalar y Configurar IPFS**: Si aún no lo has hecho, necesitas descargar e instalar IPFS. Puedes encontrar las instrucciones detalladas en el [sitio web oficial de IPFS](https://ipfs.io/). Una vez instalado, inicia el daemon de IPFS con `ipfs daemon`.
3. **Subir la Colección a IPFS**:
   * Abre una nueva terminal.
   * Navega a la carpeta donde tienes las imágenes.
   * Usa el comando `ipfs add -r <nombre_de_la_carpeta>` para añadir toda la carpeta a IPFS. El flag `r` indica que estás añadiendo una carpeta y su contenido de manera recursiva. Este comando te proporcionará un CID (identificador de contenido) único para toda la carpeta, así como CIDs individuales para cada archivo dentro de ella.
4. **Registrar los CIDs**: Asegúrate de guardar los CIDs de la carpeta y de cada imagen. Los necesitarás para vincular cada NFT a su imagen correspondiente en la blockchain.

#### **Paso 2: Crear y Desplegar una Colección de NFTs en Ethereum**

1. **Definir los Metadatos de los NFTs**: Los metadatos de cada NFT deben ser un archivo JSON que contenga detalles como el nombre, descripción, y la URL de la imagen en IPFS. Sube estos archivos JSON a IPFS de la misma manera que subiste las imágenes.
2. **Desarrollar el Contrato Inteligente de NFT**: Desarrolla un contrato inteligente para tu colección de NFTs usando Solidity. Puedes utilizar el estándar ERC-721 o ERC-1155, dependiendo de tus necesidades. En el contrato, referencia las URLs de IPFS de los metadatos de tus NFTs. Herramientas como las que vimos anteriormente de OpenZeppelin pueden facilitar este proceso.
3. **Desplegar el Contrato Inteligente**: Una vez que tu contrato esté listo, despliégalo en la blockchain de Ethereum. Puedes hacerlo utilizando herramientas como Remix o Hardhat.
4. **Acuñar los NFTs**: Con tu contrato desplegado, puedes comenzar a acuñar NFTs llamando a la función de acuñación (mint) definida en tu contrato y pasando los CIDs de los metadatos de IPFS correspondientes a cada NFT.
5. **Verificación y Pruebas**: Verifica que tus NFTs están correctamente vinculados a sus imágenes y metadatos en IPFS accediendo a ellos a través de un marketplace de NFTs como OpenSea o una billetera compatible con NFTs.

Recuerda que desplegar contratos y acuñar NFTs en Ethereum requiere gas, por lo que deberás tener algo de Ether disponible para cubrir esos costos.

Al igual que con los archivos individuales, asegúrate de que las imágenes y metadatos sean públicos y accesibles para quien posea el NFT. También considera la persistencia de tus archivos en IPFS para asegurar que siempre estén disponibles.

### Pinata

Pinata es un servicio que ofrece una manera sencilla y eficaz de utilizar IPFS para alojar y gestionar archivos de forma descentralizada. La relación entre Pinata y IPFS es directa: Pinata actúa como una capa de servicio y una interfaz de usuario amigable sobre IPFS, facilitando a usuarios y desarrolladores el almacenamiento, la gestión y la distribución de contenido en la red IPFS sin necesidad de gestionar su propia infraestructura de nodos IPFS.

#### **Funcionalidades Principales de Pinata**

* **Anclaje (Pinning)**: Pinata permite a los usuarios "anclar" sus archivos en IPFS, lo que significa que los archivos se mantienen disponibles y accesibles en la red de forma continua. Esto es crucial porque, en IPFS, si un archivo no está anclado y todos los nodos que lo contienen se desconectan, ese archivo ya no estará accesible.
* **Gestión de Archivos**: A través de su interfaz web y API, Pinata ofrece herramientas para subir, organizar y administrar archivos y datos en IPFS de manera eficiente. Esto incluye la capacidad de crear y gestionar colecciones de archivos, lo que es especialmente útil para proyectos que manejan grandes cantidades de datos, como las colecciones de NFTs.
* **Interfaz de Usuario y API**: Pinata proporciona una interfaz de usuario intuitiva para interactuar con IPFS, lo que reduce la barrera de entrada para usuarios no técnicos. Para los desarrolladores, Pinata ofrece una API robusta que permite integrar las capacidades de IPFS en aplicaciones y servicios personalizados.
* **Distribución de Contenido**: Al usar Pinata, los archivos subidos a IPFS pueden ser distribuidos eficientemente a través de la red, mejorando la velocidad de acceso y la disponibilidad del contenido. Esto es particularmente valioso para aplicaciones que dependen de la entrega rápida y confiable de contenido digital.

La relación entre Pinata e IPFS es complementaria. Mientras IPFS proporciona la infraestructura de red descentralizada subyacente y el protocolo para el almacenamiento y distribución de archivos, Pinata simplifica y potencia el uso de IPFS para casos de uso específicos como la creación de NFTs, la distribución de contenido multimedia, y la gestión de datos descentralizados. Pinata se encarga de la complejidad técnica asociada con la operación de nodos IPFS, permitiendo que los usuarios se concentren en el contenido y la aplicación en lugar de en la gestión de la infraestructura.

En el contexto de los NFTs (Tokens No Fungibles), Pinata se ha convertido en una herramienta popular para alojar los archivos multimedia que estos representan (por ejemplo, arte digital, música, videos) y los metadatos asociados en IPFS. Al garantizar que estos archivos permanezcan accesibles en la red IPFS, Pinata juega un papel crucial en asegurar la integridad y la disponibilidad a largo plazo de los activos digitales vinculados a NFTs en blockchains como Ethereum.


# Crea un Token ERC-721

Es el momento de crear nuestro primer NFT. Utilizaremos nuevamente Remix y subiremos la imagen y metadatos a IPFS. Empecemos

### **Paso 1: Configurar el entorno**

Utilizaremos Remix.

1. **Instalación de OpenZeppelin**: Esta operación ya la hicimos para la creación del token ERC-20, así que ya tendremos acceso a la librería de contratos de Open Zeppelin.

### **Paso 2: Crea una imagen o utiliza una existente**

Crea una imagen con tu software favorito (Canva, Dall-e, Pain, etc.).

En nuestro caso utilizaremos la siguiente imagen en un formato cuadrado.

<figure><img src="/files/Qn9PE4L4qYG7MvWvb5Wk" alt="" width="375"><figcaption></figcaption></figure>

### **Paso 3: Subimos nuestra imagen a IPFS**

Utilizaremos Pinata (<https://www.pinata.cloud/>) que nos permita subir archivos a IPFS de forma sencilla y gratuita. Es necesario crear una cuenta.

Una vez hayas ingresado, vas a la sección Files y subes (Upload)el archivo.

Obtendrás un CID (Content Identifier) que es el código que representa de forma única a tu imagen y que a la vez es el hash de ella.

Para nuestro ejemplo, el CID de la imagen es: Qmdae238wjRaCrNuBzNTJDeJNbuemGWQB9tioDKrrjosMY

### **Paso 4: Creamos el JSON del NFT y lo subimos a IPFS**

Del módulo de Fundamento de Solidity, donde aprendimos que los ABI se escriben en formato JSON, recordarás que JSON significa JavaScript Object Notation y que es un formato de intercambio de datos ligero y de fácil lectura para humanos, pero también fácil de generar y analizar por las máquinas.

Dentro de la creación de NFTs se utilizan mucho los JSON para guardar los metadatos de los NFTs.

El objeto JSON que utilizaremos será muy sencillo en esta oportunidad:

```json
{
    "name": "Mi primer NFT",
    "description": "Un NFT creado en el Ethereum Developer Pack",
    "image": "<https://ipfs.io/ipfs/Qmdae238wjRaCrNuBzNTJDeJNbuemGWQB9tioDKrrjosMY>"
}
```

En este JSON definimos un nombre y descripción para este primer NFT, añadiremos también la referencia de IPFS donde está almacenada la imagen correspondiente.

Asegúrate de reemplazar el hash Qmdae238wjRaCrNuBzNTJDeJNbuemGWQB9tioDKrrjosMY del ejemplo, por el CID de tu imagen (que obtuviste en Pinata en el paso anterior).

Esta es la única forma que tienen de enterarse de la información de tu NFT los marketplaces de NFTs como OpenSea o Rarible.

Ahora es necesario subir este objeto JSON a IPFS. Para ello copia el contenido completo del objeto en un archivo con extensión json. Para ello utiliza tu editor de texto favorito.

En nuestro caso denominaremos al archivo MiNFT.json y lo subiremos a IPFS utilizando Pinata.

Obtenemos el CID QmVjHeKjQk5snH1KAcVUPXzwSrW2e7oX1KmVQHHoPwKkuJ

### **Paso 5: Creamos el smart contract y lo desplegamos en Sepolia**

Utilizaremos la librería de Open Zeppelin para obtener alguno de los contratos ERC-721.

Utilizamos Remix para desplegar el siguiente contrato en Sepolia. Antes de desplegarlo asegúrate de reemplazar el CID QmVjHeKjQk5snH1KAcVUPXzwSrW2e7oX1KmVQHHoPwKkuJ por el CID del JSON que creaste en el paso anterior.

También puedes personalizar en el constructor el nombre del NFT y su identificador o símbolo.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract MyNFT is ERC721 {
    uint256 token_count;

    constructor() ERC721("My NFT", "MNFT") {}

    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_ownerOf(tokenId) != address(0), "ERC721Metadata: URI query for nonexistent token");
        return "<https://ipfs.io/ipfs/QmVjHeKjQk5snH1KAcVUPXzwSrW2e7oX1KmVQHHoPwKkuJ>";
    }

    function mintNFT(address to) public
    {
        token_count  += 1;
        _mint(to, token_count);
    }
}
```

Una vez desplegado el contrato en Sepolia. Estarán disponibles en Remix las funciones para ejecutar con el contrato.

<figure><img src="/files/UR3bW77acxVVDuDmwX5C" alt="" width="375"><figcaption></figcaption></figure>

### **Paso 6: Mintear el NFT**

Crearemos el primer NFT utilizando la función **`mintNFT` .** Debemos indicar la dirección del destinatario **`to` ,** para el ejemplo utilicemos nuestra misma address, de esa forma seremos el propietario de ese primer NFT minteado.

Aprobamos la transacción y ¡habremos minteado nuestro primer NFT!

Recuerda que si validas y publicas este contrato en Etherscan podrás acceder a estas mismas funciones desde ese explorador de bloques.

### **Paso 7: Verifica que tu NFT ha sido creado**

Para verificar que nuestro NFT ha sido creado y que toda la información está correcta podemos ingresar a un marketplace de NFT como OpenSea.

Para ello ingresamos al entorno de pruebas de OpenSea ya que hemos desplegado nuestro contrato en una testnet como es Sepolia. La dirección es: <https://testnets.opensea.io/>

Nos conectamos con la misma wallet con la que hicimos el minteo del NFT.

Vamos a la sección de datos de la cuenta y seleccionamos nuestro perfil (Profile) para ver los NFT que son de nuestra propiedad.

<figure><img src="/files/EZ5FpYMYQmXEKvvZ1xuP" alt=""><figcaption></figcaption></figure>

Si seguimos los pasos correctamente, podremos encontrar nuestro NFT en el marketplace.

Haciendo click en él, podremos verificar que los metadatos están registrados correctamente.

<figure><img src="/files/JF3NRMNJ1nGnkwyuDnUW" alt=""><figcaption></figcaption></figure>

Podemos hacer la misma verificación en Rarible: <https://testnet.rarible.com/>

Felicitaciones por haber completado este reto.

### 🫶🏻 **Nota:** Agradecemos a Ahmed Casto (@filosofiacodigo) por darnos la inspiración para desarrollar esta sección.


# DeFi

DeFi (Decentralized Finance) o Finanzas Descentralizadas es un término que se refiere de forma general a servicios financieros sin intermediarios.

Hablamos de aplicaciones descentralizadas (DApps) que articulan programas (*smart contracts*) que corren sobre una blockchain y que prestan los mismos servicios que hasta el momento solo hemos recibido de bancos o empresas financieras: depósitos, préstamos con garantía, intercambios de moneda, seguros, etc.

Sin bancos, sin empleados, sin tarjetas, sin documentos de identidad.

### **Lo que quiere resolver DeFi**

Hasta hace poco, los usuarios de los servicios financieros no tenían más opción que utilizar a los intermediarios de las finanzas tradicionales (TradFi) como son los bancos, financieras y empresas de seguros.  Estas instituciones han venido utilizando modelos de negocio que no han evolucionado mucho desde sus orígenes hace más de 500 años y más bien se han convertido en gigantes lentos, poco tolerantes a la competencia, y en muchos casos corruptos y poco transparentes, tal como se evidenció en la crisis financiera del 2008.

Evaluemos algunos aspectos de los servicios financieros para explicar qué aporta el surgimiento de DeFi en cada caso.

1. **Accesibilidad:** Si bien cada día mas personas tienen acceso a servicios financieros, aún muchos de estos servicios requieren una evaluación detallada de los potenciales clientes para ser autorizados, por ejemplo préstamos, acceso a cuentas de inversiones, incluso cuentas bancarias. Con DeFi no hay restricciones para acceder a los servicios financieros que ofrece. Sólo se requiere una computadora o un teléfono y una conexión a internet.
2. **Centralización y competencia:** Los servicios financieros tradicionales están centralizados, requieren un intermediario como un banco que controla todas las transacciones que hacen sus clientes y que en cualquier momento puede bloquear el acceso a sus servicios y modificar las condiciones sin previo aviso. Estas instituciones conforman oligopolios muy poderosos que pueden influir en la legislación a través de lobbies y pueden concertar tasas de interés, como se demostró en el 2012 con la tasa Libor.

   Los servicios de DeFi están descentralizados, no hay un ente que tenga control de los servicios y que pueda apagarlos o alterar condiciones: una vez que un protocolo de DeFi es puesto a funcionar nadie lo puede parar.

   Por otro lado, dado que los programas que soportan los servicios DeFi son de dominio público, un competidor puede hacer una réplica y ponerla a funcionar brindando mejores condiciones, generando una competencia muy alta en este mercado.
3. **Transparencia:** Los bancos no están obligados a dar ninguna explicación respecto a ninguna de sus operaciones o procesos. No podemos pretender conocer cuánto dinero captan de sus ahorristas, a qué tasas, ni donde colocan sus préstamos. Con DeFi, todo esto cambia. Hay transparencia total, no solo es posible conocer todas las operaciones financieras sino que incluso se puede ver el código con el que funciona el protocolo (programa) de DeFi.
4. **Custodia del dinero:** En las finanzas tradicionales esperamos que el banco custodie nuestro dinero. Cedemos al banco la responsabilidad de mantenerlo a buen recaudo, pero existe el riesgo de que ocurra algún evento, remoto por cierto, que haga que lo perdamos (una corrida financiera, una quiebra, un manejo irresponsable, etc.). En el mundo de DeFi, al no haber intermediario, nosotros mismos custodiamos nuestro dinero. Eso implica que debemos tomar las precauciones correspondientes: «todo gran poder conlleva una gran responsabilidad» como diría un conocido super héroe.
5. **Transferencias internacionales:** Hacer una transferencia internacional utilizando los servicios tradicionales implica un alto costo y un plazo de atención de al menos dos días. En el caso de las remesas algunos intermediarios cobran más del 10%, lo que es excesivo. A través de los servicios DeFi las transferencias son inmediatas a cualquier lugar del mundo y a un costo ínfimo.
6. **Costos de transacción:** Si hablamos de obtener un préstamo a través de un banco, existen muchos costos adicionales al costo financiero en si: tasaciones, costos por desembolso, notariales, etc. En el mundo DeFi se habla de los costos financieros principalmente pero existen costos asociados al uso de la blockchain que, dependiendo de cuál sea, pueden ser más o menos altos, aunque siempre menores a los que cobraría una institución financiera.
7. **Disponibilidad:** Los servicios financieros tradicionales están normalmente disponibles en horarios de oficina de lunes a sábado, aunque algunos servicios digitales se pueden realizar en cualquier momento. Los servicios DeFi están disponibles 24×7 en cualquier lugar del mundo donde haya una conexión a internet.
8. **Participación del usuario en la gestión del negocio:** Salvo que un cliente sea a la vez accionista del banco, no tiene ni voz ni voto en su gestión, es impensable. En DeFi algunos protocolos como UniSwap o SushiSwap emitieron tokens de gobernanza (UNI y SUSHI respectivamente) que regalaron a sus usuarios. Estos tokens son una forma de compartir las ganancias con los usuarios de los servicios, pero además les brindan la posibilidad de participar en las decisiones del protocolo a través de una entidad que los asocia denominada DAO (Organización Autónoma Descentralizada).

El siguiente cuadro compara lo que ofrece DeFi respecto a las Finanzas Tradicionales (TradFi).

<figure><img src="/files/X18ROfu2URKbtotTZ6QM" alt="" width="563"><figcaption></figcaption></figure>

### **Cómo llegamos a DeFi**

El surgimiento de DeFi es parte de un desarrollo progresivo que se inicia con la aparición de la primera Blockchain (Bitcoin) y su criptomoneda.

Se incorpora luego la posibilidad de almacenar y ejecutar programas en la blockchain con el nacimiento de [Ethereum](https://cadenadebloques.io/ethereum/) y los *smart contracts*.

Las dApps integran los *smart contracts* y ofrecen una interfaz de usuario amigable que facilita su uso.

Finalmente, DeFi es el desarrollo de aplicaciones financieras sobre esta base.

<figure><img src="/files/uIePNhL8wCgsxt2iCvU1" alt="" width="563"><figcaption></figcaption></figure>

### **El origen del término DeFi**

En agosto del año 2018 un grupo de desarrolladores Ethereum y algunos emprendedores, evaluaban en un chat de Telegram cómo llamar de forma general a la ola de innovación financiera que surgía en blockchain.

Como lo cuenta en un [tweet](https://twitter.com/injeyeo/status/1157001562457174016) Inje Yeo, CEO de Set Protocol, se le podía haber llamado  Protocolos Financieros Abiertos, Horizontes Abiertos, entre otros nombres.

<figure><img src="/files/Ygf2o8vjyz7609gsIpL3" alt="" width="563"><figcaption></figcaption></figure>

Finalmente eligieron el nombre DeFi porque simbolizaba el desafío a las finanzas tradicionales (Defy en inglés).

### **Crecimiento de DeFi**

Desde que  MakerDAO, la primera aplicación de DeFi, naciera a fines del 2017, el crecimiento de los importes económicos manejados en las aplicaciones DeFi ha sido acelerado y en particular en el 2020 explotó, llegando a su pico en el 2021. Hubo una gran caída en el 2022 y una recuperación en el 2024.

El TVL (Valor Total Bloqueado – la cantidad de dinero depositada en los protocolos de DeFi) pasó de US$9,000 mill a mediados del 2020 a 174,000 mill a fines del 2021 y actualmente está cerca de los 90,000 mill.

<figure><img src="/files/5iE0mdHLLy1pyGO33fCi" alt=""><figcaption></figcaption></figure>

Pese al alto crecimiento, aún es un pequeño porcentaje de lo que mueve el sistema financiero tradicional. Lo que se mueve en DeFi es equivalente a lo que mueve Visa y un sexto de lo que se mueve en NASDAQ.

Sin embargo el TradFi ya ha volteado su atención hacia el movimiento de DeFi, lo demuestra la portada de The Economist del mes de setiembre del 2021.

Esta portada de setiembre 2021 se subastó como NFT y llegó a venderse por 99.9 ETH (US$420,000)

<figure><img src="/files/D655ZyXaX1mkTIzPUjek" alt="" width="375"><figcaption></figcaption></figure>

Esto es un reconocimiento de la importancia de este nuevo mundo y una evidencia de la preocupación del mundo financiero tradicional respecto a un cambio que ya está revolucionando el sector financiero.

### **Los building blocks de DeFi**

Podemos pensar en el ecosistema de aplicaciones de DeFi como en un juego de piezas de Lego, donde podemos armar figuras complejas a partir de bloques sencillos.

Las piezas de Lego de DeFi, los *building blocks*, son las siguientes:

**Wallets:** Son los integradores de la web con los servicios en la blockchain. Guardan las claves privadas de los usuarios para autorizar las operaciones con los protocolos DeFi, permiten monitorear los saldos y movimientos de diversas criptomonedas y tokens de la cuenta asociada . La wallet más utilizada en el ecosistema de Ethereum es [Metamask](https://metamask.io/).

**Stablecoins:** Dentro del mundo de DeFi, donde las criptomonedas tienen un valor que es bastante volátil, los inversionistas deben tener donde resguardarse sin tener que salir del mundo cripto cuando realicen una venta de bitcoin o ETH por ejemplo.

Para ello se crearon las *stable coins* que son monedas o tokens cuyo valor intenta ser siempre equivalente a una moneda importante como el dólar. Hablamos entonces de dólares digitales.

Una vez que uno convierte una criptomoneda a una *stable coin* puede utilizarla por ejemplo para ganar intereses mientras espera otra oportunidad para volver a entrar al mercado.

Las *stablecoins* líderes son USDT, USDC y DAI. No todas las *stablecoins* han sido creadas iguales.

**Exchanges Descentralizados (DEXES):** En el ecosistema se necesita a alguien que convierta una criptomoneda (o token) a otra, a esta operación se le denomina swap y los protocolos que las ejecutan son los Exchanges. Estos programas hacen la función  de intercambio de monedas que en el mundo tradicional hacen las casas de cambio o los bancos.

Los Exchanges utilizan diferentes mecanismos para atraer la liquidez que permite que se pueda intercambiar cualquier cantidad de una criptomoneda por otra. El intercambio de monedas tiene un costo que normalmente es el 0.3% del importe de la transacción.

Entre los principales DEX (Exchange Descentralizados) tenemos a [UniSwap](https://app.uniswap.org/#/swap), [SushiSwap](https://app.sushi.com/es/swap) y [Curve](https://curve.fi/).

**Depósitos y préstamos:** Otro grupo de protocolos ofrece la posibilidad de otorgar préstamos contra un colateral de criptomonedas, algo similar a pedir un préstamo contra una garantía hipotecaria.

Normalmente recurren a estos servicios las personas que no quieren vender sus criptomonedas y se apalancan en ellas para aprovechar otras oportunidades de inversión. Del otro lado, estos protocolos reciben depósitos en criptomonedas y pagan intereses superiores al sistema financiero tradicional.

Los protocolos líderes son [Aave](https://app.aave.com/#/markets) y [Compound](https://app.compound.finance/).

**Staking Líquido:** Protocolos que te permiten obtener recompensas de staking en tus tokens y que a la vez proporcionan un token negociable y líquido por tu stake. Con estos protocolos se bloquean fondos sin perder liquidez.

Destacan en esta categoría Lido y Rocketpool.

**Sintéticos:** También existe la posibilidad dentro de DeFi de invertir en activos sintéticos. Se denominan sintéticos porque no son los activos reales sino que simulan serlo; así como existe una esencia de vainilla sintética que simula a la vainilla natural, de la misma manera estos activos se comportan como si fueran el original.

Se puede crear sintéticos de muchas cosas, de una acción, del oro, de un índice, de una alguna criptomoneda. De esta manera uno puede estar expuesto a cualquier activo del mundo sin tener que salir del  mundo cripto. Los líderes son [Synthetix](https://synthetix.io/) y [Mirror](https://mirrorprotocol.app/#/trade).

Otro protocolo de esta categoría es [SetToken](https://www.tokensets.com/) que ofrece tokens asociados a índices que reflejan el comportamiento de diversos sectores del mundo blockchain como por ejemplo Metaverso (MVI) y protocolos DeFi (DeFi Pulse). En lugar de comprar muchos tokens basta con comprar uno de estos y ganar exposición a esos mercados.

**Seguros:** El mundo de DeFi no está ajeno a los riesgos y allí entran en juego protocolos de seguros que protegen a quienes los adquieren contra diversos eventos, por ejemplo que un hacker ataque un protocolo y se robe los fondos, o que el código del protocolo tenga errores que puedan perjudicar económicamente al inversionista.

[Nexus Mutual](https://nexusmutual.io/) e [InsurAce](https://www.insurace.io/) son protocolos de seguros que por un porcentaje del importe invertido ofrece una cobertura contra ese tipo de riesgos.

**Agregadores/Optimizadores:** Otras piezas de Lego son los agregadores. Estas aplicaciones alivian la tarea de buscar los mejores rendimientos entre las diferentes ofertas de protocolos, brindando bajo una solo interfaz la mejor opción en un momento determinado, optimizando con esto tiempo y costo.

Agregadores destacados son [1Inch](https://app.1inch.io/), [Matcha](https://matcha.xyz/), [Zerion](https://app.zerion.io/) y [ParaSwap](https://paraswap.io/).

**Dashboards:** Con tantos protocolos es difícil hacer el seguimiento a todas las inversiones que en simultáneo estarían activas. Una opción es ingresar a cada protocolo para ver la situación en algún momento, pero hay otra mejor y son los dashboards o gestores de portafolios.

Estos dashboards permiten visualizar en un solo sitio todos los instrumentos en los que se ha invertido y que están asociados a una cuenta (dirección), pudiendo incluso ver lo que hay en diferentes blockchains.

Los *dashboards* más utilizados son [Zapper](https://zapper.fi/) y [Zerion](https://app.zerion.io/).

<figure><img src="/files/NRYgYfaoj895or8nBQcU" alt="" width="563"><figcaption></figcaption></figure>

### **Los retos de DeFi**

Si bien DeFi trae grandes beneficios respecto a las finanzas tradicionales, no deja de tener algunos aspectos a resolver.

1. **Requiere mucha educación:** El mundo de DeFi está lleno de oportunidades pero también de personas dispuestas a aprovecharse de nuestro desconocimiento. Por ello es importante instruirse concienzudamente antes de interactuar con los protocolos de DeFi. Se debe  entender el funcionamiento del protocolo con el que se interactúa,  verificar si el código ha sido auditado, los antecedentes de los creadores, quiénes respaldan el protocolo, cuánto tiempo viene funcionando. Esto ayudará a invertir con mejores probabilidades de éxito y evitará que caigamos fácilmente en manos de estafadores.
2. **Costos de transacción:** Si bien las comisiones son pequeñas en DeFi, los costos de transacción asociados al uso de la blockchain pueden ser relativamente altos. En particular en Ethereum las inversiones de importes pequeños, por ejemplo de menos de 1000 dólares, no hacen sentido algunas veces pues gran parte de la rentabilidad se pierde en fees de la red. Por ello, mientras Ethereum no implante su solución para ser más escalable y veloz, las redes competidoras seguirán creciendo.
3. **Riesgos particulares de DeFi:** Existen algunos riesgos que son inherentes a la tecnología  que utiliza DeFi. En principio tenemos los errores de programación. Un hacker puede detectar alguna debilidad en el código y aprovecharla en su beneficio. Otro riesgo, más acotado es que no haya liquidez en los protocolos puesto que el dinero se mueve rápidamente si identifica una mejor rentabilidad, así que la liquidez que originalmente está en un protocolo salta a otro y puede hacer que los costos de salir de una inversión sean más altos de lo esperado.
4. **Regulación:** DeFi busca ser un mundo totalmente libre, anónimo, sin controles ni restricciones. Esto inevitablemente generará fricciones con los gobiernos que buscan dar protección a los usuarios, cobrar impuestos, controlar la oferta monetaria y  el flujo de capitales. El desenlace es incierto, pero una mayor adopción de DeFi y en general del uso de criptomonedas es imprescindible para su supervivencia a largo plazo.


# Toolkit para desarrollo en Ethereum

**Objetivo:** Conectar el frontend a la blockchain, utilizando las principales herramientas del mercado para desarrollar en Web3.

**Duración:** 9 horas (3 clases de 3 horas cada una).

<figure><img src="/files/Jj0aODIipLoaP2i3HvPr" alt=""><figcaption></figcaption></figure>

Para poder participar en este módulo es necesario instalar y conocer las herramientas señaladas en la sección de requisitos del módulo 4.

{% hint style="info" %}
Este documento es una herramienta pedagógica que actualizamos constantemente. Creemos en la importancia de contenidos open-source. Si quieres mejorar los contenidos, únete a nuestro programa de [Kipu Explorers](/contribuye/kipu-explorer).
{% endhint %}


# Requisitos para el módulo 4

Para poder iniciar el módulo 4 se requiere haber instalado las siguientes herramientas y saber cómo utilizarlas:

1. Terminal
2. Git y GitHub
3. Node.js y npm
4. Visual Studio Code

En esta sección revisaremos cada uno de estas dependencias.


# Terminal

El terminal es una herramienta que permite a los usuarios interactuar con su sistema operativo mediante comandos de texto. En Windows, se conoce comúnmente como "Command Prompt" o "PowerShell", mientras que en macOS se utiliza la "Terminal". A menudo se tendrá que utilizar el terminal para ejecutar diversos programas, así como para trabajar con directorios y archivos.

### **Introducción al Terminal**

#### **Acceso al Terminal**

**Windows:**

* **Command Prompt:**
  * Abre el menú de inicio y escribe "cmd".
  * Selecciona "Command Prompt".
* **PowerShell:**
  * Abre el menú de inicio y escribe "powershell".
  * Selecciona "Windows PowerShell".

**macOS:**

* Abre Spotlight (Cmd + Espacio), escribe "Terminal" y selecciona la aplicación Terminal.

### **Comandos Básicos**

#### **Navegación de Directorios**

**Windows:**

* **`dir`**: Lista los archivos y carpetas en el directorio actual.
* **`cd [ruta]`**: Cambia al directorio especificado.
* **`cd ..`**: Sube un nivel en la estructura de directorios.
* **`cd`**: Vuelve al directorio raíz del usuario.

**macOS:**

* **`ls`**: Lista los archivos y carpetas en el directorio actual.
* **`cd [ruta]`**: Cambia al directorio especificado.
* **`cd ..`**: Sube un nivel en la estructura de directorios.
* **`cd`**: Vuelve al directorio raíz del usuario.
* **`pwd`**: Indica el directorio en el que estás ubicado.

#### Limpieza del terminal

**Windows:**

* **`cls`**: Borra los comandos anteriores que quedaron en el terminal.

**macOs:**

* **`clear`**: Borra los comandos anteriores que quedaron en el terminal.

#### **Manipulación de Archivos y Directorios**

**Windows:**

* **`mkdir [nombre_del_directorio]`**: Crea un nuevo directorio.
* **`rmdir [nombre_del_directorio]`**: Elimina un directorio vacío.
* **`del [nombre_del_archivo]`**: Elimina un archivo.
* **`copy [origen] [destino]`**: Copia un archivo de origen a destino.
* **`move [origen] [destino]`**: Mueve un archivo de origen a destino.

**macOS:**

* **`mkdir [nombre_del_directorio]`**: Crea un nuevo directorio.
* **`rmdir [nombre_del_directorio]`**: Elimina un directorio vacío.
* **`rm -r [nombre_del_directorio]`**: Elimina un directorio y su contenido.
* **`rm [nombre_del_archivo]`**: Elimina un archivo.
* **`cp [origen] [destino]`**: Copia un archivo o directorio de origen a destino.
* **`mv [origen] [destino]`**: Mueve un archivo o directorio de origen a destino.
* **`touch [nombre_del_archivo]`**: Crea un archivo de texto.

#### **Visualización y Edición de Archivos**

**Windows:**

* **`type [nombre_del_archivo]`**: Muestra el contenido de un archivo.
* **`notepad [nombre_del_archivo]`**: Abre un archivo en el editor de texto Notepad.

**macOS:**

* **`cat [nombre_del_archivo]`**: Muestra el contenido de un archivo.
* **`nano [nombre_del_archivo]`**: Abre un archivo en el editor de texto Nano.
* **`open -e [nombre_del_archivo]`**: Abre un archivo en el editor de texto por defecto (TextEdit).

### **Comandos Avanzados**

#### **Gestión de Procesos**

**Windows:**

* **`tasklist`**: Lista todos los procesos en ejecución.
* **`taskkill /PID [pid]`**: Termina un proceso por su ID.

**macOS:**

* **`ps -A`**: Lista todos los procesos en ejecución.
* **`kill [pid]`**: Termina un proceso por su ID.
* **`top`**: Muestra los procesos en ejecución en tiempo real.

#### **Redes**

**Windows:**

* **`ipconfig`**: Muestra la configuración de red.
* **`ping [dirección]`**: Envía paquetes ICMP a una dirección de red.

**macOS:**

* **`ifconfig`**: Muestra la configuración de red.
* **`ping [dirección]`**: Envía paquetes ICMP a una dirección de red.

### **Recursos Adicionales**

#### **Ayuda y Documentación**

**Windows:**

* **`help [comando]`**: Muestra la ayuda para un comando específico.
* **`get-help [comando]`** (PowerShell): Muestra la ayuda para un comando específico.

**macOS:**

* **`man [comando]`**: Muestra el manual de usuario para un comando específico.

#### **Enlaces Útiles**

* [Documentación de PowerShell](https://docs.microsoft.com/en-us/powershell/)
* [Documentación de Terminal de macOS](https://support.apple.com/guide/terminal/welcome/mac)
* [Comandos de terminal que un usuario de Mac debe conocer](https://www.techrepublic.com/article/16-terminal-commands-every-user-should-know/)
* [Tutorial para para Windows (video)](https://www.youtube.com/watch?v=erKosEQaaFc)
* [Tutorial para macOs (video)](https://www.youtube.com/watch?v=TmP3y7Z2kk4)&#x20;


# Git y Github

Git es un sistema de control de versiones distribuido que permite a los desarrolladores rastrear y gestionar cambios en el código. GitHub es una plataforma basada en la web que utiliza Git para el control de versiones y facilita la colaboración en proyectos.

### **Instalación**

#### **Instalación de Git**

* **Windows:**
  1. Descarga Git desde [git-scm.com](https://git-scm.com/).
  2. Ejecuta el instalador y sigue las instrucciones.
* **macOS:**
  1. Es probable que Git ya esté instalado en tu computadora, para verificarlo ingresa el siguiente comando en el terminal: **`git -v` .** Si aparece el número de versión de Git, ya lo tienes instalado.
  2. Si no lo tienes instalado utiliza el siguiente comando en el terminal: **`brew install git`**.
  3. Alternativamente, descarga desde [git-scm.com](https://git-scm.com/).

#### **Configuración Inicial**

Configura tu nombre de usuario y correo electrónico:

```bash
git config --global user.name "Tu Nombre"
git config --global user.email "tuemail@example.com"
```

#### **Comandos Básicos de Git**

#### **Inicializar un repositorio**

* Crear un nuevo repositorio: El repositorio es el directorio donde guardarás la información del proyecto que vayas a crear. Una vez que te ubiques dentro de ese directorio usando el terminal, ingresa el siguiente comando.

  ```bash
  git init
  ```

#### **Añadir y confirmar cambios**

* Para que Git comience a controlar los cambios en los archivos de tu repositorio debes indicarle los nombres de los archivos.
* Si solo quieres monitorear archivos específicos, usa el siguiente comando:

  ```bash
  git add nombre_del_archivo
  ```
* Si quieres monitorear todos los archivos del repositorio utiliza:

  ```bash
  git add .
  ```

  Este es el comando que utilizarás más frecuentemente para guardar tus cambios. Cada vez que quieras definir un punto de control, este es el comando que debes utilizar.
* En cada oportunidad que incluyas un punto de control debes confirmar los cambios con un commit e incluir un texto recordatorio que resuma los últimos cambios realizados:

  ```bash
  git commit -m "Mensaje del commit"
  ```

  El mensaje que incluyas te permitirá identificar la versión de tus archivos en caso quieras regresar a ese punto.

#### **Ver el estado y el historial**

* Ver el estado del repositorio:

  ```bash
  git status
  ```
* Ver el historial de commits: Si quieres ver todos los cambios guardados desde el primer commit utiliza este comando.

  ```bash
  git log
  ```

#### Saltar entre versiones

* Regresar a un punto de control: Utilizando este comando puedes regresar a una versión identificada con el commit\_hash que aparece en el historial (log).

  ```bash
  git checkout commit_hash
  ```

#### **Clonar un repositorio**

* Clonar un repositorio existente: Cuando quieras descargar hacia tu computadora todo el contenido de un repositorio.

  ```bash
  git clone URL_del_repositorio
  ```

#### Vídeo tutorial de Git

<https://www.loom.com/share/0e1f7466a3c6423b879d40e1bf24e673?sid=516e205a-5a96-4430-b8be-0923941104ce>

### **Trabajo con Ramas**

Cuando empieces a trabajar en un proyecto estarás sobre la rama principal (master), pero más adelante tal vez te interese crear una nueva rama que te permita hacer cambios que no afecten a la rama principal. O tal vez quieres proponer un cambio a un proyecto de otra persona y vas a trabajar sobre una copia en la que harás tus propuestas de cambios sin afectar la rama principal. En esos casos debes crear una rama.

#### **Crear y Cambiar de Rama**

* Crear una nueva rama: Al crear una rama haces una copia de la rama principal a partir de la cual puedes incluir nuevos cambios.

  ```bash
  git branch nombre_de_la_rama
  ```
* Cambiar a otra rama:

  ```bash
  git checkout nombre_de_la_rama
  ```
* Crear y cambiar a una nueva rama: Este será el comando más utilizado para crear nuevas ramas.

  ```bash
  git checkout -b nombre_de_la_rama
  ```

#### **Fusionar Ramas**

* Fusionar una rama en la rama actual: Una vez que estás seguro de que los cambios que hiciste en una rama no tienen errores y quieres incorporarlos a tu rama principal, utiliza este comando para fusionar los cambios.

  ```bash
  git merge nombre_de_la_rama
  ```

### **Trabajando con GitHub**

GitHub es una plataforma web de desarrollo colaborativo que utiliza Git para el control de versiones de proyectos. Permite a los desarrolladores alojar sus proyectos, colaborar con otros, gestionar versiones de código y realizar seguimiento de cambios. GitHub facilita la colaboración mediante características como pull requests, issues y proyectos. Es ampliamente utilizado en la comunidad de desarrollo de software para proyectos de código abierto y privados, proporcionando un entorno eficiente para la gestión y revisión del código.

#### **Crear un Repositorio en GitHub**

1. Inicia sesión en [GitHub](https://github.com/). Si no tienes un usuario, procede a crearlo.
2. Haz clic en "New" para crear un nuevo repositorio.
3. Completa la información del repositorio y haz clic en "Create repository".

#### **Conectar un Repositorio Local a GitHub**

* Añadir un repositorio remoto: Este comando establece la conexión entre el repositorio local y el repositorio en GitHub.

  ```bash
  git remote add origin URL_GitHub
  ```

#### **Enviar Cambios a GitHub**

* Enviar cambios al repositorio remoto:

  ```bash
  git push origin nombre_de_la_rama
  ```

#### **Recuperar Cambios de GitHub**

* Recuperar cambios del repositorio remoto: Recupera hacia el repositorio local el contenido de la rama especificada que está en GitHub.

  ```bash
  git pull origin nombre_de_la_rama
  ```

\<aside> 💡 **Tip:** Si estás conectando por primera vez Git a GitHub, asegúrate de estar utilizando el mismo usuario.

Recibirás el siguiente mensaje:

```bash
Username for '<https://github.com>': 
```

Asegúrate de ingresar tu usuario. Luego se te solicitará la contraseña

```bash
Password for '<https://tuusuario@github.com>':
```

Ingresa tu Token de Acceso Personal de GitHub. Si no lo tienes, debes crearlo siguiendo los siguientes pasos:

1. Inicia sesión en tu cuenta de GitHub.
2. Ve a [Settings](https://github.com/settings/profile).
3. En el menú de la izquierda, selecciona **Developer settings**.
4. Haz clic en **Personal access tokens**.
5. Haz clic en **Generate new token**.
6. Asigna un nombre a tu token, selecciona los permisos necesarios (al menos **`repo`** para acceso completo a los repositorios) y haz clic en **Generate token**.
7. Copia el token generado y guárdalo en un lugar seguro. No podrás verlo de nuevo una vez que cierres la ventana. \</aside>

### **Colaboración**

#### **Fork y Pull Request**

* **Fork:** Duplica un repositorio en tu cuenta para hacer cambios.
* **Pull Request:** Solicita la incorporación de tus cambios en el repositorio original.

#### **Revisar y Fusionar Pull Requests**

1. Ve a la pestaña "Pull requests" en GitHub.
2. Revisa los cambios y comentarios.
3. Si la propuesta de cambios te satisface, haz clic en "Merge pull request" para fusionar los cambios.

#### Vídeo tutorial de GitHub

<https://www.loom.com/share/d60ea427b61646cea80f474c5d2e4a10?sid=a917e006-8e41-4420-9dae-e407ee837159>

### Documentación de ayuda.

* [Documentación de Git](https://git-scm.com/doc)
* [Guía rápida de Git y GitHub en español](https://training.github.com/downloads/es_ES/github-git-cheat-sheet/)
* [Guía de GitHub](https://docs.github.com/es/get-started/start-your-journey)


# Node.js y npm

### Node.js

Node.js es un entorno de ejecución de JavaScript basado en el motor V8 de Google Chrome que permite a los desarrolladores ejecutar código JavaScript en el lado del servidor. Antes a su existencia el código JavaScript sólo se podía ejecutar en un navegador web.

Para el propósito de nuestro curso es importante porque ejecutaremos código JavaScript que interactúa con la blockchain y necesitamos un entorno que pueda procesar ese código.

#### **Instalación de Node.js y npm**

**Instalación en Windows**

1. **Descargar el Instalador:**
   * Ve a la [página de descarga de Node.js](https://nodejs.org/) y descarga el instalador para Windows.
2. **Ejecutar el Instalador:**
   * Ejecuta el instalador y sigue las instrucciones del asistente de instalación.
3. **Verificar la Instalación:**
   * Abre una terminal (Command Prompt o PowerShell) y ejecuta los siguientes comandos:

     ```bash
     node -v
     npm -v
     ```

     Deberías ver las versiones instaladas de Node.js y npm.

**Instalación en macOS**

1. **Instalar Homebrew (si no está instalado):**
   * Abre una terminal y ejecuta:

     ```bash
     /bin/bash -c "$(curl -fsSL <https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh>)"
     ```
2. **Instalar Node.js usando Homebrew:**
   * Ejecuta el siguiente comando en la terminal:

     ```bash
     brew install node
     ```
3. **Verificar la Instalación:**
   * Ejecuta los siguientes comandos en la terminal:

     ```bash
     node -v
     npm -v
     ```

     Deberías ver las versiones instaladas de Node.js y npm.

### npm

npm (Node Package Manager) es el gestor de paquetes de Node.js que permite instalar y actualizar paquetes de terceros para utilizarlos en tu propio código.

Veamos un ejemplo de cómo utilizar Node.js y npm en un proyecto sencillo que será un servidor web básico.

#### **Creación de un Proyecto Node.js**

**Crear un Directorio para el Proyecto**

* Abre una terminal y navega hasta el directorio donde quieres crear tu proyecto, luego ejecuta:

  ```bash
  mkdir mi-proyecto-node
  cd mi-proyecto-node
  ```

**Inicializar el proyecto con npm**

* Ejecuta el siguiente comando para crear un archivo **`package.json` ,** más adelante explicaremos el uso de este archivo con más detalle.

  ```bash
  npm init
  ```
* Sigue las instrucciones para completar la configuración del proyecto. Puedes aceptar las configuraciones por defecto presionando Enter.

**Instalar Express**

* Asegúrate de estar en el directorio del proyecto y ejecuta:

  ```bash
  npm install express
  ```

  Express es un framework web minimalista y flexible que proporciona herramientas para desarrollar aplicaciones web y móviles.

**Crear el archivo del servidor**

* En el directorio del proyecto, crea un archivo llamado **`server.js`** y agrega el siguiente código:

  ```jsx
  const express = require('express');
  const app = express();
  const port = 3000;

  app.get('/', (req, res) => {
    res.send('Hello World!');
  });

  app.listen(port, () => {
    console.log(`Server running at <http://localhost>:${port}/`);
  });
  ```

  Asegúrate de haber grabado los cambios.

**Ejecutar el servidor**

* En la terminal, asegúrate de estar en el directorio del proyecto y ejecuta:

  ```bash
  node server.js
  ```

**Verificar el servidor:**

* Abre un navegador web e ingresa a **`http://localhost:3000/`**.
* Deberías ver el mensaje "Hello World!". Si es así, ¡felicitaciones!.

De esta manera vimos cómo se utiliza npm para instalar paquetes como Express y cómo Node.js nos ayuda a ejecutar el código JavaScript, que en este caso nos permitió activar un servidor web.

A continuación hablaremos del fichero **`package.json`** que se utiliza en la configuración de proyectos con Node.js.

#### **Archivo package.json**

El archivo **`package.json`** es un archivo de configuración en cualquier proyecto Node.js. Contiene información sobre el proyecto y sus dependencias, entre otros detalles.

A continuación un ejemplo

```json
{
  "name": "mi-proyecto",
  "version": "1.0.0",
  "description": "Descripción de mi proyecto",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "dev": "nodemon index.js"
  },
  "dependencies": {
    "express": "^4.17.1"
  },
  "devDependencies": {
    "nodemon": "^2.0.7"
  },
  "author": "Tu Nombre",
  "license": "MIT"
}
```

#### Dependencias

Las dependencias son módulos de código (también conocidos como paquetes o librerías) que tu proyecto necesita para funcionar correctamente. Estas dependencias son gestionadas automáticamente por npm, que facilita su instalación, actualización y gestión.

1. **Tipos de Dependencias en npm**

* **Dependencias de producción (`dependencies`):**
  * Estas son las dependencias necesarias para que tu aplicación funcione en un entorno de producción. Se especifican en el archivo **`package.json`** bajo el campo **`dependencies`**.

    Ejemplo:

    ```json
    {
      "dependencies": {
        "express": "^4.17.1"
      }
    }
    ```

    En este caso, **`express`** es una dependencia de producción.
* **Dependencias de desarrollo (`devDependencies`):**
  * Estas son las dependencias necesarias solo durante el desarrollo de tu aplicación, como herramientas de prueba, traductores y detectores de errores. Se especifican en el archivo **`package.json`** bajo el campo **`devDependencies`**.

    Ejemplo:

    ```json
    {
      "devDependencies": {
        "nodemon": "^2.0.7"
      }
    }
    ```

    En este caso, **`nodemon`** es una dependencia de desarrollo.

2. **Gestión de dependencias con npm**

* **Instalación de Dependencias**
  * **Instalar todas las dependencias:**

    Ejecuta el siguiente comando en el directorio de tu proyecto. Esto instalará todas las dependencias especificadas en tu archivo **`package.json`**.

    ```bash
    npm install
    ```
  * **Instalar una nueva dependencia de producción:**

    Usa el siguiente comando para instalar una nueva dependencia y agregarla automáticamente al campo **`dependencies`** en tu **`package.json`**.

    ```bash
    npm install nombre-del-paquete
    ```
  * **Instalar una nueva dependencia de desarrollo:**

    Usa el siguiente comando para instalar una nueva dependencia y agregarla automáticamente al campo **`devDependencies`** en tu **`package.json`**.

    ```bash
    npm install nombre-del-paquete --save-dev
    ```
* **Actualizar dependencias**
  * **Actualizar todas las dependencias:**

    Para actualizar todas las dependencias a sus versiones más recientes permitidas por el rango de versiones en **`package.json`**.

    ```bash
    npm update
    ```
  * **Actualizar una dependencia específica:**

    Para actualizar una dependencia específica a su versión más reciente permitida por el rango de versiones en **`package.json`**.

    ```bash
    npm update nombre-del-paquete
    ```
* **Eliminar dependencias**
  * **Eliminar una dependencia de producción:**

    Usa el siguiente comando para eliminar una dependencia y eliminarla automáticamente del campo **`dependencies`** en tu **`package.json`**.

    ```bash
    npm uninstall nombre-del-paquete
    ```
  * **Eliminar una dependencia de desarrollo:**

    Usa el siguiente comando para eliminar una dependencia y eliminarla automáticamente del campo **`devDependencies`** en tu **`package.json`**.

    ```bash
    npm uninstall nombre-del-paquete --save-dev
    ```

3. **Beneficios de gestionar dependencias con npm**

* **Facilidad de instalación y actualización:** Simplifica la instalación y actualización de dependencias mediante comandos sencillos.
* **Control de versiones:** Permite especificar versiones exactas de dependencias, garantizando que el proyecto utilice las versiones correctas.
* **Reducción de tamaño del código:** Permite reutilizar código de terceros, lo que puede reducir el tamaño del código que necesitas escribir y mantener.
* **Automatización de tareas:** Permite automatizar tareas comunes (como la ejecución de pruebas o la construcción del proyecto) mediante comandos definidos en el archivo **`package.json`**.




---

[Next Page](/llms-full.txt/1)

