Created: Mar 28, 2024

[Write-Up: Challenges and Pitfalls for Beginner Front-End Blockchain Developers]

Introduction

In the dynamic realm of blockchain, beginner front-end developers often encounter a myriad of challenges. From struggles with unfamiliar terms and technologies to meeting the unique demands of the industry, navigating this landscape can be both hard and rewarding. 

Here we delve into the major challenges faced by front-end developers in blockchain, offer strategies to overcome them effectively, and examine a simple project to highlight the core difficulties of Web3 front-end development.

Major Challenges

  1. Adapting to the New Environment: Transitioning into blockchain development requires acclimating to a new paradigm, especially for those accustomed to traditional software development. The decentralized nature of blockchain demands a shift in mindset, which can initially feel disorienting for beginners.
  2. Security and Trust: Security is a top priority in blockchain, with maintaining trust among users and safeguarding sensitive data. Beginners must familiarize themselves with basic web security principles to mitigate risks and ensure the integrity of their apps.
  3. Complex Data Processing at Front-end: Unlike centralized apps, blockchain apps often lack backend servers, delegating complex data processing to the front-end. This places greater emphasis on front-end developers to optimize business logic, performance, and memory usage effectively.
  4. Dealing with poor code quality: Within the fast-paced startup environment prevalent in blockchain development, encountering legacy or poorly written code is common. Front-end developers must be prepared to refactor and maintain such code, despite it being time-consuming and frustrating.

Toolkit

  • Solidity: Basic knowledge of Solidity is essential for front-end developers to read and analyze smart contracts.
  • Hardhat: Understanding Hardhat is crucial for testing, debugging, and deploying smart contracts in EVM-compatible blockchains.
  • Ethers.js or Web3.js: These libraries are indispensable for front-end developers to interact with the blockchain.
  • Typing smart contracts: Learning to set up and utilize TypeChain, ABIType or similar tools helps generate type-safe wrappers for smart contracts, enhancing the development experience.

Strategies

Choosing the right approach depends on individual circumstances, such as available time and prior experience. Balancing theory and practice is key. Here are just two possible approaches:

  • Top-to-Bottom Strategy: Grasp the minimal basics and start building projects as fast as possible. While this approach may yield employable skills faster, it risks overlooking crucial theoretical concepts that could lead to issues in the long run.
  • Bottom-to-Top Strategy: Delve deep into the history and intricacies of blockchain technology, explore code of open-source projects, gain hands-on experience writing smart contracts, familiarize yourself with every aspect of Web3 development. This method requires more time upfront but provides a comprehensive understanding of Web3 development which would boost your growth as you land your first blockchain-related job.

Practical guide

Let’s break down a small Blockchain project for practical demonstration of Web3 development’s intricacies. This project comprises two key components: Hardhat repo and Frontend repo. It’s a crowdfunding platform where users can launch fundraising campaigns, donate and withdraw tokens. Please, don’t mind primitive UI, it serves minimal functionality.

img1

Smart contracts

First of all, a Web3 front-end developer should thoughtfully examine the smart contracts they will work with. MyToken.sol is a ERC-20 token used in transactions. CrowdFund.sol contract manages fundraising activities through methods like launch for starting campaigns, pledge for donations, and claim for withdrawing tokens. Each method triggers an event with a corresponding name, which is listened to and displayed by the front-end. The ability to understand smart contracts is crucial in Web3 just like API in mainstream web development.

An example of CrowdFund’s method that initiates token transfer from a user to a certain campaign:

function pledge(uint _id, uint _amount) external {
    Campaign storage campaign = campaigns[_id];
    require(block.timestamp >= campaign.startAt, "not started");
    require(block.timestamp <= campaign.endAt, "ended");

    campaign.pledged += _amount;
    pledgedAmount[_id][msg.sender] += _amount;
    token.transferFrom(msg.sender, address(this), _amount);

    emit Pledge(_id, msg.sender, _amount);
}

Deploy smart contracts

Secondly, run the hardhat node locally and deploy smart contracts. Here’s deploy.ts script: 

async function main() {
  const [deployer] = await ethers.getSigners();

  console.log("Deploying contracts with the account:", deployer.address);

  // deploy MyToken
  const tokenFactory = await ethers.getContractFactory("MyToken");
  const token = await tokenFactory.deploy(1000000)
  await token.deploymentTransaction()
  console.log("MyToken address:", await token.getAddress());

  // deploy CrowdFund
  const crowdFundFactory = await ethers.getContractFactory("CrowdFund");
  const crowdFund = await crowdFundFactory.deploy(token)
  await crowdFund.deploymentTransaction()

  console.log("CrowdFund address:", await crowdFund.getAddress());
}  

This involves initializing the MyToken and CrowdFund contracts and deploying them to the local network. N.B.: MyToken requires an initial token supply to enable token transfers, CrowdFund needs linking to MyToken for use in transactions.

Front-end side

The front-end side focuses on integrating with the blockchain. First of all, take a look at blockchainInfo dir where all the necessary data about smart contracts and network is stored: ABI, smart contract addresses, network configs.

Next we should highlight the useMetaMask hook which serves as an entry point to the app. MetaMask is not the only wallet for integration but it was chosen due to popularity and simplicity. The hook connects the front-end app to the blockchain, checks if there’s a connected account and if no issues are encountered, displays the current account’s address. MetaMaskContext.Provider wraps the whole app and enables interaction with blockchain.

src/
└── blockchainInfo/
    ├── abi/
    │   ├── Achievement.json
    │   ├── Rewarder.json
    │   └── ValueToken.json
    ├── addresses.json
    ├── chainConfig.ts
    └── README.md
return (
    <MetaMaskContext.Provider
      value={{
        accounts,
        hasProvider,
        error: !!errorMessage,
        errorMessage,
        isConnecting,
        connectMetaMask,
        clearError,
      }}
    >
      {children}
    </MetaMaskContext.Provider>
)

Connect to smart contract

To interact with the smart contracts, front-end app must first connect to them using information about the network, user (provider and signer), and the contracts themselves (their addresses on the chain). We use TypeChain-generated factory to connect to the contracts:

const getContracts = async () => {
  const provider = new providers.Web3Provider(window.ethereum as unknown as providers.ExternalProvider, 'any')
  const signer = await provider?.getSigner()
  if (!signer) return {}

  // use Typechain-generated contract factory to connect to smart contracts
  return {
    crowdFund: CrowdFund__factory.connect(crowdFundAddress, signer), 
    myToken: MyToken__factory.connect(myTokenAddress, signer) 
  }
}

Read data from blockchain

Reading data from blockchain, such as campaign details, is straightforward and similar to a GET request in traditional web development. Here’s how we get data about fundraising campaigns from CrowdFund contract by calling getCampaignIds and getCampaignById methods:

const getCampaign = async () => {
  if (!contract) return

  // call getter method
  const allCampaignIds = await contract.getCampaignIds()
  const campaignFetchingPromises = allCampaignIds.map(async (campaignId: BigNumber) => contract.getCampaignById(campaignId))

  const result = await Promise.all(campaignFetchingPromises) as Campaign[]
  setCampaigns(result)
}

Write data to blockchain

Writing data presents more challenges, particularly for beginners. It involves: 

  • smart contract’s method call and transaction creation.
  • correct parameters formatting: Ethers.js accepts only whole numbers and usually you would work with BigNumbers. Human-readable params, like goal, start and end should be converted into their BigNumber representation. 
  • waiting till the transaction is completed. Normally you should also pass a number of confirmations to wait() method. It will specify how many blocks must prove that the transaction was included in the blockchain to consider this transaction successful. We don't do it here because it's a local network.
// create transaction
const tx = await contract.launch(
  BigNumber.from(goal), // how many tokens are expected to raise - number
  dateToBigNumber(start), // when campaign starts - timestamp
  dateToBigNumber(end), // when campaign ends - timestamp
)

await tx.wait()

Transfer tokens

Much more interesting example is a pledge transaction. Before pledging tokens to a campaign, we should ask the user to approve tokens transfer from their account to the contract's account. 

In ERC-20 token standard transferFrom method allows a third-party to transfer tokens on behalf of the token owner. To do so, it needs to get approval first. This is why before pledging a token to a campaign we need to request user’s approval.

CrowdFund’s pledge method:

token.transferFrom(msg.sender, address(this), _amount);

Here’s how it’s done at the front-end:

try {
    setIsLoading(true)

    // before transferring user's tokens, we need to ask for approval
    await myToken.approve(contract.address, amount)
    listenToContractEvent(provider, contract, 'Pledge')

    // create transaction
    const tx = await contract.pledge(BigNumber.from(chosenCampaignId), BigNumber.from(amount))
    await tx.wait()
} finally {
    setIsLoading(false)
}

Event listeners

Finally, the front-end should listen to events emitted by the contract. Conceptually it’s analogous to traditional web development but includes unique challenges. For example, due to some issues with Ethers.js (v. 5.7) we need to wrap contract’s event listener in block listener. This is done to prevent the event handler from firing on page reload. 

What we actually mean here: add a listener only when a new block is detected; eventName is the name of the event you want to watch; the second param for contract.on() is the event handler.

const setEventHandler = () => {
  // tricky moment
  provider.once('block', () => {
    contract.on(eventName, genericEventHandler)
  })
}

We haven’t mentioned here some other challenges, like loading management, error handling, gas price estimation, formatting blockchain data into human-readable form, etc. Our current goal is just scratch the surface of Web3 front-end development, showcasing the essential skills and considerations needed to create blockchain-based apps.

Conclusion

Successfully navigating the challenges of blockchain development demands a blend of technical proficiency, self-awareness, and perseverance. By leveraging the right tools, adopting optimal learning strategies, and maintaining a balance between theory and practice, front-end developers can embark on a rewarding career in Web 3 development. Stay vigilant of the challenges outlined and remain committed to continuous improvement and growth in this dynamic field.

Summary

This write-up explores the challenges faced by beginner front-end developers entering the dynamic world of blockchain technology. It outlines key challenges in the industry and provides a toolkit of essential tools, along with strategies for learning and development. In the practical section it offers a step-by-step guide into a simple blockchain project, explaining how to set up a development environment, deploy smart contracts, and integrate blockchain functionalities into web apps.