[Ethereum] getting data from events

pythonsolidity-0.6.xweb3.pyweb3js

versions:
solidity 6.0.0
python3.8
web3 5.13.1

I am trying to get return data from a smart contract function that changes a struct on the contract. I cannot do that using return because the transaction changes the blockchain, so I read that I can use a Solidity event. Now running my code I am getting the following error:

 web3.exceptions.SolidityError: execution reverted: VM Exception while processing transaction: revert Request not approved yet.

Below is some of the contract. The getKYCData is what is giving me the problem.

pragma solidity ^0.6.0;

contract ReturnData
    

    struct KYCData {
        string name;
        string home;
        string tin;
        string phone;
    }

    event WalletData(
        string name,
        string home,
        string tin,
        string phone
    );

    Request[] public requests;
    uint[] public request_ids;
    address public manager;
    address public owner;
    KYCData private data;
    uint public serviceCost;

    modifier restricted() {
        require(msg.sender == manager || msg.sender == owner);
        _;
    }

    constructor (address wallet_owner, address admin, string memory name, string memory home,
        string memory tin, string memory phone, uint serviceFee) public {
        manager = admin;
        owner = wallet_owner;
        data = KYCData({
            name: name,
            home:home,
            tin: tin,
            phone: phone
        });
        serviceCost = serviceFee;
    }

    function getData() public restricted view returns (string memory, string memory, string memory, string memory) {
        return (data.name, data.home, data.tin, data.phone);
    }

    function makeRequest(string memory name, string memory description) public payable returns (uint) {
        require(msg.value > serviceCost, "Did not meet fee requirement.");

        Request memory newRequest = Request({
            requestor: msg.sender,
            name: name,
            description: description,
            approved: false,
            active: true
        });
        requests.push(newRequest);
        uint val = request_ids.length;
        request_ids.push(val + 1);
        return val;
    }

    function approveRequest(uint index) public restricted {
        Request storage request = requests[index-1];

        request.approved = true;
    }

    function getKYCData(uint index) public returns (string memory, string memory, string memory, string memory) {
        Request storage request = requests[index-1];

        require(request.approved, "Request not approved yet.");
        require(request.active, "Request is not active");
        require(msg.sender == request.requestor, "Must be requested from request originator.");

        request.active = false;
        emit WalletData(data.name, data.home, data.tin, data.phone);
    }

Best Answer

Using events to get return values from functions is not the same as using return in view/pure functions. In fact you shouldn't use the keyword returns when declaring a function that results in a transaction. The only exception to this is if you call such a function inside of another function in the smart contract:

 uint256 public x = 0;
 
 function first() internal  pure returns(uint256){
     return 10;
 }
 
 function second() public {
     x = put();
 }

When emitting an event in solidity the parameters passed to it are stored in the Logs, a special data structure found inside each transaction receipt. Keep in mind that there is extra cost for emitting an event as it is considered a state modifying action

Anyway, in your case, changing function getKYCData(uint index) public returns (string memory, string memory, string memory, string memory)

to function getKYCData(uint index) public should do the work.

Then you can use web3 to listen for new events/Logs and retrieve the data you want when the transaction is completed and its receipt is available. Unfortunately i don't think there is a more efficient way to do it, i.e not waiting for the transaction to complete.

web3.js example for listening for a new event:

//create instance of contract
var contract = new web3.eth.Contract(ABI_of_contract, address_of_contract);

//listen for new events
contract.events.WalletData({
  fromBlock: 'latest'
  }, function(error, event){
      console.log(event); 
  });

Also, you can consult an example from web3 docs for better understanding.

Other than using events to return values from functions, you may treat them as a cheaper way of storage. See my answer here or a nice post from ConsenSys here

I hope this will be of help!

Related Topic