3

I am creating an DAPP where the functions will be disaplyed in the front end if I pass an ABI of a contract.

Is there a way to extract functions of the contract if I pass ABI?

For example,

[
  {
    inputs: [],
    name: 'count',
    outputs: [ [Object] ],
    stateMutability: 'view',
    type: 'function'
  },
  {
    inputs: [],
    name: 'increaseCount',
    outputs: [],
    stateMutability: 'nonpayable',
    type: 'function'
  }
]

if that is the ABI of a sample contract in JSON format I need to extract count and increaseCount functions and display it in the front.

Thank you!

2 Answers 2

2

I created a simple and quick implementation with plain JS and HTML so you can see how something like that can be done.

Check (replace <yourInfuraRinkebyKey> with your own infura api key):

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/web3/1.7.5/web3.min.js" integrity="sha512-/T7YwzOsNeoNkuTfYKXn3CrJCGc5cnC8T4QW46Hy+3Xjdjrxzokmbx8M8Xavjq1K7dN4958kIRGy4J03VRIlSg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
</head>
<body>

<div id="container">


</div>


<script>

const web3 = new Web3("https://rinkeby.infura.io/v3/<yourInfuraRinkebyKey>");

const abi = [
    {
        "inputs": [
            {
                "internalType": "uint256",
                "name": "_element",
                "type": "uint256"
            }
        ],
        "name": "addElement",
        "outputs": [],
        "stateMutability": "nonpayable",
        "type": "function"
    },
    {
        "inputs": [],
        "name": "count",
        "outputs": [
            {
                "internalType": "uint256",
                "name": "",
                "type": "uint256"
            }
        ],
        "stateMutability": "view",
        "type": "function"
    },
    {
        "inputs": [],
        "name": "element",
        "outputs": [
            {
                "internalType": "uint256",
                "name": "",
                "type": "uint256"
            }
        ],
        "stateMutability": "view",
        "type": "function"
    },
    {
        "inputs": [],
        "name": "increaseCount",
        "outputs": [],
        "stateMutability": "nonpayable",
        "type": "function"
    }
];

const counter = new web3.eth.Contract(
  abi,
  "0x0ea68C4FcF2B49280DC599e5F5D907844135f107",
  {
    from: "0x64eF77725E9E408FD6ea587ECAeAcA2476234Ba9",
  }
);


async function handleClick() {
    const button = event.target;

    console.log(button)

    const children = [...button.parentNode.children];

    const inputs = children.filter(element => element.nodeName === 'INPUT');

    const args = inputs.map(input => input.value);

    const functionName = button.innerText;
    console.log("functionName: ", functionName);
    // Dynamically calling the function with it's args. More logic should be added, like making sure required args are provided and the correct type.
    const result = await counter.methods[functionName](...args).call();
    // Show this result somewhere
    console.log("result: ", result)

    const displaySpan = children[children.length - 1];

    displaySpan.innerText = result;

}

const groups = abi.filter(element => element.type === 'function')
  .map(element => {
    const button = document.createElement('button');
    button.textContent = element.name;
    button.onclick = handleClick;

    const group = document.createElement('div');
    group.setAttribute("class", "group");

    group.appendChild(button);

    for(let i = 0; i < element.inputs.length; i++) {
        const input = element.inputs[i];
        const inputText = document.createElement('input');
        inputText.setAttribute("type", "text");
        inputText.setAttribute("placeholder", `${input.type} ${input.name}`);
        group.appendChild(inputText);
    }

    const display = document.createElement('span');

    group.appendChild(display);
    return group;

  });

  const container = document.getElementById("container");

  for(let i = 0; i < groups.length; i++) {
    container.appendChild(groups[i]);
  }

</script>

    
</body>
</html>

It will look something like this:

enter image description here

Ugly, but it's just an idea on how you could dynamically create buttons/fields in the frontend to interact with your smart contract.

Notice that the functions that are supposed to mute the state will not mutate the state in my example because it's not connected to a wallet like Metamask or something like that, so it's not able to sign transactions. But the view functions will work and return you the current value in the contract.

While calling the functions, check the network tab in the browser and analyze the payload being sent and the response.

Remember to replace <yourInfuraRinkebyKey> with your own infura api key so you can test this code.

The contract is deployed on Rinkeby at address: 0x0ea68C4FcF2B49280DC599e5F5D907844135f107

It's verified, so you can play around with the functions in rinkeby etherscan:

https://rinkeby.etherscan.io/address/0x0ea68C4FcF2B49280DC599e5F5D907844135f107#readContract

https://rinkeby.etherscan.io/address/0x0ea68C4FcF2B49280DC599e5F5D907844135f107#writeContract

Maybe you want to create a funcionality like that of etherescan that allows you to interact with a smart contract showing you the function names and arguments and the results.

I provided you with a starting point for you to have an idea.

Maybe implementing that in React would be better.

Let me know if it helps.

1

The ABI is basically a list of objects. You can iterate over this list to get the individual objects and process them. You can easily parse the function name and the input types from them. Then use this to render however you like.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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