«Возвращенное значение не является преобразуемой строкой» (интерфейс web3js и React)

Редактировать: чтобы было ясно, функция getMessage() отлично работает в ремиксе. Это просто не работает с моим передним концом.

Я пытаюсь настроить внешний интерфейс для своего контракта, но при попытке вызвать определенную функцию, возвращающую строку, я получаю следующую ошибку в своей веб-консоли:

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

Ошибка возникает, когда я вызываю getMessageфункцию здесь (в 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 });
}

Я использую web3 версии 1.0.0-beta.35. Я подтвердил, что создал экземпляр контракта с правильным байт-кодом и ABI. Я не уверен, почему мне удалось вернуть строки из других функций, но не из этой. Любая помощь будет принята с благодарностью. Код ниже.

Договор:

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 (проблемы с форматированием, пришлось использовать ссылку github): https://github.com/PresidentPorpoise/chatroom-react/blob/master/src/App.js

Ответы (1)

Будет ли mustBeMemberмодификатор on getMessage()иметь какое-то отношение к этому?

Я обнаружил, что способ работы модификаторов на основе разрешений с функциями просмотра может быть немного странным. Как и в случае с броском, вы получите некоторые другие данные.

В этом случае... контракт может ожидать, что строка будет возвращена, несмотря ни на что, потому что это функция просмотра, когда на самом деле она выбрасывает из-за несоответствия требованиям разрешения модификатора... это означает, что он будет возвращать бросок вместо нить.

небольшое дополнение к этому: 1. Чтобы быть более конкретным, я говорю, что, возможно, вы не вызываете этот метод контракта как член. 2. Действительно ли нужен этот модификатор? Вы знаете, что любой желающий может увидеть любое хранилище по контракту (включая ваши пароли): github.com/ethereum/wiki/wiki/…
Это на самом деле не нужно, но я добавил его для сложности, потому что я делаю это в основном для практики. Однако, если функция работает в режиме ремикса, и мне удалось использовать функцию sendMessage()из внешнего интерфейса (использует тот же модификатор), я не думаю, что это проблема. Я все же попробую, чтобы убедиться.
Убрано много сложностей, в том числе onlyMembersмодификатор, и теперь он работает. Не знаю почему, но большое спасибо.