Saltar al contenido principal
Change page

Anatomía de los contratos inteligentes

Un contrato inteligente es un programa que se ejecuta en una dirección en Ethereum. Están compuestos por datos y funciones que pueden ejecutarse al recibir una transacción. A continuación, se presenta una descripción general de lo que compone un contrato inteligente.

Requisitos previos

Asegúrese de haber leído primero sobre los contratos inteligentes. Este documento asume que ya está familiarizado con lenguajes de programación como JavaScript o Python.

Datos

Cualquier dato del contrato debe asignarse a una ubicación: ya sea a storage o a memory. Modificar el almacenamiento en un contrato inteligente es costoso, por lo que debe considerar dónde deben residir sus datos.

Almacenamiento

Los datos persistentes se denominan almacenamiento y están representados por variables de estado. Estos valores se almacenan permanentemente en la cadena de bloques. Debe declarar el tipo para que el contrato pueda realizar un seguimiento de cuánto almacenamiento necesita en la cadena de bloques cuando se compila.

// Ejemplo de Solidity
contract SimpleStorage {
    uint storedData; // Variable de estado
    // ...
}
# Ejemplo de Vyper
storedData: int128

Si ya ha programado en lenguajes orientados a objetos, es probable que esté familiarizado con la mayoría de los tipos. Sin embargo, address debería ser nuevo para usted si es nuevo en el desarrollo de Ethereum.

Un tipo address puede contener una dirección de Ethereum, lo que equivale a 20 bytes o 160 bits. Se devuelve en notación hexadecimal con un 0x inicial.

Otros tipos incluyen:

  • booleanos
  • enteros
  • números de punto fijo
  • matrices de bytes de tamaño fijo
  • matrices de bytes de tamaño dinámico
  • literales racionales y enteros
  • literales de cadena
  • literales hexadecimales
  • enumeraciones (enums)

Para obtener más explicaciones, eche un vistazo a la documentación:

Memoria

Los valores que solo se almacenan durante la vida útil de la ejecución de una función de contrato se denominan variables de memoria. Dado que no se almacenan permanentemente en la cadena de bloques, su uso es mucho más económico.

Obtenga más información sobre cómo la EVM almacena datos (almacenamiento, memoria y la pila) en la documentación de Solidity (opens in a new tab).

Variables de entorno

Además de las variables que define en su contrato, existen algunas variables globales especiales. Se utilizan principalmente para proporcionar información sobre la cadena de bloques o la transacción actual.

Ejemplos:

PropiedadVariable de estadoDescripción
block.timestampuint256Marca de tiempo de la época del bloque actual
msg.senderaddressRemitente del mensaje (llamada actual)

Funciones

En los términos más simples, las funciones pueden obtener información o establecer información en respuesta a las transacciones entrantes.

Hay dos tipos de llamadas a funciones:

  • internal: estas no crean una llamada a la EVM
    • A las funciones internas y a las variables de estado solo se puede acceder internamente (es decir, desde dentro del contrato actual o de los contratos que derivan de él)
  • external: estas sí crean una llamada a la EVM
    • Las funciones externas forman parte de la interfaz del contrato, lo que significa que pueden ser llamadas desde otros contratos y mediante transacciones. Una función externa f no puede ser llamada internamente (es decir, f() no funciona, pero this.f() sí funciona).

También pueden ser public o private

  • Las funciones public pueden ser llamadas internamente desde dentro del contrato o externamente a través de mensajes
  • Las funciones private solo son visibles para el contrato en el que están definidas y no en los contratos derivados

Tanto las funciones como las variables de estado pueden hacerse públicas o privadas

Aquí hay una función para actualizar una variable de estado en un contrato:

// Ejemplo de Solidity
function update_name(string value) public {
    dapp_name = value;
}
  • El parámetro value de tipo string se pasa a la función: update_name
  • Está declarada como public, lo que significa que cualquiera puede acceder a ella
  • No está declarada como view, por lo que puede modificar el estado del contrato

Funciones de vista (View)

Estas funciones prometen no modificar el estado de los datos del contrato. Ejemplos comunes son las funciones "getter" (obtener); podría usar esto para recibir el saldo de un usuario, por ejemplo.

// Ejemplo de Solidity
function balanceOf(address _owner) public view returns (uint256 _balance) {
    return ownerPizzaCount[_owner];
}
dappName: public(string)

@view
@public
def readName() -> string:
  return dappName

Qué se considera modificar el estado:

  1. Escribir en variables de estado.
  2. Emitir eventos (opens in a new tab).
  3. Crear otros contratos (opens in a new tab).
  4. Usar selfdestruct.
  5. Enviar ether mediante llamadas.
  6. Llamar a cualquier función que no esté marcada como view o pure.
  7. Usar llamadas de bajo nivel.
  8. Usar ensamblador en línea (inline assembly) que contenga ciertos códigos de operación (opcodes).

Funciones constructoras

Las funciones constructor solo se ejecutan una vez cuando el contrato se despliega por primera vez. Al igual que constructor en muchos lenguajes de programación basados en clases, estas funciones a menudo inicializan variables de estado con sus valores especificados.

# Ejemplo de Vyper

@external
def __init__(_beneficiary: address, _bidding_time: uint256):
    self.beneficiary = _beneficiary
    self.auctionStart = block.timestamp
    self.auctionEnd = self.auctionStart + _bidding_time

Funciones integradas

Además de las variables y funciones que define en su contrato, existen algunas funciones integradas especiales. El ejemplo más obvio es:

  • address.send() – Solidity
  • send(address) – Vyper

Estas permiten a los contratos enviar ETH a otras cuentas.

Escribir funciones

Su función necesita:

  • variable de parámetro y tipo (si acepta parámetros)
  • declaración de interna/externa (internal/external)
  • declaración de pure/view/payable
  • tipo de retorno (si devuelve un valor)

Un contrato completo podría verse algo así. Aquí, la función constructor proporciona un valor inicial para la variable dapp_name.

Eventos y registros

Los eventos permiten que su contrato inteligente se comunique con su frontend u otras aplicaciones suscritas. Una vez que una transacción se valida y se agrega a un bloque, los contratos inteligentes pueden emitir eventos y registrar información, que el frontend puede procesar y utilizar.

Ejemplos comentados

Estos son algunos ejemplos escritos en Solidity. Si desea jugar con el código, puede interactuar con ellos en Remix (opens in a new tab).

Hola mundo

Token

Activo digital único

Lecturas adicionales

Consulte la documentación de Solidity y Vyper para obtener una descripción más completa de los contratos inteligentes: