Contacto
Similar a la clase, puede ser: abstracta, heredada y convocada por otros contratos.
Uso típico:
- Crear un nuevo contrato:
new MyContract(...)
- Utilice el contrato desplegado:
MyContract($address)
visibilidad
visibilidad | similar | aplicar para | accesible desde el exterior | Los subcontratos son accesibles |
---|---|---|---|---|
externo | función | √ | ||
público | público | función + variable de estado | √ | √ |
interno | protegido | función + variable de estado | √ | |
privado | privado | función + variable de estado |
Nota: Para las variables públicas, el captador correspondiente se generará automáticamente (consulte: Guía de desarrollo no autorizada de Ethers.js (continuación) para obtener más detalles ).
Elementos clave
elemento | ilustrar | ejemplo |
---|---|---|
Variables de estado | Almacenado permanentemente en la cadena, toma gas | uint data; |
función | Hay dos tipos de lectura/escritura, y el método de escritura necesita consumir gas; puede existir dentro y fuera del contrato | function func() public {...} |
retroceder() | No se puede llamar directamente desde el exterior, se ejecutará cuando no haya ninguna función en el contrato de solicitud | fallback() external { ... } |
recibir() | No se puede llamar directamente externamente, se ejecuta al recibir eth | receive() external payable {...} |
modificador | Restricciones declarativas reutilizables, ejecutadas antes de las llamadas a funciones. | Declaración: modifier onlyOwner(){...} Uso:function func() public onlyOwner {...} |
evento | El registro de ejecución en la cadena se puede consultar en el futuro. | emit Event1(data); |
estructura | tipo personalizado | struct MyType { uint item1; bool item2; } |
error | excepción personalizada | Declaración: error MyError(unit reason); Uso:revert MyError(200); |
enumerar | La mejor opción para valores constantes finitos | enum State { Created, Locked, Inactive } |
Nota:
-
pagadero, la función que recibe eth debe agregar
-
view o pure, lo que indica que la función no cambiará el estado de Ethereum
-
funciones de respaldo y recepción
- Ninguno puede tener un nombre de función, por lo que no hay
function
palabras clave. - debe usar
external
- La función alternativa también se puede pagar, pero se recomienda utilizar primero la función de recepción.
- Las funciones de respaldo y recepción pagables pueden consumir hasta 2300 de gas, lo que debe probarse aquí.
- El respaldo ordinario no tiene esta limitación, siempre que el gas sea suficiente, se puede realizar cualquier operación compleja.
- Ninguno puede tener un nombre de función, por lo que no hay
Para obtener detalles sobre cómo usar registros de eventos y consultas en dapps, consulte: Guía de desarrollo no autorizado de Ethers.js (Parte 2)
Interfaz
De forma similar a las interfaces en otros idiomas, puede:
- Heredar otras interfaces
- Solo declaraciones de métodos, nada más.
- Todos los métodos son externos.
Biblioteca
Similar al contrato, pero:
- no puede usar variables de estado
- no puede heredar o ser heredado
- no puedo recibir eth
- No puede ser ejecutado de forma independiente y debe ser referenciado por otros contratos.
La relación entre ambos es similar: contrato, archivo ejecutable; biblioteca, biblioteca de enlaces dinámicos
tipo de datos
Tipos de valor y tipos de referencia
similar | |
---|---|
tipo de valor | bool, uint / int, dirección, byte, enumeración |
tipo de referencia | Matriz (como bytes / cadena), estructura, mapeo |
Nota:
-
matrices de bytes y cadenas
keccak256(abi.encodePacked(s1)) == keccak256(abi.encodePacked(s2))
bytes bs = 'bytes';
string str = 'string';
byte1 b1 = 'a';
byte2 b2 = 256;
, 256 supera el tamaño de un solo byte- Matriz de bytes de longitud fija: byte1 ~ byte32
- Datos de bytes dinámicos: bytes y cadena, donde la cadena se codifica en utf-8.
- Solidity no proporciona un método para la comparación de cadenas, pero se puede hacer con la ayuda de una función hash.
-
formación
-
int[] age = [10, 20, 30, 40, 50];
-
int[] age = new int[](5);
-
int[5] age = [10, 20, 30, 40, 50];
-
Las matrices de longitud fija no se pueden inicializar con nuevas
-
Las matrices dinámicas pueden usar una asignación nueva o inicial al mismo tiempo
-
Hay dos tipos de dirección:
address
yaddress payable
, frente a la primera, la segunda tiene más funciones de transferencia.
ubicación de almacenamiento
similar | ejemplo | |
---|---|---|
almacenamiento | Persistencia, memoria global en el contrato | Variables de estado |
memoria | Memoria local de la función, no persistente | entrada de función |
datos de llamada | Parámetros de entrada de funciones, no persistentes | entrada de función |
pila | Pila de llamadas de EVM |
Reglas relevantes:
-
Priorice los datos de llamadas porque pueden evitar la copia y no se pueden modificar
-
Función variables locales:
mapping (uint => address) storage localNames = names;
- Cuando es almacenamiento, debe apuntar a la variable de estado externa
- tipo de valor, memoria
- Tipo de referencia, el valor predeterminado es almacenamiento, pero se puede especificar como memoria.
- mapeo, almacenamiento, siempre apuntan a variables de estado externas. El siguiente ejemplo
names
es una variable de estado definida en un contrato, y su tipo también es mapeo.
-
Reglas de asignación
-
tipo de valor, haciendo una copia independiente
-
tipo de referencia, copia de referencia
-
La asignación entre almacenamiento y memoria/calldata siempre produce una copia independiente.
-
Asignación entre variables de memoria
-
almacenamiento Asigna un valor a una variable de almacenamiento local, copiando la referencia.
-
Otras asignaciones de almacenamiento siempre generan copias independientes.
variables globales y métodos
ilustrar | ejemplo | |
---|---|---|
unidad ética | wei, wei, éter | 1 gwei |
unidad de tiempo | segundos, minutos, horas, días, semanas | 1 minutes |
bloquear | objeto de bloque | |
bloquehash() | Si el parámetro de entrada es uno de los últimos 256 bloques, será su hash. De lo contrario, 0. | |
mensaje | objeto de mensaje | |
tx | objeto de transmisión | |
izquierda gas() | gas restante | |
abi | objeto abi | |
DIRECCIÓN | objeto de dirección | |
este | El objeto del contrato actual, que se puede convertir explícitamente en dirección | address(this).balance |
tipo() | clasificar información | |
añadir mod | (a + b) %k | |
mulmod | (a * b) %k | |
función hash | keccak256,sha256,ripemd160 | |
ecrecover | Recuperar la dirección de la firma |
详见:Unidades y variables disponibles globalmente — documentación de Solidity - 0.8.18
Nota:
-
La diferencia entre tx.orgin y msg.sender
- tx.orgin es la primera cuenta que inicia tx y su valor siempre es eoa.
- msg.sender es la cuenta de llamada directa de la función actual, que puede ser eoa o dirección de contrato.
-
Asegúrese de que el primer parámetro de ecrecover sea un hash de firma de mensaje eth válido, lo que se puede hacer con la ayuda de la clase de herramientas ecdsa de openzepplin.
-
Se usa primero
address.transfer
, y cuando falla,transfer
lanza una excepción ysender
regresafalse
. -
Los métodos de bajo nivel (llamada, llamada delegada, llamada estática, envío, transferencia) en la dirección tienen dos lados:
-
Son baratos de ejecutar debido a la falta de controles de tiempo de ejecución como tipo, existencia, etc.
-
Por lo tanto, no es seguro.
-
El uso de llamada, llamada delegada y llamada estática en la dirección es similar, pero los escenarios de aplicación son diferentes:
-
llamada, aplicada al contrato
-
llamada delegada, aplicada a la biblioteca
-
staticcall, aplicado a métodos de contrato de solo lectura, a saber, métodos de vista o puros, de lo contrario, se lanzará una excepción.
-
Una típica llamada address.call:
bytes memory payload = abi.encodeWithSignature("register(string)", "MyName");
(bool success, bytes memory returnData) = address(nameReg).call(payload);
require(success);
- Si necesita ajustar el gas y enviar eth, entonces:
address(nameReg).call{gas: 1000000, value: 1 ether}(abi.encodeWithSignature("register(string)", "MyName"));
manejo de excepciones
Tipo de excepción:
- Pánico, error interno, como división por cero.
- Error, excepción general.
Después de que el contrato arroja una excepción, el estado se revierte. Actualmente hay 3 formas:
-
require (expresión), si la expresión es falsa, se lanza una excepción y se devuelve el gas no utilizado
- Es adecuado para el parámetro de entrada de la función de verificación y arroja Error.
- La versión actual de require no se puede usar con tipos de error personalizados. Si es necesario, use la combinación de "declaración condicional + revertir".
-
afirmar (expresión), igual que arriba, pero el gas no utilizado no se reembolsará y se consumirá en su totalidad
-
Adecuado para verificar el estado interno y lanzar Panic.
-
revert(), lanzar Error directamente o personalizar Error, similar a lanzar en otros idiomas.
Ejemplo de una sentencia try...catch:
try feed.getData(token) returns (uint v) {
return (v, true);
} catch Error(string memory /*reason*/) {
// require 导致
errorCount++;
return (0, false);
} catch Panic(uint /*errorCode*/) {
// assert 导致
errorCount++;
return (0, false);
} catch (bytes memory /*lowLevelData*/) {
// revert 导致
errorCount++;
return (0, false);
}