Error while interacting with my contract (Hackathon season 6 )

Good day all, I am learning Solidity, and I came up with this issue while deploying my contract yesterday.

So I can deploy it correctly, and the calls to the readonly function getVotes() seem to work ok.

When I deploy it on test net, and use tronBox (in a browser environment)


            const tronWeb =  window.tronWeb;

            const contractAddress = 'TFBP2xBhYTb2hNrLep5UpFW1a2DZ5AP4yY';
            let contract;
            try {
                contract = await tronWeb.contract().at(contractAddress);
            } catch(e) {
                console.error(`error loDING CONTRACT ${contractAddress}`);
                throw e
            }
            //
            //
            try {
                let votes = await contract.getVotes().call();
                console.log(`Loading All votes from TRON `, votes);
            } catch(e) {
                console.error(`error getting votes`, votes);
                throw e
            }

            try {
                const txId = await contract.voteBooks([35, 36], [3, 4]).send()
                console.log(`Check tx on the explorer: https://shasta.tronscan.org/#/transaction/${txId}`);
            } catch(e) {
                console.error(`error voting`, e);
                throw e
            }

The 3d try/catch block doesnā€™t throw any error, and the console log shows check tx on the explorer:

When I go check it out, the transaction failed, and I cannot find any debug information that could help me figure out what is going on, and why it failed. So I am turning to the TRON experts out there that eat solidity at breakfast, and can spot bugs effortlessly while doing cardio.

Here is the contract on Shasta: TRONSCAN | TRON BlockChain Explorer | ę³¢åœŗåŒŗå—é“¾ęµč§ˆå™Ø

I cannot seem to explore why the transaction failed, and I am quite stuck.

Here is the contract source code:


pragma solidity >=0.7.0 <0.9.0;

uint8 constant ILLEGAL = 1;
uint8 constant POOR = 2;
uint8 constant OK = 3;
uint8 constant GREAT = 4;

struct BookVote {
    bool initialized;
    uint8 classification;
}

struct UserVotes {
    mapping(uint256 => BookVote) data;
    bool initialized;
}


contract BookVotes {

    // user Data
    address[] all_addresses;
    mapping(address => UserVotes) user_votes;

    // all Data
    uint256[] public ids;
    // map[id] index
    mapping(uint256 => uint256) public idsIndex;
    uint256[] public illegal;
    uint256[] public ok;
    uint256[] public poor;
    uint256[] public great;


    constructor() {
        ids.push(0);
    }

    function addGlobalVote(uint256 bookId, uint8 cl) public {
        if (idsIndex[bookId] == 0) {
            ids.push(bookId);
            illegal.push(0);
            ok.push(0);
            poor.push(0);
            great.push(0);
            idsIndex[bookId] = ids.length - 1;
        }

        uint index = idsIndex[bookId];

        if (cl == GREAT) {
            great[index]++;
        } else if (cl == OK) {
            ok[index]++;
        } else if (cl == POOR) {
            poor[index]++;
        } else if (cl == ILLEGAL) {
            illegal[index]++;
        }
    }

    function removeGlobalVote(uint256 bookId, uint8 cl) private {
        require(idsIndex[bookId] > 0, "cannot remove vote for book");

        uint index = idsIndex[bookId];

        if (cl == GREAT) {
            great[index]--;
        } else if (cl == OK) {
            ok[index]--;
        } else if (cl == POOR) {
            poor[index]--;
        } else if (cl == ILLEGAL) {
            illegal[index]--;
        }
    }

    function voteBooks(uint256[] memory bookIds, uint8[] memory classifications) public {
        if (user_votes[msg.sender].initialized == false) {
            all_addresses.push(msg.sender);
            user_votes[msg.sender].initialized = true;
            for (uint i = 0; i < bookIds.length; i++) {
                user_votes[msg.sender].data[bookIds[i]].initialized = true;
                user_votes[msg.sender].data[bookIds[i]].classification = classifications[i];
            }
        } else {
            // user is already in the mapping
            for (uint i = 0; i < bookIds.length; i++) {
                uint8 cl = classifications[i];
                uint256 book = bookIds[i];

                if (user_votes[msg.sender].data[book].initialized) {
                    // book has already been voted upon in the past
                    if (cl != user_votes[msg.sender].data[book].classification) {
                        // the book classification has been updated
                        // TODO: Runs allVotes
                        removeGlobalVote(book, cl);
                    }
                    user_votes[msg.sender].data[book].classification = cl;
                } else {
                    user_votes[msg.sender].data[book].classification = cl;
                    user_votes[msg.sender].data[book].initialized = true;
                }
                addGlobalVote(book, cl);
            }
        }
    }

    function getVotes() public view returns (uint256[] memory, uint256[] memory, uint256[] memory, uint256[] memory, uint256[] memory) {
        return (ids, illegal, poor, ok, great);

    }
}


1 Like

Hello @lawpond , let me go trough it tomorrow morning and will let you know my findings :slight_smile:

3 Likes

@EMerchant @OnChainVision @WindsOfChange92 :pray:

2 Likes

You can ask your developer questions in TRON developers telegram chat link Telegram: Contact @TronOfficialDevelopersGroupEn You may find your answers there faster.

2 Likes

The Boss at the top Can be of good help @Prince-Onscolo .

1 Like

Good day ! I hope you had a great weekend ! Any luck? I am going to look into it tomorrow.

1 Like

Thank you, just did it few hours ago :wink:

1 Like

looking at one of the failed transaction when you click on the RED ā€œFAILED-TRANSACTION-REVERTEDā€ a pop up will appear and wil show you the reaseon

check the contract logic for that particular failed

thanks,
onchaindev :white_heart:

2 Likes

Hello @lawpond , Iā€™ve been testing your smart contract in my bench and have some clues already:

  1. First, I created a sandbox project using tronweb/tronbox: You can find it here in case you need to follow the same steps as I did :slight_smile:
  2. I have succesfully used the voteBooks function (please check in your contract for details and trasanctions), however I could only add one data point, as soon as I add 2 books IDs the execution is reverted. This might be due to a low MaxLimitFee or wrong logic implementation in your code. What I did is to only send one ID + 1 classification as follows(check line 21):

If you vote for books one by one it works, however the energy used per transaction is too big, thus I assume when you are trying to add more books the TRX needed for energy exceeds your contract maxLimitFee and thus it reverts. Please check into optimizing your logic and also increasing your maxFeeLimit. Let me know if this info works for you :slight_smile:

3 Likes

Legend !

I will study maxFeeLimit and also do some cost optimization !

1 Like

Anytime! Yes check for any loops and other time / data consuming operations in your logic to save some fees

1 Like

Wao this is amazing, keep on with the good work buddy

1 Like

OK, so I have made some progress, thanks to @SimbadMarino :wink:

I went and checked his code here and start experimenting with having one smart contract per Book.

I also didnā€™t see there was a docker image tronbox/tre that is cool for quick iterations.

So not a significant progress, beacause now, the new strategy sounds like it would cost me around 35.000$ to deploy 5000 smart contract (one per book)ā€¦

I donā€™t think I can optimize the code more than that, I simplified it to its maximum:

An example here (Git coming soon)

pragma solidity >=0.7.0 <0.9.0;

/**
Illegal = 1
Poor = 2
Good = 3
Great = 4

*/

contract BookVote {

    uint16 public nbGood = 0;
    uint16 public nbGreat = 0;
    uint16 public nbPoor = 0;
    uint16 public nbIllegal = 0;

    mapping(address =>uint8) public userVotes;

    uint16 public bookId;

    constructor(uint16 id ) {
        bookId = id;
        // register itself to Main contract
    }

    function voteGreat() public {
        uint8 current = userVotes[msg.sender];
        removeVote(current);
        userVotes[msg.sender] = 4;
        nbGreat++;
    }

    function removeVote(uint8   current) internal {
        if (current != 0) {
            // The user has already voted for this book, and changed its mind
            if (current==1) {
                nbIllegal--;
            } else if (current==2) {
                nbPoor--;
            } else if (current==3) {
                nbGood--;
            } else if (current==4) {
                nbGreat--;
            } else {

            }
        }
    }

    function voteGood() public {
        uint8 current = userVotes[msg.sender];
        removeVote(current);
        userVotes[msg.sender] = 3;
        nbGood++;
    }

    function votePoor() public {
        uint8 current = userVotes[msg.sender];
        removeVote(current);
        userVotes[msg.sender] = 2;
        nbPoor++;
    }
    function voteIllegal() public {
        uint8 current = userVotes[msg.sender];
        removeVote(current);
        userVotes[msg.sender] = 1;
        nbIllegal++;
    }
}


This contract costs about 7$ to deploy.

Then, I have this one:


pragma solidity >=0.7.0 <0.9.0;

import './BookVote.sol';

contract BookVoteFactory {

    BookVote[] public listOfBooks;

    function createBookVoteContract(uint index) public {
        listOfBooks.push(new BookVote(uint16(index)));
    }

    constructor() {
        for (uint i=0; i<5000; i++) {
            createBookVoteContract(i);
        }
    }
}


Money wise, anyone see a better approach? 5000 is the number of books I have in the library currently.

ok, so I went the really simplistic way.

I will however need to find a way to stop user for voting more than once for the same bookā€¦
But now, fees looks alright.

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract BookVotes {

    // 5000 books, 4 classification per books => 4*5000 = 20.000
    uint16[20000] public votes ;
    
    constructor() {
    }

    function getVotes(uint16 bookId) public view returns (uint16, uint16, uint16, uint16) {
        return (votes[bookId*4], votes[bookId*4+1], votes[bookId*4+2], votes[bookId*4+3]);
    }

    function voteBook(uint16 bookId, uint8 cl) public {
        votes[bookId*4+cl]++;
    }

    function voteBooks(uint16[] memory bookIds, uint8[] memory classifications) public {
        for (uint i = 0; i < bookIds.length; i++) {
            voteBook(bookIds[i], classifications[i]);
        }
    }
}
1 Like

Hello again @lawpond ! glad you find out a more efficient way to deal with your contract :slight_smile:

What I would do is to save both the ID and voter address in a mapping, later on you can look for that address by simply getting getVoterAddress writing dow the bookID


    mapping (uint16 bookID => address voter  ) public getVoterAddress;

     //You will have to modify your functions to something like this:


 function voteBook(uint16 bookId, uint8 cl) public {
            require(msg.sender != getVoterAddress(bookId), "voter already voted for this book, try with another book");
            _; //Underscore and ; after the require means require will be executed before the called fuction is called.
            votes[bookId*4+cl]++;
            getVoterAddress[bookId] += msg.sender;     //This should link both the voter address with bookID
    }

Please consider I did not compile the provided code, so it might not work out of the box, this is just to give you an idea on how to achieve what you want to do, the require keyword is used to check wether certain conditions are met in order to execute the function or not to avoid users on spending fees if conditions are not going to be met anyways.

keywords to look for additional docs, info: require, mapping, msg.sender,

Wish you luck!
Feel free to ask if additional support is needed.

@SimbadMarino - thank you ! I will give that pattern a try later in the week. Right now, I am focused on redesigning the leveling system from the ground up.

I have released the MVP v1 yesterday, if youā€™d like to have a peek @ it :
Enter the Library - Start New Game and let me know your first impressions.

Good topic!! I follow it

1 Like

Hello @Gustavo @SimbadMarino @Okorie @OnChainVision @HODL @Prince-Onscolo

I am pinging you here because I need your helpā€¦

I have published this new repo for the operational smart contract that I have deployed on Shasta, and I am now trying to deploy it to mainnet, and I have just lost quite a lot of money on Mainnet trying to deploy it with consistent errors about not available energy to deploy the contract.

The problem, is that I am not financially stable, almost homeless right now, and I donā€™t have much left on my bank account to spend trying againā€¦

So far the error I got is https://api.trongrid.io/wallet/gettransactionbyid?value=5121f98398e4348c7e7ad154f04f5300cf1ef9e67d4729944af3a8c8ae5a80b1

And the amount of money I assumed I had to pay (and that disappeared without any warningā€¦) is around 35$.

How did I do it?

1 - I simulated it with Shasta, and made a tronbox migrate --reset --network shasta, and on Shasta, the deployment cost was about 30$

2 - I bought 40$ of TRON, lost about 5$ in fees

3 - I ran tronbox migrate --network mainnet, and I lost it all.

No jokes, I really am struggling financially right nowā€¦ And my integrity is on the line - I keep cool with faith and grit, but it is not an easy place to be in.

I got some food to last a week, and get a small payment coming tomorrow that will just cover rent and utilitiesā€¦ I could try to skip a week rent to find 100US$ to deploy the contract to meet that 29th June deadlineā€¦

How do I know if this is going to be enough?

I really cannot afford that expense right now, but I am still willing to risk itā€¦ But Iā€™d be really angry at that ecosystem if I put 100$, and I get that out of energy error again, losing it all.

letā€™s put it this wayā€¦ 140US$ is a lot of money to me. And is what separates me from having a bed next week, from the streetā€¦

How can I estimate the cost of deployig my smart contract

.

2 Likes

Can you post the account that deploy the contract on Shasta, I usually look at the energy consume on Testnet and then go on and rent the Energy using different Rent Platforms. Donā€™t forget to keep some TRX for bandwidth also!

If you join our Telegram Telegram: Join Group Chat ping the admin and now we have an ongoing campaign(basically to check our mobile dApp) will pay you all the necessary fess so that you can deply the contracts and transfer the amount and let you know how to deploy step by step!

4 Likes

Best is to stake trx for the energy you need. Still you do not have sufficient fees you can rent energy from platforms like NRG. @OnChainVision has also provided a solution to you @lawpond you should contact him. Wish you all the best !

1 Like