Search code examples
goethereumsoliditygo-ethereum

How to get the result and the status of a transaction


I am trying to work with the Ethereum blockchain, with the Solidity contracts. I am currently deploying a contract and performing some actions on it, but I would like to know how to get some 'feedback/callback/returns' of a specific transaction.

Is there a way to set the status of a transaction to 0 (error) and still get events, for example ?

if (id.length <= 0) {
    emit Result("KO", "1");
    revert();
} 

This will not work (no event) because I revert everything, but the status will be set to 0

if (id.length <= 0) {
    emit Result("KO", "1");
    return;
} 

I will get some event, but the status will stay 1

if (id.length <= 0) {
    revert("KO_1");
} 

The status will be 0, but I will not have any event

Here is my go code to perform the action :

func    testFunction(id []byte) {
    //...
    //...
    tx, err := instance.Action(opt, id)
    if (errors.HasError(err)) {
        return
    }
    callbackValue := subscribeToContract(tx.Hash().Hex())
    logs.Pretty(tx, callbackValue)
    //...
    //...
}

func    subscribeToContract(hashToRead string) myStruct {
    query := ethereum.FilterQuery{
        Addresses: []common.Address{address},
    }
    soc := make(chan types.Log)

    sub, err := WssClient.SubscribeFilterLogs(context.Background(), query, soc)
    if err != nil {
        logs.Error(err)
    }

    for {
        select {
        case err := <-sub.Err():
            logs.Info(`SOMETHING ERROR`)
            logs.Error(err)
        case vLog := <-soc:
        logs.Info(`SOMETHING`)
        contractAbi, _ := abi.JSON(strings.NewReader(string(SignABI)))  
        event := myStruct{}
    contractAbi.Unpack(&event, "Result", vLog.Data)
    logs.Info(`New Event from [` + vLog.TxHash.Hex() + `] : ` + event.Message)
        }
    }
}

If id.length > 0, all good. But if id.length <= 0, I have no callback from the subscribeToContract function.

Is there a way to directly have the result status, or should loop with tx, err := client.TransactionReceipt(context.Background(), txHash) until I get a Status?


Solution

  • I didn't found any way to subscribe to a specific status change for a transaction, but a workaround :
    The go-ethereum package provides 2 functions SubscribeFilterLogs and SubscribeNewHead. We can use the first one to get the logs (if relevant) and the second one to get a block information :

    SubscribeNewHead subscribes to notifications about the current blockchain head on the given channel.

    A transaction can be validated or rejected/reverted when a block is mined, so we can use this 'trick'

    func checkTransactionReceipt(_txHash string) int {
        client, _ := getClient("https://ropsten.infura.io/v3/XXXXXX")
        txHash := common.HexToHash(_txHash)
        tx, err := client.TransactionReceipt(context.Background(), txHash)
        if (Error.HasError(err)) {
            return (-1)
        }
        return (int(tx.Status))
    }
    
    func    WaitForBlockCompletation(data EthData, hashToRead string) int {
        soc := make(chan *types.Header)
        sub, err := data.WssClient.SubscribeNewHead(context.Background(), soc)
        if (err != nil) {
            return -1
        }
    
        for {
            select {
                case err := <-sub.Err():
                    _ = err
                    return -1
                case header := <-soc:
                    logs.Info(header.TxHash.Hex())
                    transactionStatus := checkTransactionReceipt(hashToRead)
                    if (transactionStatus == 0) {
                        //FAILURE
                        sub.Unsubscribe()
                        return 0
                    } else if (transactionStatus == 1) {
                        //SUCCESS
                        sub.Unsubscribe()
                        return 1
                    }
            }
        }
    }
    

    Basically we are waiting for the block to be mined, then we check the TransactionReceipt which fail with an error (not found) if the transaction is not yet validated/rejected. Then, if the transaction is, we can unsubscribe the subscription and return the transaction status (0 fail, 1 success).

    Not sure if it's the worst, best, only method, but it's working ! Be free to improve this solution !