Implement the Frontend ReactJS
Here we learn how to use sign-in-with-solana in react js
We are going to use creat-a-react-app for this example
- Boilerplate & dependencies setup
npx create-react-app my-app --template typescript
cd my-app
Overwrite package.json
with
{
"name": "solana-frontend-quickstart",
"version": "0.1.0",
"private": true,
"dependencies": {
"@solana/wallet-adapter-base": "^0.6.0",
"@solana/wallet-adapter-react": "^0.12.2",
"@solana/wallet-adapter-react-ui": "^0.4.2",
"@solana/wallet-adapter-wallets": "^0.10.0",
"@solana/web3.js": "^1.29.1",
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"@web3auth/sign-in-with-solana": "^0.0.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "4.0.3",
"sweetalert": "^2.1.2",
"typescript": "^4.1.2",
"web-vitals": "^1.0.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": ["react-app", "react-app/jest"]
},
"browserslist": {
"production": [">0.2%", "not dead", "not op_mini all"],
"development": ["last 1 chrome version", "last 1 firefox version", "last 1 safari version"]
}
}
Now run npm i
- Create
MyWallet.tsx
this component allows users to select their wallet provider, connect and disconnect to the wallet.
import { useConnection, useWallet } from "@solana/wallet-adapter-react";
import { WalletDisconnectButton, WalletModalProvider, WalletMultiButton } from "@solana/wallet-adapter-react-ui";
const MyWallet = () => {
const { connection } = useConnection();
let walletAddress = "";
// if you use anchor, use the anchor hook instead
// const wallet = useAnchorWallet();
// const walletAddress = wallet?.publicKey.toString();
const wallet = useWallet();
if (wallet.connected && wallet.publicKey) {
walletAddress = wallet.publicKey.toString();
}
return (
<>
{wallet.connected && (
<span>
<p className="center">Public Key</p>
<input className="publicKey" type="text" id="publicKey" value={walletAddress} />
</span>
)}
{(wallet.connected && <WalletDisconnectButton className="web3auth" />) || (
<WalletModalProvider>
<WalletMultiButton className="web3auth" />
</WalletModalProvider>
)}
</>
);
};
export default MyWallet;
- Now create
SignIn.tsx
actually implements the sign-in-with-solana flow - signing message as well as verification.
import { useWallet } from "@solana/wallet-adapter-react";
import { Payload, SIWS } from "@web3auth/sign-in-with-solana";
import bs58 from "bs58";
import { useState } from "react";
import swal from "sweetalert";
const SignIn = () => {
const { publicKey, signMessage } = useWallet();
// Domain and origin
const domain = window.location.host;
const origin = window.location.origin;
let statement = "Sign in with Solana to the app.";
const [siwsMessage, setSiwsMessage] = useState<SIWS>();
const [nonce, setNonce] = useState("");
const [sign, setSignature] = useState("");
// Generate a message for signing
// The nonce is generated on the server side
function createSolanaMessage() {
const payload = new Payload();
payload.domain = domain;
payload.address = publicKey!.toString();
payload.uri = origin;
payload.statement = statement;
payload.version = "1";
payload.chainId = 1;
let message = new SIWS({ payload });
// we need the nonce for verification so getting it in a global variable
setNonce(message.payload.nonce);
setSiwsMessage(message);
const messageText = message.prepareMessage();
const messageEncoded = new TextEncoder().encode(messageText);
signMessage!(messageEncoded).then((resp) => setSignature(bs58.encode(resp)));
}
return (
<>
<button className="web3auth" id="w3aBtn" onClick={createSolanaMessage}>
Sign-in with Solana
</button>
{sign && (
<>
<input className="signature" type="text" id="signature" value={sign} onChange={(e) => setSignature(e.target.value)} />
<button
className="web3auth"
id="verify"
onClick={(e) => {
const signature = {
t: "sip99",
s: sign,
};
const payload = siwsMessage!.payload;
siwsMessage!.verify({ payload, signature }).then((resp) => {
if (resp.success == true) {
swal("Success", "Signature Verified", "success");
} else {
swal("Error", resp.error!.type, "error");
}
});
}}
>
Verify
</button>
</>
)}
</>
);
};
export default SignIn;
- Finally put it all to work by replacing the contents of
App.tsx
with
import React from "react";
import "./App.css";
import { WalletAdapterNetwork } from "@solana/wallet-adapter-base";
import { ConnectionProvider, WalletProvider } from "@solana/wallet-adapter-react";
import {
getLedgerWallet,
getPhantomWallet,
getSlopeWallet,
getSolflareWallet,
getSolletExtensionWallet,
getSolletWallet,
} from "@solana/wallet-adapter-wallets";
import { clusterApiUrl } from "@solana/web3.js";
import MyWallet from "./MyWallet";
import SignIn from "./SignIn";
function App() {
const network = WalletAdapterNetwork.Mainnet;
// You can also provide a custom RPC endpoint
const endpoint = React.useMemo(() => clusterApiUrl(network), [network]);
// @solana/wallet-adapter-wallets includes all the adapters but supports tree shaking --
// Only the wallets you configure here will be compiled into your application
const wallets = React.useMemo(
() => [
getPhantomWallet(),
getSlopeWallet(),
getSolflareWallet(),
getLedgerWallet(),
getSolletWallet({ network }),
getSolletExtensionWallet({ network }),
],
[network]
);
return (
<ConnectionProvider endpoint={endpoint}>
<WalletProvider wallets={wallets}>
<div className="main">
<p className="sign">Sign in With Solana</p>
<br />
<div>
<MyWallet />
<SignIn />
<span id="postSignIn" style={{ display: "none" }}>
<p>Public Key</p>
<input className="publicKey" type="text" id="publicKey" />
<p>Signature</p>
<input className="signature" type="text" id="signature" />
<button className="web3auth" id="verify">
Verify
</button>
</span>
<p className="center">Created By</p>
<img className="logo" src="https://app.tor.us/v1.22.2/img/web3auth.b98a3302.svg" />
</div>
</div>
</WalletProvider>
</ConnectionProvider>
);
}
export default App;
- Just the CSS that is missing. Replace the contents of
App.css
with the following :-
body {
background-image: url("https://web3auth.io/images/Group-427318217_1.png");
background-repeat: no-repeat;
background-attachment: fixed;
background-size: cover;
align-items: center;
}
.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 {
text-align: center;
padding-top: 40px;
color: #8f9095;
font-family: "DM Sans", sans-serif;
font-weight: bold;
font-size: 23px;
}
.center {
text-align: center;
}
.web3auth {
width: 76%;
font-weight: 700;
font-size: 14px;
letter-spacing: 1px;
border: 1px solid #0364ff !important;
background-color: #0364ff !important;
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;
}
- Start the app with
npm start
PS :- Depending on the create-react-app version you might face issues with index.tsx Try replacing the index.tsx content with the following :-
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import "./index.css";
import reportWebVitals from "./reportWebVitals";
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();