Estoy desarrollando una aplicación React muy simple en la que un usuario hace clic en un botón y se envía algo de éter de una cuenta a otra. Actualmente, la función onClick sendEther crea una transacción serializada firmada adecuada y (a menudo) devuelve un hash de transacción. Sin embargo, la transacción en realidad nunca pasa por la red de prueba de Rinkeby (a la que se accede a través de Etherscan).
Mi corazonada es que estoy haciendo un mal uso de infura (que configuré en mi evento DOMContentLoaded, ver más abajo).
//1. Configurar
document.addEventListener('DOMContentLoaded', () => {
const endpoint = 'https://rinkeby.infura.io/APIKEY';
window.web3 = new Web3(new Web3.providers.HttpProvider(endpoint));
});
//2. Reaccionar componente front-end.
sendEther() {
const fromAccount = **acct1**;
const toAccount = **acct2**;
const rawTransaction = this.makeRawTransaction(fromAccount, toAccount);
const signedTransaction = this.makeSignedTransaction(rawTransaction);
const serializedTransaction = `0x${signedTransaction.serialize().toString('hex')}`;
window.web3.eth.sendSignedTransaction(serializedTransaction, (error, result) => {
if(!error) {
console.log(`Transaction hash is: ${result}`);
this.setState({
etherscanUrl: `https://rinkeby.etherscan.io/tx/${result}`,
error: null
});
} else {
this.setState({ error: error.message })
console.error(error);
}
});
}
makeSignedTransaction(rawTransaction) {
const privateKey = '**************';
const privateKeyX = new Buffer(privateKey, 'hex');
const transaction = new EthTx(rawTransaction);
transaction.sign(privateKeyX);
return transaction;
}
makeRawTransaction(fromAccount, toAccount) {
const { exchangeRate } = this.props;
const amount = (1 / exchangeRate) * 5;
return ({
nonce: window.web3.utils.toHex(window.web3.eth.getTransactionCount(fromAccount)),
to: toAccount,
gasPrice: window.web3.utils.toHex(100000000000),
gasLimit: window.web3.utils.toHex(100000),
value: window.web3.utils.toHex(window.web3.utils.toWei(`${amount}`, 'ether')),
data: ''
});
}
Esta línea ...
nonce: window.web3.utils.toHex(window.web3.eth.getTransactionCount(fromAccount)),
... se basa en un recuento de transacciones fiable de Infura. El problema es que no es tan confiable (ni tan siquiera cerca) como usted necesita que sea. Este método producirá resultados poco fiables e inconsistentes como ha observado.
Algunos antecedentes ayudarán a explicar lo que está sucediendo y cómo puede abordarlo.
Ethereum usa una nonce
para cada cuenta para protegerse contra ataques de repetición. Hay algunos detalles sutiles de implementación.
nonce
orden.nonce
se detendrán. Por lo general, una transacción estancada se puede cancelar (ver #4).nonce
y superior gas
si el reemplazo se extrae primero. Un método común para cancelar una transacción es repetir nonce
y enviar 0
ETH desde la cuenta a sí misma.getTransactionCount()
no tiene en cuenta las transacciones que puedan existir en la cola pendiente.Todo lo cual para decir que hay razones conocidas por las que su proceso no es confiable.
El enfoque habitual es hacer que el cliente de software sea el administrador del nonce
. El escenario más fácil de describir es el caso en el que no hay transacciones pendientes porque la cuenta ha estado inactiva o es una cuenta nueva.
A partir de 0
, o el recuento de transacciones, incremente el nonce a medida que avanza sin depender de los nodos. Tienes que confiar en tu propio conteo más de lo que confías en cualquier señal de la red.
Puede encontrar más información aquí: Patrones de concurrencia para cuenta nonce
Espero eso ayude.
Como explicó Rob Hitchens, Infura está diseñado a través de un balanceador de carga con múltiples nodos detrás. Esto significa que dos de las mismas solicitudes a Infura pueden generar respuestas diferentes porque cada solicitud podría llegar a un nodo diferente. A veces obtendrá respuestas de bloques obsoletos, nodos con diferentes transacciones en sus mempools o nodos que no contienen el bloque más nuevo. Una alternativa a este problema es usar un servicio como Alchemy
Lauri Peltonen