DEV Community

Cover image for Working with smart contract on the Frontend
Mikhail Kedzel for MiKi

Posted on

Working with smart contract on the Frontend

Table of Contents

  1. Working with smart conrtacts on the Frontend
  2. Connecting to a wallet
  3. Better UX for mobile users
  4. Connecting to the smart contract
  5. Exporting all the logic into a hook
  6. Caveats
  7. Thank you for reading

Working with smart conrtacts on the Frontend

Hello fellow web3 enthusiasts, in this article, we'll explore how to integrate smart contracts on the frontend using ethers a library for interacting with Ethereum and its ecosystem. Its often used to connect your decentralized app(dapp) to MetaMask wallet, and interact with Smart Contracts on Ethereum blockchain.

Connecting to a wallet

ethers provides an extremely simple API to connect to a wallet

if (window.ethereum) {
  const accounts = await window.ethereum.request({
    method: "eth_requestAccounts",
  });
  const account = accounts[0];

  setIsWalletConnected(true);
  setCurrentAccount(account);

  console.log("Connected Wallet:", account);
} else {
  alert("Connect your Ethereum Wallet");
}
Enter fullscreen mode Exit fullscreen mode

If you are using TypeScript you'll get an error that property ethereum doesn't exist on window. To fix it create a react-app-env.d.ts in your root directory and paste the following

*/* <reference types="react-scripts" />

interface Window {
    ethereum: any
}
Enter fullscreen mode Exit fullscreen mode

The only caveat is that it won't work on mobile browsers, since there's no way to install the Metamask extension on there. You'll need to use the browser inside metamask app on mobile.

Metamask in app browser

Better UX for mobile users

Some mobile users might be confused if they'll open your open outside metamask. Thankfully, MetaMask has a deeplink generator which you can use to redirect mobile users to the app. Before the user open a page which requires MetaMask, you can check whether he's on mobile and redirect him if needed

<a href={\`${
        !window.ethereum ? `https://metamask.app.link/dapp/${process.env.NEXT_PUBLIC_APP_URL}\` : ''
}pagesWith/metamask\`}>Go to page</a>
Enter fullscreen mode Exit fullscreen mode

Connecting to the smart contract

You'll need your smart contract's ABI stored somewhere on the FE, along with the contract address and the amount you want to send.

import { BrowserProvider, Contract, parseEther } from 'ethers';

const provider = new BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const smartContract = new Contract(
  contractAddress,
  contractABI,
  signer
);

if (isWalletConnected) {
  const valueToSend = parseEther(amount); */ Value to send in Ether
  const methodName = 'publicSale'; /* Replace with your payable method name

  const transaction = await smartContract.connect(signer)[methodName]({
  value: valueToSend
});

  const receipt = await transaction.wait();
  console.log('Transaction hash:', transaction.hash);
  console.log(receipt);
  return transaction;
} else {
  alert("Connect your wallet first to request PAC tokens.");
}
Enter fullscreen mode Exit fullscreen mode

The code also checks if your wallet is connected first, before making the transaction. One of the cool features is ethers picks up the chain you are using automatically, so there's no need for additional config.

Exporting all the logic into a hook

Now, let's make our code a bit more suitable for React. All those separate functions are great, but using them in a React component can be a hassle. So let's encalpsulate everything into a hook.

import {
    useEffect,
    useState
} from "react";
import {
    abi
} from '@/mock/abi';
import {
    BrowserProvider,
    Contract,
    parseEther
} from 'ethers';

export default function useMetamaskSdk() {
    const [isWalletConnected, setIsWalletConnected] = useState(false);
    const [currentAccount, setCurrentAccount] = useState(null);

    const contractAddress: string = process.env.NEXT_PUBLIC_CONTRACT_ADDRESS // It 's better to store sensetive data like that in .env
    const contractABI = abi.abi; // Your abi, make sure to point to the Abi object itself.

    const checkIfWalletIsConnected = async () => {
        try {
            if (window.ethereum) {
                const accounts = await window.ethereum.request({
                    method: "eth<sub>requestAccounts</sub>",
                });
                const account = accounts[0];

                setIsWalletConnected(true);
                setCurrentAccount(account);

                console.log("Connected Wallet:", account);
            } else {
                alert("Connect your Ethereum Wallet");
            }
        } catch (error) {
            console.log(error);
        }
    };

    // Amount - Ether value e.g 0.003
    const requestToken = async (amount: string) => {
        try {
            const provider = new BrowserProvider(window.ethereum);
            const signer = await provider.getSigner();
            const smartContract = new Contract(
                contractAddress,
                contractABI,
                signer
            );

            if (isWalletConnected) {
                const valueToSend = parseEther(amount);*/
                Value to send in Ether
                const methodName = 'publicSale'; // Replace with your payable method name

                const transaction = await smartContract.connect(signer).[methodName]({
                    value: valueToSend
                });

                const receipt = await transaction.wait();
                console.log('Transaction hash:', transaction.hash);
                console.log(receipt);
                return transaction;
            } else {
                alert("Connect your wallet first to request PAC tokens.");
            }
        } catch (error) {
            console.log(error);
        }
    };

    // Connecting the wallet on the first render
    useEffect(() => {
        checkIfWalletIsConnected();
    }, []);

    return {
        requestToken,
        checkIfWalletIsConnected,
        isWalletConnected,
        currentAccount
    }
}
Enter fullscreen mode Exit fullscreen mode

Caveats

There's a couple of downsides to using ethers instead of Moralis

  • Mobile experience suffers a bit, since users have to go through MetaMask browser instead of their preferred one. With Moralis, you can use WalletConnect which recently started supporting MetaMask, so you can do it all in your browser.
  • Poor wallet support. When using WalletConnect instead of ethers, you have a much wider choice of wallets. Even thought MetaMask is used by 80-90% of users, you can grow your user base by offering more wallet options.

Moralis isn't the silver bullet as well, since compared to ethers its API isn't that powerful. So you need to weigh the upsides and downsides of both. We'll cover using Moralis in our next article, so you could compare it with ethers and decide easily.

Thank you for reading

If you have any questions or suggestions, feel free to leave them in the comments!


At MiKi we help businesses make stunning websites with a blazing-fast and cost-efficient cloud backend.
Interested?
Feel free to give us a call at +447588739366, book a meeting at https://lnkd.in/gGmfJu8z or feel out the contact form at our website https://www.miki.digital/contact

Top comments (0)