1

I am getting ALERT: Transaction Error. Exception thrown in contract code in metamask when calling addBet function from truffle. The function works without any errors when called from remix. The function also works from truffle if I change the code and pass individual values instead of arrays. Is it really possible to pass arrays to a solidity function from truffle?

In browser console I see this

MetaMask - RPC Error: Error: [ethjs-rpc] rpc error with payload {"id":779020742217,"jsonrpc":"2.0","params":["0xf901ad0c8504a817c800836170d4949d70edd4e3647f2ec9a3b5abce637854b650fe1b80b901440c7bf290000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9cffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffce000000000000000000000000000000000000000000000000000000000000001047656f72676961207673204d69616d6900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000747656f726769610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000054d69616d69000000000000000000000000000000000000000000000000000000822d46a0c2291f4adb451dae0cdfc07076a0af766c061bf9c7ffa56750bb36c7057d10f5a03920df81d72cd6a838664ff8d01eaebb6e51ae07a83b477d74ed03e51905d5cb"],"method":"eth_sendRawTransaction"} [object Object] 
uncaught exception: Object

Betting.sol

pragma solidity ^0.5.0;
pragma experimental ABIEncoderV2;

contract Betting {

    struct Bet{
        uint betId;
        string question;
        BetStatus betStatus;
        mapping(uint => MoneyLineDatum) moneyLineData;
        uint lengthMoneyLineData;
    }

    struct MoneyLineDatum{
        string option;
        int value; 
    }

    address public owner = msg.sender;

    mapping(uint => Bet) public bets;
    uint public lengthBets;
    enum BetStatus {Open, Closed}

    constructor() public{
        string[2] memory options = ["Gator", "Miami"];
        int[2] memory values = [int(200), -100];
        addBet("Gator vs Miami", options, values);

        options = ["Gator", "Georgia"];
        values = [int(200), -150];
        addBet("Gator vs Georgia", options, values);
    }

    modifier onlyBy(address account){
        require(msg.sender == account, "Unauthorised Access");
        _;
    }

    function addNewEmptyBet() public{
        Bet memory bet;
        bets[lengthBets] = bet;
    }

    function addBet(string memory question, string[2] memory options, int[2] memory values) public onlyBy(owner){
        addNewEmptyBet();
        Bet storage bet = bets[lengthBets];
        bet.betId = lengthBets;
        bet.question = question;
        bet.betStatus = BetStatus.Open;
        lengthBets+=1;
        for(uint i=0; i<options.length; i++){
            bet.moneyLineData[i] = MoneyLineDatum(options[i], values[i]);
        }
        bet.lengthMoneyLineData+=options.length;
    }

    function getLengthMoneyLineData(uint betId) public view returns(uint){
        return bets[betId].lengthMoneyLineData;
    }

    function getMoneyLineData(uint betId, uint idx) public view returns(string memory, int){
        MoneyLineDatum storage moneyLineDatum = bets[betId].moneyLineData[idx];
        return (moneyLineDatum.option, moneyLineDatum.value);
    }
}

Snippet from app.js

App.contracts.Betting.deployed().then(function(_instance){
    return _instance.addBet(question, options, values, {from: App.account});
}).then(function(){
    console.log("Bet has been added");          
});

options and values are arrays each of size 2 with datatypes string[] and int[] respectively

Any help would be greatly appreciated! Thank you!

1

Solution:

  1. Get rid of pragma experimental ABIEncoderV2;
  2. Replace every occurrence of string with bytes32
  3. Replace every occurrence of bytes32 memory with bytes32
  4. Replace every occurrence of "Gator" with bytes32("Gator")
  5. Replace every occurrence of "Miami" with bytes32("Miami")
  6. Replace every occurrence of "Georgia" with bytes32("Georgia")
  7. Replace every occurrence of "Gator vs Miami" with bytes32("Gator vs Miami")
  8. Replace every occurrence of "Gator vs Georgia" with bytes32("Gator vs Georgia")
  9. In your Truffle test, convert each string x to:
    • web3.fromAscii(x) if you're on Truffle v4.x (which relies on Web3 v0.x) or earlier
    • web3.utils.asciiToHex(x) if you're on Truffle v5.x (which relies on Web3 v1.x) or later

A few notes on your contract's code:

  • It seems that each one of your mappings is used as if it was an array; so you should probably use arrays instead (for good practice, as well as for gas-cost efficiency)
  • The onlyBy modifier doesn't need to take account as input argument; instead, it can compare the value of msg.sender directly to the value of owner
  • Thank you so much for such a detailed solution. I was stuck at this since 3 days. It works now. I am not using array because later I do want to access things by key. onlyBy is kind of a general function for restricting access so I have kept the address parameter. – Deep Bodra Oct 30 at 21:37
  • @DeepBodra: You're welcome :) – goodvibration Oct 31 at 5:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.