guides
Guide

How to Build a dApp on the Moonbeam Network with Magic

Magic Staff · September 1, 2021

#Resources

#Quick Start

Bash
01$ git clone https://github.com/magiclabs/example-moonbeam.git
02$ cd example-moonbeam
03$ mv .env.example .env // enter your PUBLISHABLE API Key (from https://dashboard.magic.link)
04$ yarn install
05$ yarn start

#Introduction

#What is Moonbeam

Moonbeam is a developer-focused blockchain aimed at providing compatibility with the existing Ethereum infrastructure. It does this by providing a full EVM implementation, a Web3-compatible API, and bridges that connect Moonbeam to existing Ethereum networks. This allows developers to deploy existing Solidity smart contracts and DApp frontends to Moonbeam with minimal changes.

As a parachain on the Polkadot network, Moonbeam will benefit from the shared security of the Polkadot relay chain and integrations with other chains that are connected to Polkadot (once that functionality is available on Polkadot).

This guide will show how to use Magic on the public Moonbase Alpha testnet.

With Magic, developers can connect to the Moonbase Alpha testnet by simply specifying the network URL when initiating a Magic instance. This guide will show how you can create a web3-enabled app, allow users to switch between the Ethereum and Moonbase Alpha networks, call smart contracts, and send transactions.

#Connecting to Ethereum / Moonbeam

In magic.js, we will need two Magic and two Web3 instances, one for each network, since we're allowing users to switch between the two. If you're only interested in connecting to Moonbeam, then only one instance of Magic and Web3 should be created. We also are adding magicEthereum.network = 'ethereum' to be able to identify the Magic network we're creating.

You’ll use the same API key for both Magic instances so that the user’s public address does not change.

Javascript
01import { Magic } from 'magic-sdk';
02import Web3 from 'web3';
03
04const customNodeOptions = {
05  rpcUrl: 'https://rpc.testnet.moonbeam.network',
06  chainId: 1287
07}
08
09// Setting network to Moonbeam Testnet
10export const magicMoonbeam = new Magic(process.env.REACT_APP_MAGIC_PUBLISHABLE_KEY, { network: customNodeOptions });
11magicMoonbeam.network = 'moonbeam'
12
13export const web3Moonbeam = new Web3(magicMoonbeam.rpcProvider);
14
15// Setting network to Ethereum (Rinkeby Testnet)
16export const magicEthereum = new Magic(process.env.REACT_APP_MAGIC_PUBLISHABLE_KEY, { network: 'rinkeby' });
17magicEthereum.network = 'ethereum'
18
19export const web3Ethereum = new Web3(magicEthereum.rpcProvider);

#Switching Between Networks

Users will be able to switch between the Ethereum and Moonbeam networks with the select element dropdown list. Since one Magic instance points towards Ethereum, and the other Moonbeam, we simply update the instance that we’re using for our app based on whichever network the user selects.

Javascript
01import { magicEthereum, magicMoonbeam } from '../magic';
02
03  const [magic, setMagic] = useState(magicEthereum);
04
05  const handleChangeNetwork = (e) => {
06    e.target.value === 'ethereum' ? setMagic(magicEthereum) : setMagic(magicMoonbeam);
07    fetchBalance(userMetadata.publicAddress);
08    fetchContractMessage();
09  }
10
11  return (
12    <div className='info'>
13      <select name='network' onChange={(e) => handleChangeNetwork(e)}>
14        <option value='ethereum'>Ethereum Testnet (Rinkeby)</option>
15        <option value='moonbeam'>Moonbeam Testnet</option>
16      </select>
17    </div>
18  )

#Viewing User Balance

A user's public address will be the same on both Ethereum and Moonbeam (as long as you are using the same API key for each instance) so a simple web3.eth.getBalance call is all that is needed for either network.

Javascript
01const fetchBalance = (address) => {
02  web3.eth.getBalance(address).then(bal => setBalance(web3.utils.fromWei(bal)))
03}
04
05return (
06<h1>Balance</h1>
07<div className="info">
08  {balance.toString().substring(0, 6)} {magic.network === 'moonbeam' ? 'DEV' : 'ETH'}
09</div>
10)

#Send Transaction

Sending a transaction is also very simple and similar for both networks. The gas limit can be hard-coded in as 21000.

Javascript
01const web3 = magic.network === "ethereum" ? web3Ethereum : web3Moonbeam;
02
03const sendTransaction = async () => {
04  if (!toAddress || !amount) return;
05  const { transactionHash } = await web3.eth.sendTransaction({
06    from: publicAddress,
07    to: toAddress,
08    value: web3.utils.toWei(amount),
09    gas: 21000
10  });
11}
12
13return (
14 <div className="container">
15  <h1>Send Transaction</h1>
16  <input 
17    type="text" 
18    value={toAddress} 
19    onChange={(e) => setToAddress(e.target.value)} 
20    placeholder="To Address" 
21  />
22  <input 
23    type="text" 
24    value={amount} 
25    onChange={(e) => setAmount(e.target.value)} 
26    placeholder="Amount" 
27  />
28  <button onClick={sendTransaction}>Send Transaction</button>
29</div>
30)

#Calling Smart Contracts

Separate smart contracts will need to be deployed on each Ethereum and Moonbeam for your users to interact with them, so you'll need to know the address of each in order to call it.

Javascript
01const [message, setMessage] = useState('...');
02const [newMessage, setNewMessage] = useState('');
03const network = magic.network === 'ethereum' ? 'ethereum' : 'moonbeam';
04const ethContractAddress = '0x5b7D039DaE8D61CC3393fc9eAE42014D0C2CE689';
05const moonbeamContractAddress = '0x8389bb98FcE80c444190A3Ec7d0e0673032771F6';
06const contract = new web3.eth.Contract(abi, network === 'ethereum' ? ethContractAddress : moonbeamContractAddress);
07
08// Grabbing `message` variable value stored in the smart contract
09const fetchContractMessage = () => contract.methods.message().call().then(setMessage);
10
11// Update contract `message` value on the blockchain
12const updateContractMessage = async () => {
13  if (!newMessage) return;
14
15  // Estimate Gas Limit
16  let gasLimit = await contract.methods.update(newMessage).estimateGas({});
17
18  const { transactionHash } = await contract.methods.update(newMessage).send({ 
19    from: publicAddress, 
20    gasLimit,
21  });
22}
23
24return (
25  <h1>Contract Message</h1>
26  <div className="info">{message}</div>
27
28  <h1>Update Message</h1>
29  <input 
30    type="text" 
31    value={newMessage} 
32    onChange={(e) => setNewMessage(e.target.value)} 
33    placeholder="New Message" />
34
35  <button onClick={updateContractMessage}>Update</button>
36)

#Done

That's all there is to it! You've now got an app that allows users to create a wallet with just their email, and switch between the Moonbeam and Ethereum networks within your app.

Let's make some magic!