Implement the Frontend

Here we learn how to connect the user's wallet with the web application and sign messages.

A completed version of this part can be found in the example repository (01_frontend). This example uses the browser console to print messages, so it should be actively monitored.

To sign in with Ethereum we only need to send two pieces of information:

  • The message

  • The signature of the message

On the previous page, we wrote a function that gives us the means to create messages, so now we only need the means to sign messages.

So we must first connect the web application and the user's wallet so that the application can request information about the Ethereum account and sign messages.

1. In order to do that we will need to add some new dependencies to our print project:

mkdir siwe-frontend && cd siwe-frontend/
yarn init --yes
mkdir src/
yarn add siwe                   \
         ethers                 \
         webpack-node-externals \
         node-polyfill-webpack-plugin
         
yarn add -D html-webpack-plugin \
            webpack             \
            webpack-cli         \
            webpack-dev-server  \
            bufferutil          \
            utf-8-validate

2. Create a new file webpack.config.js and add the following:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const NodePolyfillPlugin = require("node-polyfill-webpack-plugin")

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  resolve: {
    fallback: {
      net: false,
      tls: false,
      fs: false,
    }
  },
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      filename: 'index.html'
    }),
    new NodePolyfillPlugin(),
  ]
};

3. Make sure that package.json has the scripts section and match it to the following:

{
  "name": "siwe-frontend",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "start": "webpack serve"
  },
  "dependencies": {
    "siwe": "^2.1.4",
    "ethers": "^6.3.0",
    "node-polyfill-webpack-plugin": "^2.0.1",
    "webpack-node-externals": "^3.0.0"
  },
  "devDependencies": {
    "bufferutil": "^4.0.7",
    "html-webpack-plugin": "^5.5.1",
    "utf-8-validate": "^6.0.3",
    "webpack": "^5.80.0",
    "webpack-cli": "^5.0.1",
    "webpack-dev-server": "^4.13.3"
  }
}

4. Populate src/index.js with the following:

src/index.js
import { BrowserProvider } from 'ethers';
import { SiweMessage } from 'siwe';

const domain = window.location.host;
const origin = window.location.origin;
const provider = new BrowserProvider(window.ethereum);

function createSiweMessage (address, statement) {
  const message = new SiweMessage({
    domain,
    address,
    statement,
    uri: origin,
    version: '1',
    chainId: '1'
  });
  return message.prepareMessage();
}

function connectWallet () {
  provider.send('eth_requestAccounts', [])
    .catch(() => console.log('user rejected request'));
}

async function signInWithEthereum () {
  const signer = await provider.getSigner();
  const message = createSiweMessage(
      signer.address, 
      'Sign in with Ethereum to the app.'
    );
  console.log(await signer.signMessage(message));
}

// Buttons from the HTML page
const connectWalletBtn = document.getElementById('connectWalletBtn');
const siweBtn = document.getElementById('siweBtn');
connectWalletBtn.onclick = connectWallet;
siweBtn.onclick = signInWithEthereum;

5. Populate src/index.html with the following:

src/index.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>SIWE Quickstart</title>
</head>

<body>
  <div><button id='connectWalletBtn'>Connect wallet</button></div>
  <div><button id='siweBtn'>Sign-in with Ethereum</button></div>
</body>
</html>

6. Now run the following command and visit the URL printed to the console. After you connect your wallet and sign the message, the signature should appear in the console.

yarn start

Explanation of Components

  • ethers.js is a library that provides functionality for interacting with the Ethereum blockchain. We use it here for connecting the webpage to extension wallets.

  • The Metamask extension injects the window.ethereum object into every webpage, and the ethers library provides a convenient provider class to wrap it. We then use this provider to connect to the wallet, and access signing capabilities:

const provider = new BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
  • Running the connectWallet function below will send a request to the MetaMask extension to ask permission to view information about the Ethereum accounts that it controls. MetaMask will then show a window to the user asking them to authorize our application to do so. If they authorize the request then we've connected their account:

function connectWallet () {
  provider.send('eth_requestAccounts', [])
    .catch(() => console.log('user rejected request'));
}
  • We can also now start signing requests with the following:

await signer.signMessage(message);

To disconnect an account, the user must do so from the MetaMask extension in this example.

Last updated