hashgraph/hedera-smart-contracts

Enhancement: Support for Console Logging in Smart Contracts

Opened this issue · 3 comments

Problem

In the current Hedera Smart Contracts environment, developers face challenges in debugging and obtaining real-time data on chain for development purposes. This issue becomes particularly pronounced when trying to understand the flow of transactions or the state of contracts without a straightforward way to log information.

Valuable community feedback underscores the difficulty in getting data on chain and, although Solidity 0.8 has introduced custom error types for better visibility, the process remains cumbersome. A direct method to log outputs during contract execution, similar to development experiences on other platforms, is lacking, hindering efficient development and debugging. This enhancement is inspired by hardhat's console log library

Solution

To address this gap, integrating a console logging feature for smart contracts within the Hedera ecosystem, would be most helpful. This enhancement would involve:

  • Introducing a console logging library or utility that can be imported into Hedera smart contracts.
  • Allowing developers to insert console.log calls within their contract code to print messages or variable values to the console during execution.
  • Ensuring compatibility with existing Hedera tools and nodes to display these logs in a developer-friendly manner, facilitating easier debugging and development workflows.

This feature would significantly improve the developer experience by providing real-time insights into contract behavior and aiding in quick identification and resolution of issues.

Alternatives

The current alternatives involve using custom error types in Solidity 0.8 and above, which requires reverting transactions to output data, or utilizing off-chain logging mechanisms that complicate development workflows. Another alternative is relying on transaction receipts or events for indirect insights, which lacks immediacy and granularity in debugging contexts.

This would really help with debugging smart contracts. I have been using events and the error feature in Solidity 0.8 for debugging. It definitely helps in that you can see results easier, but you have to use the mirror node. It would be valuable if I could see values while debugging and not have to write any code to decode them from the mirror node.

Hello @mgarbs 👋

There are two types of networks in the hardhat ecosystem:

  • JSON-RPC networks - this type of network uses a JSON RPC provider which doesn't support console logging
  • hardhat network - it uses custom hardhat network helpers as console logging

To enable the console logging without any custom logic, we can fork a network (current forking doesn't support precompile functionalities as far as I know).

My hardhat.config.js is like that

  ...
  defaultNetwork: 'hardhat',
  networks: {
    hardhat: {
      forking: {
        url: 'https://testnet.hashio.io/api',
        blockNumber: 6906770,
        accounts: [
          '0x8d19000000aeb6079c000a690000008e438f0001000030006763b0001ad52000'
        ],
      },
    },
  },
  ...

The test contract I'm using

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.24;

import "hardhat/console.sol";

contract ConsoleLogExample {
    address lastCaller;

    function testConsoleLog() external {
        lastCaller = msg.sender;
        console.log(5644);
    }
}

And the js code snippet

  const factory = await ethers.getContractFactory('ConsoleLogExample');
  const contract = await (await factory.deploy()).waitForDeployment();
  await contract.testConsoleLog();

Another non-user-friendly approach (using the same contract as above) when we want to use a JSON-RPC network is manually handling of console logs that were executed within the call.
hardhat.config.js

  defaultNetwork: 'testnet',
  networks: {
    testnet: {
      url: 'https://testnet.hashio.io/api',
      accounts: [
          '0x8d19000000aeb6079c000a690000008e438f0001000030006763b0001ad52000'
      ],
      chainId: 296,
    },
  },
  ...

and the js code snippet

    const factory = await ethers.getContractFactory('ConsoleLogExample');
    const contract = await (await factory.deploy()).waitForDeployment();

    const tx = await contract.testConsoleLog();

    // Below are all of the extra steps we should do to get the console logs
    
    await tx.wait(); // we need to wait for the transaction to be "mined" and to be visible in the mirror node as well

    // fetch the executed internal calls within the top-level call
    // PS: observe what the console.sol is doing (just a staticcall) https://github.com/NomicFoundation/hardhat/blob/main/packages/hardhat-core/console.sol#L8
    const calls = await signers[0].provider.send(
        'debug_traceTransaction',
        [
          tx.hash, {
          "tracer": "callTracer"
        }]
    );

    // format the calls returned from debug_traceTransaction
    const callsCalldata = calls['calls'].map(e => Buffer.from(e.input.replace('0x', ''), 'hex'));

    // the hardhat logger we could use https://github.com/NomicFoundation/hardhat/blob/main/packages/hardhat-core/src/internal/hardhat-network/stack-traces/consoleLogger.ts#L57
    const logger = new ConsoleLogger();

    // decode the console logs
    console.log(logger.getDecodedLogs(callsCalldata));