Implement the Frontend JS
info
React JS example here.
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 folder in the repository (example/javascript-app). To sign in with Solana we only need to send two pieces of information:
- The address
- The message (statement)
On the previous page, we wrote a function function createSolanaMessage(address, statement)
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 Solana account and sign messages.
- We first create the project structure:
mkdir siws-frontend && cd siws-frontend/
npm init --yes
mkdir src/
- Create a new file webpack.config.js and add the following:
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: "development",
entry: "./src/index.js",
resolve: {
fallback: {
fs: false,
path: false,
util: false,
},
},
output: {
filename: "main.js",
path: path.resolve(__dirname, "dist"),
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
filename: "index.html",
}),
],
};
- Overwrite package.json with the following
{
"name": "siws-frontend",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "webpack serve --config webpack.config.js --progress"
},
"author": "",
"license": "ISC",
"dependencies": {
"@solana/wallet-adapter-base": "^0.9.5",
"@solana/wallet-adapter-react": "^0.15.4",
"@solana/wallet-adapter-react-ui": "^0.9.6",
"@solana/wallet-adapter-wallets": "^0.16.0",
"@solana/web3.js": "^1.39.1",
"@types/varint": "^6.0.0",
"@web3auth/sign-in-with-solana": "^0.0.1",
"multiformats": "^9.0.4",
"sweetalert": "^2.1.2",
"varint": "^6.0.0"
},
"devDependencies": {
"html-webpack-plugin": "^5.5.0",
"webpack": "^5.72.0",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.9.0"
}
}
Then run npm i
- Populate src/index.js with the following:
import swal from "sweetalert";
import { Header, Payload, SIWS } from "@web3auth/sign-in-with-solana";
// Domain and origin
const domain = window.location.host;
const origin = window.location.origin;
// Nonce, public key and other global variables
let nonce = "";
let publicKey = "";
let message;
// Generate a message for signing
// The nonce is generated on the server side
function createSolanaMessage(address, statement) {
const header = new Header();
header.t = "sip99";
const payload = new Payload();
payload.domain = domain;
payload.address = address;
payload.uri = origin;
payload.statement = statement;
payload.version = "1";
payload.chainId = "1";
message = new SIWS({
header,
payload,
});
// we need the nonce for verification so getting it in a global variable
nonce = message.nonce;
// Returning the prepared message
return message.prepareMessage();
}
// Connect the solana wallet
// Tested with Phantom
function connectWallet() {
try {
window.solana.connect().then((resp) => {
// Successful connection
// Get the publicKey (string)
publicKey = resp.publicKey.toString();
});
} catch (error) {
console.log("User rejected the request." + error);
}
}
// Perform the sign in with Solana
// This function does the following
// calls the createSolanaMessage,
// encodes that message and then
// creates a browser signin request
async function signInWithSolana() {
const message = createSolanaMessage(publicKey, "Sign in with Solana to the app.");
const encodedMessage = new TextEncoder().encode(message);
const signedMessage = await window.solana.request({
method: "signMessage",
params: {
message: encodedMessage,
display: "text",
},
});
document.getElementById("postSignIn").style = "display:block";
document.getElementById("signature").value = signedMessage.signature;
document.getElementById("publicKey").value = signedMessage.publicKey;
}
// Verify Signature allows users to test
// if their signature is valid
async function verifySignature() {
const signatureString = document.getElementById("signature").value;
const publicKey = document.getElementById("publicKey").value;
const signature = {
t: "sip99",
s: signatureString,
};
const payload = message.payload;
payload.address = publicKey;
const resp = await message.verify({ payload, signature });
if (resp.success == true) {
swal("Success", "Signature Verified", "success");
} else {
swal("Error", resp.error.type, "error");
}
}
/*
Creating CID
async function createCID() {
const {bytes, cid} = await message.createCID(JSON.stringify(message));
swal("CID base64 encoded",cid.toString(base64.encoder),"info")
// To decode the block use from the bytes
// await message.decodeBlock(bytes)
}
const cidBtn = document.getElementById('cid');
*/
// On-load, connect the wallet
window.addEventListener("load", function () {
connectWallet();
});
const connectWalletBtn = document.getElementById("connectWalletBtn");
const w3aBtn = document.getElementById("w3aBtn");
const verifyBtn = document.getElementById("verify");
verifyBtn.onclick = verifySignature;
connectWalletBtn.onclick = connectWallet;
w3aBtn.onclick = signInWithSolana;
- Populate src/index.html with the following:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Web3Auth SWS</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
body {
background-image: url('https://web3auth.io/images/Group-427318217_1.png');
background-repeat: no-repeat;
background-attachment: fixed;
background-size: cover;
}
.main {
background-color: #FFFFFF;
width: 400px;
height: auto;
margin: 7em auto;
border-radius: 1.5em;
box-shadow: 0px 11px 35px 2px rgba(0, 0, 0, 0.14);
}
.sign {
padding-top: 40px;
color: #8f9095;
font-family: 'DM Sans', sans-serif;
font-weight: bold;
font-size: 23px;
}
.web3auth {
width: 76%;
font-weight: 700;
font-size: 14px;
letter-spacing: 1px;
border: 1px solid #0364ff;
background-color: #0364ff;
padding: 10px 20px;
box-sizing: border-box;
margin-bottom: 50px;
margin-left: 46px;
text-align: center;
margin-bottom: 27px;
text-transform: none;
box-sizing: border-box;
cursor: pointer;
transition: all 400ms ease;
font-family: 'DM Sans', sans-serif;
color: #fff;
text-decoration: none;
border-radius: 6px;
}
.signature,.publicKey {
width: 76%;
color: rgb(38, 50, 56);
font-weight: 700;
font-size: 14px;
letter-spacing: 1px;
background: rgba(136, 126, 126, 0.04);
padding: 10px 20px;
border: none;
border-radius: 20px;
outline: none;
box-sizing: border-box;
border: 2px solid rgba(0, 0, 0, 0.02);
margin-bottom: 50px;
margin-left: 46px;
text-align: center;
margin-bottom: 27px;
font-family: 'Ubuntu', sans-serif;
}
.logo {
width: 76%;
font-weight: 700;
font-size: 14px;
letter-spacing: 1px;
padding: 10px 20px;
box-sizing: border-box;
margin-bottom: 50px;
margin-left: 46px;
text-align: center;
margin-bottom: 27px;
text-transform: none;
box-sizing: border-box;
cursor: pointer;
transition: all 400ms ease;
font-family: 'DM Sans', sans-serif;
color: #fff;
text-decoration: none;
border-radius: 6px;
}
.web3auth:focus {
border: 2px solid rgba(0, 0, 0, 0.18) !important;
}
p {
text-shadow: 0px 0px 3px rgba(117, 117, 117, 0.12);
color: #8f9095;
text-decoration: none
}
</style>
</head>
<body>
<div class="main">
<p class="sign" align="center">Sign in With Solana</p>
<br>
<div>
<button class='web3auth' id='connectWalletBtn'>Connect wallet</button>
<button class='web3auth' id='w3aBtn'>Sign-in with Solana</button>
<span id="postSignIn" style="display:none">
<p align="center">Public Key</p>
<input class='publicKey' type="text" id="publicKey"/>
<p align="center">Signature</p>
<input class='signature' type="text" id="signature" />
<button class='web3auth' id='verify'>Verify</button>
</span>
<p align="center">Created By</p>
<img align="center" class='logo' src="https://app.tor.us/v1.22.2/img/web3auth.b98a3302.svg">
</div>
</div>
</div>
</body>
</html>
- 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.
npm run dev
Explanation of Components
- @solana/web3.js is a library that provides functionality for interacting with the Solana blockchain. We use it here for connecting the webpage to extension wallets.
- The Phantom wallet injects the window.solana object into every webpage, and the library provides a convenient provider class to wrap it. We then use this provider to connect to the wallet, and access signing capabilities:
- Running the connectWallet function below will send a request to the Phantom wallet to ask permission to view information about the Solana accounts that it controls. Phantom 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:
// Connect the solana wallet
// Tested with Phantom
function connectWallet() {
try {
window.solana.connect().then((resp) => {
// Successful connection
// Get the publicKey (string)
publicKey = resp.publicKey.toString();
});
} catch (error) {
console.log("User rejected the request." + error);
}
}
- We can also now start signing requests with the following:
const message = createSolanaMessage(publicKey, "Sign in with Solana to the app.");
const encodedMessage = new TextEncoder().encode(message);
const signedMessage = await window.solana.request({
method: "signMessage",
params: {
message: encodedMessage,
display: "text",
},
});