Tengo este dapp en la red principal aquí .
El siguiente contrato se ha desplegado en esta dirección :
pragma solidity ^0.4.19;
import 'zeppelin-solidity/contracts/ownership/Ownable.sol';
import 'zeppelin-solidity/contracts/lifecycle/Destructible.sol';
import 'zeppelin-solidity/contracts/lifecycle/Pausable.sol';
import 'zeppelin-solidity/contracts/math/SafeMath.sol';
contract CryptoTwittos is Ownable, Pausable, Destructible {
using SafeMath for uint;
// A Twitto is owned by a stealer and has a price
struct Twitto {
address stealer;
uint price;
}
// Look up Twitto by ids
mapping(uint => Twitto) public twittos;
// All Twitto ids and counter
uint[] public twittoIds;
uint public twittosCounter;
// Fire event when steal happens
event stealEvent(
uint indexed id,
address indexed owner,
uint price,
address indexed stealer,
uint newPrice
);
// Get twittoIds
function getTwittoIds(bool all) public view returns (uint[]) {
// Return empty array if counter is zero
if (twittosCounter == 0) return new uint[](0);
if (all) {
// Return all of them
return twittoIds;
} else {
// Create memory array to store filtered ids
uint[] memory filteredIds = new uint[](twittosCounter);
// Store number of belongings
uint twittosCount = 0;
for (uint i = 0; i < twittosCounter; i++) {
// Check if stealer is sender
if (twittos[twittoIds[i]].stealer == msg.sender) {
filteredIds[twittosCount] = twittoIds[i];
twittosCount++;
}
}
// Copy the filteredIds array into a smaller array
uint[] memory trophies = new uint[](twittosCount);
for (uint j = 0; j < twittosCount; j++) {
trophies[j] = filteredIds[j];
}
return trophies;
}
}
// Steal a Twitto by paying its price and setting a new one
function steal(uint id, uint256 newPrice) payable whenNotPaused public {
// look up the twitto and put on storage
Twitto storage _twitto = twittos[id];
// Prevent self stealing!
require(msg.sender != _twitto.stealer);
// Make sure the sender pays the right price
require(msg.value == _twitto.price);
// Make sure that the new price is higher than the old price
require(newPrice > _twitto.price);
// Transfer value with the 1% dev fee
if (msg.value > 0) {
_twitto.stealer.transfer(msg.value.mul(99).div(100));
}
// Push new Twitto if not existing
if (_twitto.price == 0) {
twittoIds.push(id);
twittosCounter++;
}
// Trigger event
stealEvent(id, _twitto.stealer, _twitto.price, msg.sender, newPrice);
// Store new stealer
_twitto.stealer = msg.sender;
// Store new price
_twitto.price = newPrice;
}
function withdraw() public onlyOwner {
// Transfer balance to owner
msg.sender.transfer(address(this).balance);
}
}
El método constante que está causando problemas es getTwittoIds
: todo funcionaba bien, pero luego el tamaño de las matrices creció un poco (como puede ver con la cantidad de transacciones en Etherscan ) y ahora esta llamada de método constante a veces devuelve una matriz vacía y a veces no.
Al leer preguntas como esta , pensé que esto se debía a que el límite de gas era demasiado bajo (para llamadas constantes), pero el problema sigue ocurriendo.
Los pasos más fáciles para reproducir este "error" son los siguientes:
Obtener contrato con abi:
var CT = web3.eth.contract([{"constant":true,"inputs":[],"name":"twittosCounter","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"unpause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"paused","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"destroy","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"pause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"twittos","outputs":[{"name":"stealer","type":"address"},{"name":"price","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"twittoIds","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_recipient","type":"address"}],"name":"destroyAndSend","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"id","type":"uint256"},{"indexed":true,"name":"owner","type":"address"},{"indexed":false,"name":"price","type":"uint256"},{"indexed":true,"name":"stealer","type":"address"},{"indexed":false,"name":"newPrice","type":"uint256"}],"name":"stealEvent","type":"event"},{"anonymous":false,"inputs":[],"name":"Pause","type":"event"},{"anonymous":false,"inputs":[],"name":"Unpause","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"constant":true,"inputs":[{"name":"all","type":"bool"}],"name":"getTwittoIds","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"id","type":"uint256"},{"name":"newPrice","type":"uint256"}],"name":"steal","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}])
Obtener Instancia
var CTInstance = CT.at("0xa4b9054417ee4f06453152a45ebeba7786c84c66")
Método de llamada que debería devolver todos los TwittoIds
CTInstance.getTwittoIds(true, console.log)
Convénzase de que el método no debe devolver una matriz vacía
CTInstance.twittosCounter(console.log)
Después de dos días de investigación todavía no tengo ni idea de lo que está pasando , por lo que cualquier ayuda sería muy apreciada.
Intenté ejecutar esto en un geth
nodo y de hecho regresa0x
Sin embargo, ejecutar esto en un nodo de paridad devolvió un gran resultado (supongo que esto es lo que necesita)
> eth.call({to:"0xa4b9054417ee4f06453152a45ebeba7786c84c66", data:"0xd15e656c0000000000000000000000000000000000000000000000000000000000000001", gas: "440000"})
Salida: https://gist.github.com/cleanunicorn/b90fc5141d157e7dcc861522af25cb5a
El gas consumido para esta operación está cerca 440000
y aumentará.
Todavía no he comprobado el código fuente de geth, pero parece que hay un límite de cuánto puede ejecutarse antes de que se detenga.
Infura tiene geth
nodos y por eso (si los estás usando) devuelve 0x
. Posiblemente también estén experimentando con parity
compilaciones personalizadas y, a veces, obtienes el resultado correcto.
geth
se comporte como parity
? Entiendo que la matriz es grande pero no tan grande...MetaMask usa nodos Infura por defecto. Intenté llamar a su contrato con JSON RPC y obtuve 0x
el resultado.
Mi mejor suposición es que Infura impone limitaciones sobre lo que se puede ejecutar en el cliente EVM con eth_call, y al alcanzar ese límite devuelve 0x en lugar de un error más descriptivo. Para confirmar esto, necesitaría sincronizar mi nodo local con la red principal y ver si la API se comporta como se esperaba.
Problemas relacionados: https://github.com/INFURA/infura/issues/70 , https://github.com/ethereum/web3.py/issues/607
El mismo problema para mí cuando uso web3.eth.contract
con el proveedor MetaMask .
Resuelvo el problema usando
web3.eth.contract
después de redefinir web3 con el proveedor Infura .
De esta manera, todo funciona bien y las llamadas a funciones nunca regresan 0x
.
Espero que esto pueda ayudar.
Eliseo Drión
alambre de agua de té
CTInstance.getTwittoIds(true, console.log)
a veces devuelve una matriz no vacía. También intenté configurar el gas de estaCTInstance.getTwittoIds(true, {gas: 990000000}, console.log)
manera, pero sigue siendo "aleatorio" (a veces está vacío, a veces no)Eliseo Drión
alambre de agua de té