Cambios introducidos en Solidity v0.8.0
Esta sección versa sobre los principales cambios introducidos en la versión 0.8.0 de Solidity. Para ver la lista completa registro de cambios de lanzamiento.
Cambios silenciosos en la semántica
Esta sección enumera los cambios en los que el código existente cambia su comportamiento sin que el compilador le notifique nada al respecto.
Las operaciones aritmeticas se revertirán en underflow y overflow. Puede escribir
unchecked { ... }
para usar el comportamiento anterior del adaptador.Comprobar posibles problemas por overflow es bastante común, por lo que los hicimos predeterminados para aumentar la legibilidad del código, aunque eso signifique un ligero aumento del gas.
ABI coder v2 está activado por defecto.
Puede usar el comportamiento anterior con
pragma abicoder v1;
. La directivapragma experimental ABIEncoderV2;
aún es válida, pero está obsoleta y no tiene ningún efecto. Si quiere ser explícitco, usepragma abicoder v2;
.Tenga en cuenta que el ABI coder v2 admite más tipos que v1 y realiza más controles sanitarios en los inputs. ABI coder v2 encarece algunas llamadas de función y también puede hacer que algunas llamadas al contrato sean revertidas y que, sin embargo, no se revertirían con ABI coder v1, cuando contienen datos que no se ajustan a los tipos de parámetros.
La exponienciación es asociativa a derechas, por ejemplo, la expresión
a**b**c
es analizada comoa**(b**c)
. Antes de la versión 0.8.0, era analizada como(a**b)**c
.Esta es la forma común de analizar el operador de exponenciación.
Los asserts y otras verificaciones inernas como la división por cero o el desbordamiento aritmético no usan el opcode invalid, en su lugar usan el opcode revert. Más específicamente, usarán datos de erro equivalentes a una llamada a la función
Panic(uint256)
con un código de error acorde a las circunstancias.Esto ahorrará gas en errores mientras que permite a las herramientas de análisis estático distinguir estas situaciones entre un revert y un input inválido, como por ejemplo un fallo en
require
.Si se accede a un byte array en el almacenamiento cuya longitud está codificada incorrectamente, se generará un panic. Un contrato no puede entrar en esta situación a menos que se use inline assembly para modificar la representación raw de los byte arrays del almacenamiento (storage)
Si se usan constantes en expresiones de longitud de array, las versiones previas de Solidity usarían precisión arbitraria en todas las ramas del arbol de decisiones. Ahora, is las variables constantes se usan como expresiones intermedias, sus valores serán propiamente redondeados de la misma forma que las expresiones en tiempo de ejecución.
El tipo
byte
se ha eliminado. Era un alias debytes1
.
Nuevas restriciones
Esta sección enumera los cambios que pueden ocasionar que los contratos existentes no vuelvan a compilar nunca.
Hay nuevas restricciones relacionadas con conversiones explícitas de literales. El comportamiento anterior en los siguientes casos probablemente era ambiguo:
Conversiones explícitas de literales negativos y literales mayores que
type(uint160).max
aaddress
no están permitidas.Las conversiones explícitas entre literales y un tipo entero
T
solo se permiten si el literal se encuentra entretipo(T).min
ytipo(T).max
. En particular, reemplace los usos deuint(-1)
contipo(uint).max
.- Las conversiones explícitas entre literales y enumeraciones solo se permiten si el literal puede
representan un valor del enumerado.
Las conversiones explícitas entre literales y el tipo
address
(por ejemplo,dirección(literal)
) tienen el
tipo
address
en lugar deaddress payable
. Se puede obtener un tipo de payable address utilizando una conversión explícita, es decir,payable(literal)
.Address literals tienen el tipo
address
en lugar deaddress payable
. Se pueden convertir aaddress payable
usando una conversión explícita, por ejemplopayable(0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF)
.Hay nuevas restricciones en las conversiones de tipos explícitos. La conversión sólo se permite cuando hay como máximo un cambio de signo, tamaño o la categoría de tipo (
int
,address
,bytesNN
, etc.). Para realizar varios cambios, utilice varias conversiones.Use la notación
T(S)
para denotar la conversión explícitaT(x)
, donde,T
yS
son tipos, yx
es cualquier variable arbitraria de tipoS
. Un ejemplo de tal conversión no permitida seríauint16(int8)
ya que cambia el tamaño (8 bits a 16 bits) y signo (entero con signo a entero sin signo). Para hacer la conversión, tiene que ir a través de un tipo intermedio. En el ejemplo anterior, seríauint16(uint8(int8))
ouint16(int16(int8))
. Tenga en cuenta que las dos formas de convertir producirán resultados diferentes, por ejemplo, para-1
. Los siguientes son algunos ejemplos de conversiones que no están permitidas por esta regla.address(uint)
yuint(address)
: conversión simultanea de la categoría del tipo y tamaño. Reemplace esto poraddress(uint160(uint))
yuint(uint160(address))
respectivamente..payable(uint160)
,payable(bytes20)
ypayable(integer-literal)
: conversión simultanea de la categoría del tipo and estado de la mutabilidad. Reemplace esto porpayable(address(uint160))
,payable(address(bytes20))
ypayable(address(integer-literal))
respectivamente. Tenga en cuenta quepayable(0)
es válido y no es una excepción de la regla.int80(bytes10)
ybytes10(int80)
: conversión simultanea de la categoría del tipo y signo. Reemplace esto porint80(uint80(bytes10))
ybytes10(uint80(int80)
respectivamente.Contract(uint)
: conversión simultanea de la categoría del tipo y el tamaño. Reemplace esto porContract(address(uint160(uint)))
.
Estas conversiones fueron deshabilitadas para evitar ambiguedades. Por ejemplo, en la expresión
uint16 x = uint16(int8(-1))
, el valor dex
dependería de que conversión, el signo ó el ancho, se aplicará antes.Las opciones de llamada de función solo se pueden dar una vez, es decir,
c.f{gas: 10000}{value: 1}()
no es válido y debe cambiarse a ``c.f{gas: 10000, value: 1}() ``.Las funciones globales
log0
,log1
,log2
,log3
ylog4
han sido eliminadas.Estas son funciones de bajo nivel que en gran parte se dejaron de utilizar. Se puede acceder a su comportamiento desde el inline assembly.
Las definiciones de
enum
no pueden contener más de 256 miembros.Esto hará que sea seguro asumir que el tipo subyacente en la ABI siempre sea
uint8
.Las declaraciones con el nombre
this
,super
y_
no están permitidas, con la excepción de funciones y eventos públicos. La excepción es hacer posible declarar interfaces de contratos implementados en lenguajes distintos a Solidity que permiten tales nombres de funciones.Eliminada la compatibilidad con las secuencias de escape
\b
,\f
y\v
en el código. Todavía se pueden insertar a través de carácteres de escapes hexadecimales, p.\x08
,\x0c
y\x0b
, respectivamente.Las variables globales
tx.origin
ymsg.sender
tienen el tipoaddress
en lugar deaddress payable
. Se pueden convertir enaddress payable
usando una conversión explícita, por ejemplo,payable(tx.origin)
opayable(msg.sender)
.This change was done since the compiler cannot determine whether or not these addresses are payable or not, so it now requires an explicit conversion to make this requirement visible.
Este cambio se realizó ya que el compilador no puede determinar si estas direcciones son payable ó no, por lo que ahora requiere una conversión explícita para cumplir este requisito.
La conversión explícita al tipo
address
siempre devuelve un tipo not-payableaddress
. En particular, Las siguientes conversiones explícitas tienen el tipoaddress
en lugar deaddress payable
:address(u)
dondeu
es una variable de tipouint160
. Se puede convertioru
en el tipoaddress payable
usando dos conversiones explícitas, por ejemplo,payable(address(u))
.address(b)
dondeb
es una variable de tipobytes20
. Se puede convertirb
en el tipoaddress payable
usando dos conversiones explícitas, por ejemplo,payable(address(b))
.address(c)
dondec
es un contrato. Previamente, el tipo de retorno de esta conversión dependía si el contrato podía recibir Ether (bien por que tenía una función receive o bien por unafunción payable fallback). La conversión
payable(c)
tiene el tipo ``addresspayable`` y solamente está permitida cuando el contrato
c
puede recibir Ether. En general, siempre se puede convertirc
en el tipoaddress payable
usando la siguiente conversión explícita:payable(address(c))
. Tenga en cuenta queaddress(this)
pertenece a la misma categoría queaddress(c)
y se aplican las misma reglas.
El
chainid
incorporado en el inline assembly ahora se consideraview
en lugar depure
.La negación unaria ya no se puede usar, solo para enteros con signo.
Cambios en el interface
La salida de
--combined-json
ha cambiado: Los campos JSONabi
,devdoc
,userdoc
ystorage-layout
ahora son subobjetos. Antes de la versión 0.8.0 se usaban serializados como strings.El «legacy AST» ha sido eliminado (
--ast-json
en el interfaz de linea de comandoslegacyAST
para el standard JSON). Use «compact AST» (--ast-compact--json
paraAST
) en su lugar.El antigüo informador (
--old-reporter
) ha sido eliminado.
Como actualizar su código
Si confía en la aritmética subyacente, encuelva cada operación con
unchecked { ... }
.Opcional: Si usa SafeMath o una librería similar, cambie
x.add(y)
ax + y
,x.mul(y)
ax * y
etc.Añada
pragma abicoder v1;
si quiere mantener el antiggüo codificador de ABI.Opcionalmente elimine
pragma experimental ABIEncoderV2
opragma abicoder v2
ya que es redundante.Cambie
byte
abytes1
.Agregue conversiones de tipo explícitas intermedias si es necesario.
Combine
c.f{gas: 10000}{value: 1}()
ac.f{gas: 10000, value: 1}()
.Cambie
msg.sender.transfer(x)
apayable(msg.sender).transfer(x)
ó use una variable de almacenamiento de tipoaddress payable
.Cambie
x**y**z
a(x**y)**z
.Use inline assembly reemplazando
log0
, …,log4
.Niegue los enteros sin signo restándolos del valor máximo del tipo y sumando 1 (por ejemplo
type(uint256).max - x + 1
, mientras se asegura que x no es cero)