Skip to content

Instantly share code, notes, and snippets.

@ademidun
Created January 14, 2022 00:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ademidun/40627153345977e4d46c31c1fbc3322b to your computer and use it in GitHub Desktop.
Save ademidun/40627153345977e4d46c31c1fbc3322b to your computer and use it in GitHub Desktop.

How to send Ethereum and Binance payments on a React Website with Metamask

We're going to build a web application that allows you to send Ethereum and Binance Coin (BNB) to any address using Metamask.

You can see what the finished product looks like here: https://bit.dev/atila/web-components-library/ui/crypto-payment-form

This video was inspired by Arthur Chmaro's video, How to Send ETH using React.js.

Start with a Sample React Project

You can either add this to an existing React project or create a new one.

For this tutorial we'll use an existing project, the open source atila client web app:

git clone https://github.com/atilatech/client-web-app

You can also create a new React project using npx create-react-app sample-project

Add Basic Form

First we're going to create a basic form that takes two inputs: a destination address and an amount.

Create the file: touch src/components/CryptoPaymentsForm.tsx

Paste the following into the fle to create a simple form:

You can also try typing the code yourself, some people find it helps them with muscle memory.

Protip: You can use an IDE extension like ES7 React/Redux/GraphQL/React-Native snippets to simplify the creation of boiler plate comments.

For me, all I had to type was rfce then it creates the boilerplate for the component.

import  React  from  'react'

 
const  CryptoPaymentsForm = () => {

	return (

		<div>

			<input  type="number"  placeholder="Amount"  />

			<input  placeholder="Destination address"  />

			<button  className="col-12 btn btn-primary">

				Send Payment

			</button>

		</div>

	)

	}

  

export  default  CryptoPaymentsForm

Import that component in our practice page.

{/* src/scenes/Practice.tsx */}
import  React  from  'react'

{/* Add the line below */}
import  CryptoPaymentsForm  from  '../../components/CryptoPaymentsForm'

const  Practice = () => {

	return (

		<div  className="my-3 p-5">

			<h1>

			Practice

			</h1>

			  

			<CryptoPaymentsForm  /> {/* Add this line */}

			</div>

			)

		}


export  default  Practice

Add some Bootstrap classes to make our form look nicer.

import  React  from  'react'

const  CryptoPaymentsForm = () => {

	return (

		<div  className="p-5 card shadow text-center">

		<input  type="number"  placeholder="Amount"  className="col-12 form-control mb-3"  />

		<input  placeholder="Destination address"  className="col-12 form-control mb-3"  />

		<button  className="col-12 btn btn-primary">

		Send Payment

		</button>

		</div>

	)

}

export  default  CryptoPaymentsForm

Setting the Amount and Destination Address

Now we need to be able to set the amount and address and be able to reference them later.

import  React, { useState } from  'react'

const  CryptoPaymentsForm = () => {

	const [amount, setAmount] = useState(0); // new line

	const [destinationAddress, setDestinationAddress] = useState(""); // new line



	const  startPayment = async (event: any) => { // new line

		console.log({amount, destinationAddress});

	}

return (

	<div  className="p-5 card shadow text-center">

		{/* added onChange and onClick attributes */}

		<input  type="number"  placeholder="Amount" value={amount}  className="col-12 form-control mb-3"  onChange={event  => {setAmount(Number.parseFloat(event.target.value))}}  />

		<input  placeholder="Destination address" value={destinationAddress}  className="col-12 form-control mb-3"  onChange={event  => {setDestinationAddress(event.target.value)}}  />

		<button  className="col-12 btn btn-primary"  onClick={startPayment}>

			Send Payment

		</button>

	</div>

)

}

export  default  CryptoPaymentsForm

Add Ethers.js

Okay, now it's time for the really fun part! We're going to use ethers.js to connect metamask to our website.

Ethers is a Javascript package that allows you to connect to the Ethereum blockchain (and other blockchains) using Metamask.

React, Ethers.js, Metamask, Blockchain connection

I also really want to give a shoutout to the author, Richard Moore, it's a really great library.

Install ethers.js: yarn add ethers or npm install --save ethers

Send the transaction to the blockchain:

const  startPayment = async (event: any) => { // new line

console.log({amount, destinationAddress});

  

event.preventDefault();

try {

	if (!window.ethereum) {
		throw  new  Error("No crypto wallet found. Please install it.");
	}

	

		await  window.ethereum.send("eth_requestAccounts");

		const  provider = new  ethers.providers.Web3Provider(window.ethereum);

		const  signer = provider.getSigner();

		ethers.utils.getAddress(destinationAddress);

		const  transactionResponse = await  signer.sendTransaction({

			to:  destinationAddress,

			value:  ethers.utils.parseEther(amount.toString())

		});

		console.log({transactionResponse});

	} catch (error: any) {

		console.log({error});

	}

}

Explaining the Payment Steps

Let's walk through what each snippet means.

First we need to see if Metamask (or a similar wallet browser extension) is installed. When you install Metamask, it automatically injects data into the ethereum global object. If metamask isn't installed then window.ethereum will be empty and an error will be returned to the user. More information on the Ethereum Metamask provider API .

One piece of advice I give people who want to really learn a topic is to "read the original documents". If you want to learn more about how this API conencts to Ethereum, you might also be interested in EIP-1193 which goes into more detail on the specificaitons for the Javascript provider AI.

if (!window.ethereum) {
		throw  new  Error("No crypto wallet found. Please install it.");
	}

This line will bring up a Metamask popup that asks the user if they want to allow our website to connect to their wallet. They need to grant us permission before we can send transactions with their wallets.

await window.ethereum.send("eth_requestAccounts");

The next few lines define the parts of the transaction that need to exist for us to send a transaction. For more information, see the Etherscan API reference, it's very well documented.

The final part actually send the transaction. Ether values can go up to 18 decimal places ( 1 wei is 10^-18 Ether)! This is so many digits that you can run into underflow issues and I'm speaking from personal experience. So often you'll have to convert your numbers to a fixed number of decimal places or parse them into a hex representation.

ethers.utils.parseEther(amount.toString())

Getting "Play Money"

To get some "fake money" that we can test with, we'll use the Ropsten test network and the Egor Fine Faucet to get some "free eth" sent to us.

We're using the Ropsten test network because it's the test network that most resembles the main Ethereum blockchain.

  1. Visit https://faucet.egorfine.com/. (Sometimes it can be hard to find a faucet that works reliably. So you can use a search engine for "ropsten faucet" and try different ones until you find one that works.
  2. Enter the address of the wallet you are going to be sending the ETH from, not the address that will be receiving the ETH..

Sending the first ETH transaction

  1. Make sure you switch to the Ropsten test network in Metamask
  2. Enter a desired amount and a wallet address that you want to send the ETH to. If you don't have one, make a new one in Metamask.
  3. Send the Payment

Showing Transaction Status

Next let's display some information to the user if the transaction went through or if an error occured.

const [amount, setAmount] = useState(0);

const [destinationAddress, setDestinationAddress] = useState("");

const [error, setError] = useState(""); //newline

const [transaction, setTransaction] = useState<ethers.providers.TransactionResponse | null >(null); // new line

Inside startPayment() we'll set the error or transaction response

// startPayment()
// ...
event.preventDefault();

setError("");// clear previous error when a new transaction starts

// ...
console.log({transactionResponse});

setTransaction(transactionResponse); // new line
// ...
catch (error: any) {
	console.log({error});

	setError(error.message); // new line
}

Render the transaction response:

<button  className="col-12 btn btn-primary"  onClick={startPayment}>

	Send Payment

</button>

{transaction &&

	<div  className="alert alert-success mt-3"  role="alert">

	{JSON.stringify(transaction)}

	</div>

}

{error &&

	<div  className="alert alert-danger"  role="alert">

	{JSON.stringify(error)}

</div>

}

Now send another transaction and you should see the following. Practice confirming and rejecting the transaction to see what the response looks like:

enter image description here

Viewing the Transaction in a Block Explorer

Let's add the ability to actually see the transaction on the blockchain using a block explorer like Metamask.

export  interface  TransactionResponsePayment  extends  ethers.providers.TransactionResponse {
	network?: ethers.providers.Network,
}
  
const  CryptoPaymentsForm = () => {
// ...
const [error, setError] = useState("");

const [transaction, setTransaction] = useState<TransactionResponsePayment | null >(null);

let  transactionUrl = "";

if (transaction?.hash) {

	transactionUrl = `https://${transaction.network?.name === "homestead" ? "": transaction.network?.name+"."}etherscan.io/tx/${transaction.hash}`

}
// ...
const  signer = provider.getSigner();
const  network = await  provider.getNetwork();
const  transactionResponse = await  signer.sendTransaction({

to:  destinationAddress,

value:  ethers.utils.parseEther(amount.toString())

}) as  TransactionResponsePayment;
transactionResponse.network = network;

The block explorer we'll be using is Etherscan which is for uses ropsten.etherscan.com for it's testnet block explorer. So we want to change the block explorer we're using based on the network where our transaction was sent.

if (transaction?.hash) {

	transactionUrl = `https://${transaction.network?.name === "homestead" ? "": transaction.network?.name+"."}etherscan.io/tx/${transaction.hash}`

}

Ethereum Payment Complete

That's it! We've now added Ethereum payment to our website in just 5 lines of code.

Excluding the optional stuff, this is all we needed:

await  window.ethereum.send("eth_requestAccounts");
const  provider = new  ethers.providers.Web3Provider(window.ethereum);
const  signer = provider.getSigner();

ethers.utils.getAddress(destinationAddress);

const  transactionResponse = await  signer.sendTransaction({
	to:  destinationAddress,
	value:  ethers.utils.parseEther(amount.toString())
});

This is truly remarkable! In just 5 lines of code, without having to open a bank acount and using completely open source software we're now able to accept money from anyone with an internet connection.

As someone who's integrated credit card payments into my website before the difference is so vast that things like this is what makes me so optimistic about the future and why I believe so much in the power of cryptocurrencies and blockchain in improving the fate of humanity.

Bonus: Adding Binance Coin (BNB) Payments

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment