"El valor devuelto no es una cadena convertible" (interfaz web3js y React)

Editar: para ser claros, la función getMessage() funciona bien en remix. Simplemente no parece funcionar con mi front-end.

Estoy tratando de configurar una interfaz para mi contrato, pero cuando intento invocar una determinada función que devuelve una cadena, aparece el siguiente error en mi consola web:

Error: ERROR: The returned value is not a convertible string: formatters.js:261

El error parece ocurrir cuando invoco la getMessagefunción aquí (en App.js):

async componentDidMount() {
const host = await chatroom.methods.host().call();
const members = await chatroom.methods.getMembers().call();
const chatLoglength = await chatroom.methods.getMessagesLength().call();
const chatLog = [];

for (let i = 0; i < chatLoglength; i++) {
  const newMessage = await chatroom.methods.getMessage(i+1).call();
  chatLog.push(newMessage);
}

this.setState({ host, members, chatLog });
}

Estoy usando web3 versión 1.0.0-beta.35. He confirmado que instalé el contrato con el código de bytes y ABI correctos. No estoy seguro de por qué he podido devolver cadenas de otras funciones pero no de esta. Cualquier ayuda sería muy apreciada. Código a continuación.

Contrato:

pragma solidity ^0.4.25;

contract Chatroom {
address public host;
string private password;

address[] public members;
Message[] public chatLog;

mapping(uint => Message) msgIDPair;
mapping(address => bool) isMember;

struct Message {
    address author;
    string content;
}

// @notice Creates the chat-room. Host role is given to the address of the sender
// @dev The password could be inferred from the constructor argument, not strong security
// @param _password The password the host wishes to set for the chat-room
constructor(string _password) public {
    host = msg.sender;
    addMember(host); // adds host address to members array

    password = _password;
}

// @notice Send a message `(_message)` to the chat-room (must be a member)
// @param _message The content of the message to be sent
function sendMessage(string _message) external mustBeMember {
    uint msgID = chatLog.length + 1;

    msgIDPair[msgID] = Message(msg.sender, _message); // pairs message ID with Message struct object
    chatLog.push(msgIDPair[msgID]); // adds Message object to chatLog array
}

// @notice Retrieve a message via ID `(_ID)`
// @dev Returns struct of Message, use front-end to get specific properties
// @param _ID The ID assigned to the desired message
// @return The target message
function getMessage(uint _ID) public view mustBeMember returns(string) {
    return(msgIDPair[_ID].content);
}

// @notice Check if an address is a member
// @param _target The address to be checked
// @return true if the target address is a member, false otherwise
function checkMember(address _target) public view returns(bool) {
    if (isMember[_target] == true) { // returns true if address has a "true" value assigned in isMember mapping table
        return(true);
    }
    else { // returns false if address does not have a "true" value assigned in isMember mapping table
        return(false);
    }
}

modifier mustBeMember() {
    require(checkMember(msg.sender) == true);
    _;
}


// @notice Become a member the chat-room (requires `(password)`)
// @param _password The password to evaluate
function joinChat(string _password) public requirePassword(_password) {
    addMember(msg.sender);
}

// @notice Leave the chat-room (must be a member)
function leaveChat() public mustBeMember {
    require(msg.sender != host); // host cannot leave, must transfer role first

    for (uint i = 0; i < members.length; i++) { // loops through entire members array, deletes matching address
        if (members[i] == msg.sender) {
            swapReduceIndex(members, i);
        }
    }

    isMember[msg.sender] = false;
}

// @notice Add a new member address that is not already a member
// @dev This is a helper function
// @param _newMember The address to be granted membership
function addMember(address _newMember) private {
    if (isMember[_newMember] == true) { // does nothing if address is already a member
        return();
    }
    else { // adds address to isMember mapping table and pushes the address to the members array
        isMember[_newMember] = true;
        members.push(msg.sender);
    }
}

// @notice Retrieve a list of all members
// @return A list of all member addresses
function getMembers() public view returns(address[]) {
    return(members);
}

modifier requirePassword(string _password) {
    require(keccak256(password) == keccak256(_password));
    _;
}

modifier onlyHost {
    require(msg.sender == host);
    _;
}

// @notice Remove a member (requires 'host' status)
// @param _member Address of the member to be removed
function kickMember(address _member) external onlyHost {
    require(msg.sender != _member); // host cannot kick himself

    for (uint i = 0; i < members.length; i++) { // loops through entire members array, deletes matching address
        if (members[i] == _member) {
            swapReduceIndex(members, i);
        }
    }

    isMember[_member] = false;
}

// @notice Transfer 'Host' role to another member (requires 'host' status)
// @param newHost The address of the member to be granted the 'host' role.
function switchHost(address newHost) external onlyHost {
    require(checkMember(newHost));

    host = newHost;
}

// @notice Delete index of array, swap last index with deleted, remove last index
// @dev Only works with address arrays, inteded to be used as a helper function with removing member addresses
// @param array The array in which to modify
// @param _blankIndex The number of the index to be deleted
function swapReduceIndex(address[] storage array, uint _blankIndex) internal {
    delete array[_blankIndex];
    uint lastIndex = array.length-1;
    array[_blankIndex] = array[lastIndex];
    array.length--;
}

// @notice Get length of chatLog array
// @dev Useful for displaying messages on front-end
function getMessagesLength() external view returns (uint) {
  return(chatLog.length);
}

}

App.js (problemas de formato, tuve que usar el enlace github): https://github.com/PresidentPorpoise/chatroom-react/blob/master/src/App.js

Respuestas (1)

¿El mustBeMembermodificador en getMessage()tiene algo que ver con esto?

Descubrí que la forma en que los modificadores basados ​​en permisos funcionan con las funciones de vista puede ser un poco extraña. Como en usted no verá un lanzamiento, pero obtendrá algunos otros datos.

En este caso... el contrato podría estar esperando que se devuelva una cadena sin importar qué porque es una función de vista, cuando en realidad está lanzando debido a que no cumple con los requisitos de permiso del modificador... lo que significa que devolverá un lanzamiento en lugar de un cadena.

una pequeña adición a esto: 1. Para ser más específico, estoy diciendo que tal vez no estés llamando a ese método de contrato como miembro. 2. ¿Es realmente necesario este modificador? Sabes que cualquiera puede ver cualquier almacenamiento en el contrato (incluidas tus contraseñas): github.com/ethereum/wiki/wiki/…
No es realmente necesario, pero lo agregué por complejidad porque lo hago principalmente para practicar. Sin embargo, si la función funciona en remix, y he tenido éxito al usar la sendMessage()función desde el frente (usa el mismo modificador), no creo que este sea el problema. Aunque lo intentaré para asegurarme.
Se eliminó gran parte de la complejidad, incluido el onlyMembersmodificador, y ahora funciona. No estoy seguro de por qué, pero muchas gracias.