El propósito de este artículo no es repetir la Solidity Style Guide oficial, la cual deberías leer. Más bien, es documentar las desviaciones comunes de la guía de estilo que surgen en las revisiones de código o auditorías. Algunos de los puntos aquí no están en la guía de estilo, pero son errores estilísticos comunes de los desarrolladores de Solidity.
Las primeras dos líneas
1. Incluir SPDX-License-Identifier
Por supuesto que tu código compilará sin él, pero obtendrás una advertencia, así que simplemente haz que la advertencia desaparezca.
2. Fijar el pragma de Solidity a menos que se escriba una librería
Probablemente hayas visto pragmas como los siguientes:
pragma solidity ^0.8.0;
y
pragma solidity 0.8.21;
¿Cuál deberías usar y cuándo? Si tú eres quien compila y despliega el contrato, conoces la versión de Solidity con la que estás compilando, por lo que en aras de la claridad, deberías fijar la versión de Solidity al compilador que estás utilizando.
Por otro lado, si estás creando una librería para que otras personas la extiendan, como hacen OpenZeppelin y Solady, no deberías fijar el pragma porque no sabes qué versión del compilador estará usando el usuario final.
Importaciones
3. Establecer explícitamente la versión de la librería en la declaración de import
En lugar de hacer esto:
import "@openzepplin/contracts/token/ERC20/ERC20.sol";
Haz esto:
import "@openzeppelin/[email protected]/token/ERC20/ERC20.sol";
Puedes obtener la última versión haciendo clic en el menú desplegable de la rama en el lado izquierdo de Github y haciendo clic en tags, luego eligiendo el último lanzamiento (release). Usa la última versión limpia (que no sea rc, es decir, non-release-candidate).

Si no versionas el import y la librería subyacente se actualiza, es posible que tu código ya no compile o se comporte de manera inesperada.
4. Usar imports nombrados en lugar de importar todo el namespace
En lugar de hacer esto
import "@openzeppelin/[email protected]/token/ERC20/ERC20.sol";
haz esto
import {ERC20} from "@openzeppelin/[email protected]/token/ERC20/ERC20.sol";
Si se definen múltiples contratos o librerías dentro del archivo de import, contaminarás el namespace. Esto resultará en código muerto si el optimizador del compilador no lo elimina (de lo cual no deberías depender).
5. Eliminar imports no utilizados
Si utilizas una herramienta de seguridad de smart contracts como Slither, esto se detectará automáticamente. Pero asegúrate de eliminarlos. No tengas miedo de borrar código.
Nivel de Contrato
6. Aplicar NatSpec a nivel de contrato
El propósito de NatSpec (especificación de lenguaje natural) es proporcionar una documentación en línea que sea fácilmente legible para los humanos.
A continuación se muestra un ejemplo de NatSpec para un contrato.
/// @title Liquidity token for Foo protocol
/// @author Foo Incorporated
/// @notice Notes for non-technical readers/
/// @dev Notes for people who understand Solidity
contract LiquidityToken {
}
7. Organizar la estructura del contrato según la guía de estilo
Las funciones deben ordenarse por “externalidad” primero y luego por su “capacidad de cambiar el estado” en segundo lugar.
Deben ordenarse de la siguiente manera: funciones receive y fallback (si aplica), funciones external, funciones public, funciones internal y funciones private.
Dentro de esos grupos, las funciones payable van arriba, luego las non-payable, luego las view, y luego las pure.
contract ProperLayout {
// type declarations, e.g. using Address for address
// state vars
address internal owner;
uint256 internal _stateVar;
uint256 internal _starteVar2;
// events
event Foo();
event Bar(address indexed sender);
// errors
error NotOwner();
error FooError();
error BarError();
// modifiers
modifier onlyOwner() {
if (msg.sender != owner) {
revert NotOwner();
}
_;
}
// functions
constructor() {
}
receive() external payable {
}
falback() external payable {
}
// functions are first grouped by
// - external
// - public
// - internal
// - private
// note how the external functions "descend" in order of how much they can modify or interact with the state
function foo() external payable {
}
function bar() external {
}
function baz() external view {
}
function qux() external pure {
}
// public functions
function fred() public {
}
function bob() public view {
}
// internal functions
// internal view functions
// internal pure functions
// private functions
// private view functions
// private pure functions
}
Constantes
8. Reemplazar números mágicos con constantes
Si ves el número 100 simplemente ahí en el código, ¿qué es? ¿100 por ciento? ¿100 puntos básicos?
Generalmente, los números deberían escribirse como una constante en la parte superior del contrato.
9. Si los números se usan para medir Ether o tiempo, usar las palabras clave de Solidity
En lugar de escribir
uint256 secondsPerDay = 60 * 60 * 24;
haz
1 days
en lugar de escribir
require(msg.value == 10**18 / 10, "must send 0.1 ether");
haz
require(msg.value == 0.1 ether, "must send 0.1 ether");
10. Usar guiones bajos para hacer los números grandes más legibles
En lugar de hacer esto
uint256 private constant BASIS_POINTS_DENOMINATOR = 10000
haz esto
uint256 private constant BASIS_POINTS_DENOMINATOR = 10_000
Funciones
11. Eliminar el modificador virtual de las funciones que no serán sobrescritas
El modificador virtual significa “sobrescribible por un contrato hijo”. Pero si sabes que no vas a sobrescribir la función (porque tú eres quien despliega), entonces este modificador es superfluo. Simplemente bórralo.
12. Poner los modificadores de función en el orden correcto de visibilidad, mutabilidad, virtual, override y modificador personalizado
Lo siguiente es correcto
// visibility (payability), [virtual], [override], [custom]
function foo() public payable onlyAdmin {
}
function bar() internal view virtual override onlyAdmin {
}
13. Usar NatSpec adecuadamente
A veces referido como “estilo de comentarios de Solidity”, su nombre correcto es NatSpec:
Las reglas son similares al NatSpec del contrato, excepto que también especificamos los param basados en los argumentos de la función y lo que se devuelve.
Esta puede ser una buena manera de describir los nombres de los argumentos sin usar variables de argumento largas.
/// @notice Deposit ERC20 tokens
/// @dev emits a Deposit event
/// @dev reverts if the token is not allowlisted
/// @dev reverts if the contract is not approved by the ERC20
/// @param token The address of the ERC20 token to be deposited
/// @param amount The amount of ERC20 tokens to deposit
/// @returns the amount of liquidity tokens the user receives
function deposit(address token, uint256 amount) public returns (uint256) {
}
// If the contract inherits functions, you can also inherit their NatSpec
/// @inheritdoc Lendable
function calculateAccumulatedInterest(address token, uint256 since) public override view returns (uint256 interest) {
}
Para los parámetros dev, es bueno notificar qué tipo de cambios de estado puede realizar, por ejemplo, emitir un event, enviar Ether, hacer selfdestruct, etc.
El notice y param de NatSpec son leídos por Etherscan.

Puedes ver de dónde obtuvo Etherscan esa información en la siguiente captura de pantalla del código.

Limpieza general
14. Eliminar el código comentado
Esto debería explicarse por sí mismo. Si el código está comentado, es solo desorden.
15. Pensar cuidadosamente en los nombres de las variables
Nombrar cosas es uno de los aspectos más difíciles de escribir buen código, pero hará maravillas por la legibilidad.
Algunos consejos:
-
Evita “sustantivos genéricos” como “user”. Sé más preciso, por ejemplo “admin”, “buyer”, “seller”.
-
La palabra “data” suele ser un indicador de imprecisión. En lugar de “userData” usa “userAccount”.
-
No uses dos sustantivos diferentes para aplicar a la misma entidad del mundo real. Por ejemplo, si “depositor” y “liquidityProvider” se refieren a la misma entidad en el mundo real, apégate a un solo término, no uses ambos en el código.
-
Incluye unidades en los nombres de las variables. En lugar de “interestRate” usa “interestRatesBasisPoints” o “feeInWei”.
-
Las funciones que cambian el estado deben tener un verbo en el nombre.
-
Sé consistente sobre el uso de guiones bajos para distinguir variables y funciones internal frente a los argumentos de la función que ocultan variables de estado. Si anteponer una variable con un guion bajo significa “internal”, asegúrate de que no se use para significar otra cosa en otro contexto, por ejemplo, argumentos de función que tienen el mismo nombre que una variable de estado.
-
Usar “get” para ver datos y “set” para cambiar datos es una convención de programación ampliamente seguida. Considera incorporarla.
-
Después de terminar de escribir tu código, aléjate de la computadora, luego regresa en 15 minutos y pregúntate para cada nombre de variable y función si fuiste lo más preciso posible. Este esfuerzo deliberado te hará más bien que cualquier lista de verificación, ya que conoces la intención del código base mejor que nadie.
Trucos adicionales para organizar bases de código grandes
-
Si tienes muchas variables de storage, puedes definir todas las variables de storage en un solo contrato, y luego heredar de ese contrato para obtener acceso a esas variables.
-
Si tus funciones requieren una cantidad significativa de parámetros, usa un struct para pasar la información.
-
Si necesitas muchos imports, puedes importar todos los archivos y tipos en un solo archivo de Solidity, y luego importar ese archivo (tendrías que romper intencionalmente la regla sobre imports nombrados).
-
Usa librerías para agrupar funciones de la misma categoría y hacer los archivos más pequeños.
Organizar bases de código grandes es un arte. La mejor manera de aprenderlo es estudiar el código de grandes proyectos ya establecidos.
Aprende más con RareSkills
Esta lista de verificación se utiliza en nuestro Solidity bootcamp avanzado para revisiones de código.
Publicado originalmente el 12 de agosto de 2024