Este artículo utiliza la ethers
biblioteca como implementación subyacente y describe las propiedades detalladas del objeto de transacción Ethereum construido en Javascript. Este artículo asume que el lector tiene ciertos conocimientos básicos de Ethereum.
1. ¿Qué es una ethers
biblioteca?
La siguiente es una introducción original de su documento:
La biblioteca ethers.js pretende ser una biblioteca completa y compacta para interactuar con Ethereum Blockchain y su ecosistema. Originalmente fue diseñado para usarse con ethers.io y desde entonces se ha expandido a una biblioteca mucho más de uso general.
El significado aproximado es ethers.js
una biblioteca completa y compacta aplicada a Ethereum y su ecosistema. Originalmente fue diseñado para ser utilizado en ethers.io y poco a poco se expandió a una biblioteca multifuncional.
ethers
La biblioteca tiene las siguientes características:
- La clave privada se almacena en el cliente, segura y sin riesgos.
- Admite carteras de importación y exportación en formato json (se puede usar para Geth o Parity)
- Admite importar y exportar palabras mnemotécnicas y billetera de hardware, las palabras mnemotécnicas admiten varios idiomas
- Admite múltiples formatos ABI, incluidos ABIv2 y ABI legible por humanos
- Admite múltiples formas de conectarse a los nodos de Ethereum, como JSON-RPC, INFURA, Etherscan o MetaMask.
- Los nombres ENS son totalmente compatibles como primera categoría de elementos.
- La biblioteca es muy pequeña (versión sin comprimir 284kb, versión comprimida 88kb)
- Funciones completas para satisfacer todas sus necesidades de Ethereum
- Documentación detallada
- Se agregaron muchos casos de prueba.
- TypeScript legible
- Certificado MIT (incluidas sus dependencias), código completamente abierto
En segundo lugar, construya un objeto comercial
En ethers.js
, un objeto de transacción de Ethereum es un objeto ordinario {}
, que contiene los siguientes atributos opcionales:
- a
- gasLimit
- GasPrice
- mientras tanto
- datos
- valor
- chainId
Los atributos anteriores son todos opcionales, lo que significa que se pueden omitir, pero no todos, debe haber al menos un atributo. Creamos un objeto de transacción de la siguiente manera:
// All properties are optional
let transaction = {
nonce: 0,
gasLimit: 21000,
gasPrice: utils.bigNumberify("20000000000"),
to: "0x88a5C2d9919e46F883EB62F7b8Dd9d0CC45bc290",
// ... or supports ENS names
// to: "ricmoo.firefly.eth",
value: utils.parseEther("1.0"),
data: "0x",
// This ensures the transaction cannot be replayed on different networks
chainId: ethers.utils.getNetwork('homestead').chainId
}
A continuación, explicamos estos atributos a través de transacciones reales en la red de prueba de Kovan.
3. Explicación detallada de los atributos del objeto de transacción
Existen los siguientes fragmentos de código, que modificaremos o agregaremos a este fragmento en transacciones futuras. Esta es una transacción que crea un contrato:
let data='0x60....'
let provider = ethers.getDefaultProvider('kovan')
let wallet_new = wallet.connect(provider)
let trans = {
data:inputData
}
wallet_new.sendTransaction(trans).then( tx => {
console.log(tx)
}).catch( err => {
console.log(err)
})
Como puede ver, nuestro objeto de transacción solo tiene un atributo data
y su valor es el código de bytes para crear el contrato. Nota: El código de bytes al crear el contrato no es el código de bytes compilado por el contrato de creación, sino el código de bytes que se puede obtener ejecutando el código de bytes del contrato creado.
Echemos un vistazo a la respuesta de transacción impresa (Respuesta de transacción):
puede ver que en el objeto de respuesta de transacción, además de los to
atributos, existen otros atributos. Entonces, el atributo mencionado anteriormente se puede omitir significa que se puede omitir al construir el objeto de transacción. Si se omite, la ethers
biblioteca subyacente la configurará automáticamente. Comencemos con este objeto comercial más simple, aumentemos y expliquemos sus propiedades paso a paso.
3.1 to
Dado que el to
atributo es null
, comenzaremos con el to
atributo. to
Representa la dirección del destinatario de la llamada en la transacción.
Una transacción en Ethereum debe tener un iniciador (cuenta externa, cuenta sin contrato), generalmente from
. Debido a que la ethers
biblioteca que usamos usa la billetera para firmar transacciones, quien firma es quien firma from
. La transacción generalmente tiene un receptor (tanto la cuenta externa como la cuenta del contrato), es decir to
. ¿Por qué es habitual? Porque como en nuestro ejemplo de ahora, no hay un receptor cuando se crea el contrato. Aunque la dirección del contrato se to
devolverá como un atributo después de que se cree el contrato , esta to
dirección está vacía cuando se crea . Echemos un vistazo a la captura de pantalla en etherscan para profundizar en esta impresión:
puede ver que después de que se ejecuta la transacción, este to
atributo es la dirección del nuevo contrato. Permítanme agregar que la dirección del contrato se calcula en función de la dirección de la persona que llama y el número de transacciones (nonce) que la persona que llama ha completado. Por lo tanto, antes del despliegue real de un contrato, se establece la dirección y se puede obtener.
Para resumir, el to
atributo es la dirección del destinatario de la transacción. Específicamente: si está transfiriendo ETH a una cuenta externa, es la dirección de recepción de ETH; si está llamando a un contrato (transferir ETH a una cuenta de contrato también es un contrato de llamada), es la dirección del contrato; si está creando un contrato, porque no hay ningún destinatario en este momento, Solo lo predeterminado.
3.2 data
A continuación se habla de que el código anterior utiliza la propiedad: data
. Durante la transacción, podemos enviar datos de la transacción junto con la transacción. Los datos de transacción pueden ser llamadas a métodos al contrato, o algunos datos sin sentido, que a veces se llaman payload
. En el ejemplo anterior, data
el valor del atributo es el código de bytes del contrato que creamos. Agreguemos un to
atributo al objeto de transacción anterior y modifiquemos data
el valor del atributo:
let trans = {
data:"0x496c6f7665457468657265756d",
to:"0xDD55634e1027d706a235374e01D69c2D121E1CCb"
}
Aquí to
hay una dirección de cuenta externa, que data
es la cadena de "Me encanta Ethereum" convertida en valor hexadecimal ( data
debe 0x
comenzar con). La respuesta después de que se envía la transacción es la siguiente:
Veamos el resultado en etherscan: en
el fragmento de código, podemos ver que enviamos directamente un mensaje (cadena) a una cuenta. En la parte inferior de InputData, muestra datos nativos de forma predeterminada. Seleccione Ver entrada como UTF-8, y se mostrará IloveEthereum. No se muestran espacios aquí porque la herramienta que utilicé no codificó espacios. ¿Esta función de enviar una cadena a una cuenta parece enviar un mensaje corto a un número de teléfono móvil? Incluso puedes enviar un artículo (pero con muchas tarifas), ¿Ethereum es interesante?
Si los datos enviados son los datos cuando se llama al método de contrato, generalmente tiene un formato fijo y no pueden ser datos arbitrarios. Los ejemplos son los siguientes:
data:0x07391dd6000000000000000000000000000000000000000000000000000000000000000a
Aquí, los primeros 8 bits 07391dd6
de los primeros 32 bytes son selectores de funciones, y después de 32 bytes son los tipos de datos correspondientes. Los lectores interesados pueden leer artículos relacionados sobre la codificación de Ethereum por sí mismos.
Bueno, para resumir: el data
atributo son los datos enviados con la llamada. Si el objeto llamado es un contrato, generalmente es el código del método de llamada del contrato; si es para crear un contrato, es el bytecode creado; si el objeto llamado es una cuenta externa, el contenido de estos datos es arbitrario (la cuenta externa no tiene código , Y no ejecutará los datos enviados).
3.3 value
value
El atributo representa la cantidad de Ether enviada con esta transacción. Independientemente de si el tipo de transacción es transferencia directa de ETH (incluida la transferencia al contrato y transferencia a una cuenta externa), creación de un contrato (en este caso, ETH se utilizará como ETH inicial del contrato creado) o llamada de contrato (el método de contrato es payable
), se registra fielmente La cantidad de ETH que envió en la transacción (sin incluir la tarifa de manejo, la tarifa de manejo es un consumo adicional). Agreguemos una value
propiedad de objeto de transacción , su valor son las wei
unidades de atención . Y, por lo general, cuando estamos en una referencia general a las ether
unidades monetarias de Ethernet , debemos hacer uso de una conversión.
let trans = {
data:"0x496c6f7665457468657265756d",
value:ethers.utils.parseEther('0.1'),
to:"0xDD55634e1027d706a235374e01D69c2D121E1CCb"
}
value
El valor en el código es 0.1 ETH. Enviemos esta transacción:
en JS, si el número es relativamente grande, excederá el límite superior de la representación decimal js (aproximadamente 10 ** 15), por lo que BigNumber se usa generalmente para interactuar con Ethereum. Puede ver que el número de WEI enviado se convierte en un BigNumber. Veamos nuevamente el resultado de etherscan:
no se muestra aquí data
porque no hice clic en Haga clic para ver Más para expandir. Como puede ver, enviamos 0.1 ETH con la transacción.
3.4 gasLimit
ygasPrice
A continuación, presentaremos dos atributos relacionados con el gas: gasLimit
y gasPrice
. Se gasLimit
refiere al consumo máximo de gas de esta transacción y gasPrice
al precio que está dispuesto a pagar por el gas consumido real. La cantidad específica de gas consumida se multiplica por gasPrice
la tarifa que está dispuesto a pagar al minero. Una vez ejecutada la transacción, se le devolverá el gas no consumido (la situación de error de transacción no se discutirá aquí, en este caso, a veces no se reembolsará el gas no consumido).
gasLimit
Suele utilizarse para limitar una determinada transacción que no puede consumir demasiados recursos. Aquí hay un escenario de uso: A menudo usamos MetaMask para transferir fondos directamente a cuentas externas. El gasLimit
valor predeterminado en MetaMask es 23000 y el mínimo no puede ser inferior a 21000.
Veamos una transacción de transferencia específica en etherscan:
como puede ver en la figura anterior, cuando no enviamos ningún dato con la transacción (el data
atributo está vacío, si no está vacío, se consumirá gas adicional), a un externo La transferencia de cuenta consumirá 21.000 gas, que es básicamente fijo. Por lo tanto, gasLimit
también se establece 21000
el límite superior de esta transacción , la tasa de utilización 100%
.
El nuestro en la imagen de arriba gasPrice
es 5 Gwei
. Cuanto mayor sea el precio que ofrezca, más rápida será la transacción y, por supuesto, mayor será su tarifa de gestión. Por lo general gasPrice
, cuando hablamos de él, todos lo usamos Gwei
como una unidad, pero aún tenemos que convertirlo cuando lo usamos wei
. Esto 5 Gwei
multiplicado por el gas consumido 21000
es exactamente lo que se muestra en la figura anterior Transaction Fee
: 0.000105
ETH. Cuando el autor escribe aquí, el precio de ETH es de alrededor de $ 205, por lo que la tarifa de manejo para enviarlo es aproximadamente 0.15RMB
.
Agreguemos estos dos atributos al objeto de transacción para ver si se consume el exceso de gas. Nuestro gasLimit está configurado en 100000
, gasPrice
configurado en 3 Gwei
, reenvíemos la transacción:
let trans = {
data:"0x496c6f7665457468657265756d",
value:ethers.utils.parseEther('0.1'),
gasLimit:100000,
gasPrice:ethers.utils.parseUnits("3",'gwei'),
to:"0xDD55634e1027d706a235374e01D69c2D121E1CCb"
}
Aquí, debido a que el nuestro gasLimit
no puede exceder el límite decimal de JS, el sistema decimal se usa directamente 100000
. La respuesta de la transacción es:
miramos directamente el resultado de la transacción en etherscan:
debido a que enviamos I love Ethereum
esta cadena con la transacción , consumimos 208 gas más. Según la tarifa que deducimos, el gas no utilizado no está incluido en el costo.
Para gasLimit
hablar en general en uso ethers
, no es necesario configurar la biblioteca, déjela por defecto en la línea. Si desea configurarlo manualmente, puede usarlo para estimar primero y luego expandirlo hacia arriba de manera apropiada, como el siguiente fragmento de código:
let args = [_address,amount]
let gasLimit = await contract.estimate.transfer(...args)
let step = ethers.utils.bigNumberify(1000)
gasLimit = gasLimit.add(step)
Para gasPrice
hablar, generalmente establecido en 5 Gwei
o en circunstancias normales 6 Gwei
. Se puede configurar en 1.5 Gwei
o cuando el gas consume mucho o la red está inactiva 2 Gwei
. Sin embargo, el tiempo de la transacción se extenderá de esta manera e incluso puede fallar. Si desea operar rápidamente, configúrelo en 10 Gwei
o 20 Gwei
incluso más alto, pero esto costará más. Si tiene más dinero, será rápido, y si no tiene suficiente dinero, será lento. Y tenga cuidado: una tarifa demasiado baja puede hacer que ningún minero empaquete la transacción y la transacción fallará. Por supuesto, si está en la red de prueba, puede configurarlo más alto, porque realmente no tiene que gastar dinero.
3,5 nonce
En el objeto de transacción, nonce
representa el número de transacciones completadas por la dirección. Comienza desde 0 y es un número entero que aumenta automáticamente. Por lo general, no es necesario configurarlo. Pero en algunos casos especiales, se puede configurar manualmente. Un escenario es cuando se cubren las transacciones. Puede establecer manualmente el valor nonce en el valor nonce de una transacción que se ha enviado pero aún no se ha completado para sobrescribir la transacción. Por lo general, el propósito de esto es acelerar la transacción (aumentar gasPrice
) o utilizar una nueva transacción por completo. Esto también es fácil de entender. Por ejemplo, mi transacción No. 122 es para enviar un ETH a A, pero antes de que esta transacción no se envíe o no se complete, hice una modificación urgente para cambiar esta transacción No. 122 para enviar un ETH a B. En este punto, solo necesito establecer el nonce de la nueva transacción en 122.
Si desea establecer en el objeto de transacción de uso común, debe consultar el número de transacciones que ha completado. Este número es el valor nonce que debe usar. Utilice el siguiente código:
let address = "0x02F024e0882B310c6734703AB9066EdD3a10C6e0";
provider.getTransactionCount(address).then((transactionCount) => {
console.log("Total Transactions Ever Sent: " + transactionCount);
});
Vale la pena señalar que: nonce tiene un uso especial, puede especificar un valor futuro. Por ejemplo, si el número de transacciones que ha completado actualmente es 2096, entonces el valor nonce para la próxima transacción debe ser 2097. En este punto, también puede omitir 2097 y configurarlo en 2098, entonces, ¿qué sucederá? En este momento, la transacción con el número 2098 equivale a una transacción retrasada, que se enviará, pero no se ejecutará, y no podrá consultarla en etherscan. Luego, realizamos una transacción normal con el valor nonce establecido en 2097, y la transacción se enviará y ejecutará. Aquí viene el punto: en el siguiente bloque también se ejecutará la transacción con un nonce de 2098 (porque ya se ha ejecutado 2097, es su turno).
3.6 chainId
chainId
Representa el ID de red que desea iniciar una transacción. Con tres redes principales y una red de prueba, por ejemplo, la red principal (mainnet, pero en ethers
la todavía llamada a homestead
casa) es 1, Ropsten
la red de prueba 3, la Rinkeby
red de prueba 4, Kovan
la red de prueba a 42. La red personalizada la puede configurar usted mismo, etc.
En términos generales, no es necesario configurar esto al usar la billetera chainId
. Debido a que el inicio de sesión de la billetera vinculará una red, es la red de su objeto de transacción. Pero también puede establecer manualmente un valor específico para evitar transacciones en la red incorrecta. Puede usar directamente el valor numérico decimal anterior, o puede usar ethers
el código de muestra en:
chainId: ethers.utils.getNetwork('homestead').chainId
Si operamos en la red de prueba de Kovan, los parámetros del método deben cambiarse kovan
. Agreguemos chainId
y nonce
a la transacción. Y cambie el valor a 0.01ETH para distinguir.
let count = await provider.getTransactionCount(wallet_new.address)
let trans = {
data:"0x496c6f7665457468657265756d",
value:ethers.utils.parseEther('0.01'),
gasLimit:100000,
nonce: count,
chainId:ethers.utils.getNetwork('kovan').chainId,
gasPrice:ethers.utils.parseUnits("3",'gwei'),
to:"0xDD55634e1027d706a235374e01D69c2D121E1CCb"
}
Aquí está la respuesta de la transacción:
debido a que los números 2097 y 2098 nonce
se consumen cuando se usan valores futuros , el número ahora es 2099. Echemos un vistazo a los resultados en etherscan:
puede ver que la cantidad de ETH enviada es 0.01 ETH, y el nonce es 2099. Alguien puede preguntar por qué no se muestra en etherscan chainId
, porque etherscan está dividido en varios sitios basados en la red principal y la red de prueba, y cada sitio solo muestra transacciones en su propia red. Por ejemplo, la URL real de etherscan que visité es:
https://kovan.etherscan.io/tx/0x4db8e6b4096d6c27be341b73af99a8d0477e19ba483248c1fdb6fb431fbb3646
Todas las transacciones que se muestran en este sitio chainId
son 42.
Cuatro, resumen
En este artículo, damos una introducción detallada a las propiedades específicas del objeto de transacción Ethereum creado manualmente. Estos atributos se pueden omitir, pero no se pueden omitir (porque no tiene sentido omitirlos todos). Los más utilizados son to
, value
y data
atributos. Nota: Este es solo un atributo que debe configurarse al crear manualmente un objeto de transacción en el código; si usa directamente una billetera común (como MetaMask o Trust billetera), la billetera tendrá una interfaz de usuario para ayudarlo a configurar todo. Sin embargo, es necesario aclarar la base de la implementación. Espero que este artículo pueda proporcionar un poco de ayuda a los desarrolladores de Ethereum.
Invitamos a todos a dejar un mensaje para señalar errores o sugerir mejoras.