Skip to main content

Implement the Frontend ReactJS


Here we learn how to use sign-in-with-starkware in react js

We are going to use create-a-react-app for this example

  1. Boilerplate & dependencies setup
npx create-react-app my-app
cd my-app

Overwrite package.json with

{
"name": "my-app",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.2.0",
"@testing-library/user-event": "^13.5.0",
"@web3auth/sign-in-with-starkware": "^0.0.1",
"bs58": "^5.0.0",
"get-starknet": "^1.3.1",
"react": "^18.1.0",
"react-app-rewired": "^2.2.1",
"react-dom": "^18.1.0",
"react-scripts": "5.0.1",
"sha1": "^1.1.1",
"starknet": "^3.11.0",
"sweetalert": "^2.1.2",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired 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

  1. Create MyWallet.js this component allows users to select their wallet provider, connect and disconnect to the wallet.
import { Header, Payload, SIWStarkware } from "@web3auth/sign-in-with-starkware";
import { getStarknet } from "get-starknet";
import { useState } from "react";
import { starknetKeccak } from "starknet/dist/utils/hash";
import Swal from "sweetalert";
import "./App.css";

import StarkwareLogo from "./starknet-logo.png";

const MyWallet = () => {
let typedMessage;

const [isConnected, setIsConnected] = useState(false);
const [provider, setProvider] = useState();
// Domain and origin
const domain = window.location.host;
const origin = window.location.origin;

let statement = "Sign in with Starkware to the app.";

const [siwsMessage, setSiwsMessage] = useState();
const [nonce, setNonce] = useState("");
const [sign, setSignature] = useState("");
const [address, setAddress] = useState("");

async function connectWallet() {
const starknet = await getStarknet();

const [add] = await starknet.enable();
if (add.length > 0) {
setAddress(add);
setIsConnected(true);
setProvider(starknet);
}
}

// if (connector.connected && connector.accounts[0] && address != connector.accounts[0]) {
// setAddress(connector.accounts[0])
// }

// Generate a message for signing
// The nonce is generated on the server side
async function createStarkwareMessage() {
const payload = new Payload();
payload.domain = domain;

payload.address = address;
payload.uri = origin;
payload.statement = statement;
payload.version = "1";
payload.chainId = 1;
payload.issuedAt = new Date().toISOString();

const header = new Header();
header.t = "eip191";

let message = new SIWStarkware({ header, payload });
// we need the nonce for verification so getting it in a global variable
setNonce(message.payload.nonce);
setSiwsMessage(message);
console.log(JSON.stringify(message));
const messageText = message.prepareMessage();
console.log(messageText.replace(/\n/g, "\\n"));
const starknet = await getStarknet();
const result = await signMessage(messageText);
setSignature(result.join(","));

// signMessage(messageEncoded).then(resp => setSignature(
// bs58.encode(resp)));
}

const networkId = () => {
const starknet = getStarknet();
if (!starknet?.isConnected) {
return;
}
try {
const { baseUrl } = starknet.provider;
if (baseUrl.includes("alpha-mainnet.starknet.io")) {
return "mainnet-alpha";
} else if (baseUrl.includes("alpha4.starknet.io")) {
return "goerli-alpha";
} else if (baseUrl.match(/^https?:\/\/localhost.*/)) {
return "localhost";
}
} catch {}
};

const signMessage = async (message) => {
message = starknetKeccak(message).toString("hex").substring(0, 31);

typedMessage = {
domain: {
name: "Example DApp",
chainId: networkId() === "mainnet-alpha" ? "SN_MAIN" : "SN_GOERLI",
version: "0.0.1",
},
types: {
StarkNetDomain: [
{ name: "name", type: "felt" },
{ name: "chainId", type: "felt" },
{ name: "version", type: "felt" },
],
Message: [{ name: "message", type: "felt" }],
},
primaryType: "Message",
message: {
message,
},
};

return provider.account.signMessage(typedMessage);
};

return (
<>
{isConnected && sign == "" && (
<span>
<p className="center">Sign Transaction</p>
<input className="publicKey" type="text" id="publicKey" value={address} readOnly />
</span>
)}
{isConnected != true && sign == "" && (
<div>
<div className="logo-wrapper">
<img className="starkware-logo" src={StarkwareLogo} />
</div>
<p className="sign">Sign in With Starkware</p>
</div>
)}

{isConnected && sign == "" && (
<div>
<button className="web3auth" id="w3aBtn" onClick={createStarkwareMessage}>
Sign-in with Starkware
</button>
{/* <WalletDisconnectButton className='walletButtons' /> */}
</div>
)}
{
isConnected != true && sign == "" && (
<button className="web3auth" id="w3aBtn" onClick={connectWallet}>
Connect Wallet
</button>
)
// <WalletModalProvider >
// <WalletMultiButton className='walletButtons' />
// </WalletModalProvider>
}

{sign && (
<>
<p className="center">Verify Signature</p>
<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: "eip191",
s: sign.split(","),
};
const payload = siwsMessage.payload;
siwsMessage.verify({ payload, networkId, signature }, provider).then((resp) => {
if (resp.success == true) {
new Swal("Success", "Signature Verified", "success");
} else {
new Swal("Error", resp.error.type, "error");
}
});
}}
>
Verify
</button>
<button
className="web3auth"
id="verify"
onClick={(e) => {
setSiwsMessage(null);
setNonce("");
setSignature("");
}}
>
Back to Wallet
</button>
</>
)}
</>
);
};

export default MyWallet;
  1. Finally put it all to work by replacing the contents of App.js with
import "./App.css";
import MyWallet from "./MyWallet";

function App() {
return (
<div className="main">
<MyWallet />
<div className="footer-logo-wrapper">
<img className="logo" src="https://app.tor.us/v1.22.2/img/web3auth.b98a3302.svg" />
</div>
</div>
);
}

export default App;
  1. Just the CSS that is missing. Replace the contents of App.css with the following :-
body {
background-image: url(../public/bg.png);
background-repeat: no-repeat;
background-attachment: fixed;
background-size: cover;
align-items: center;
}

.logo-wrapper {
width: 100%;
display: flex;
align-items: center;
padding-top: 20%;
padding-bottom: 4%;
}

.footer-logo-wrapper {
width: 100%;
display: flex;
align-items: center;
padding-top: 5%;
}

.starkware-logo {
margin-left: auto;
margin-right: auto;
width: 20vw;
}

.main {
background: #10141f;
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;
color: #fff;
font-family: "DM Sans", sans-serif;
font-weight: bold;
font-size: 23px;
}

.center {
font-family: "DM Sans";
font-style: normal;
font-weight: 700;
font-size: 28px;
line-height: 120%;
padding-top: 50px;
text-align: center;
}

.walletButtons {
align-content: stretch;
display: flex;
justify-content: center;
flex-wrap: nowrap;
width: 76%;
height: 6vh !important;
font-weight: 700 !important;
font-size: 14px !important;
letter-spacing: 1px !important;
border: 1px solid #9945ff !important;
background-color: transparent !important;
padding: 10px 20px !important;
margin-bottom: 20px !important;
margin-left: auto !important;
margin-right: auto !important;
text-align: center !important;
margin-bottom: 27px;
text-transform: none;
box-sizing: border-box;
cursor: pointer;
transition: all 400ms ease;
font-family: "DM Sans", sans-serif;
color: #9945ff !important;
text-decoration: none;
border-radius: 6px;
}

.web3auth {
width: 76%;
font-weight: 700;
font-size: 14px;
letter-spacing: 1px;
border: 1px solid #9945ff !important;
background-color: transparent !important;
padding: 10px 20px;
box-sizing: border-box;
margin-bottom: 50px;
margin-left: 46px;
text-align: center;
margin-bottom: 20px;
text-transform: none;
box-sizing: border-box;
cursor: pointer;
transition: all 400ms ease;
font-family: "DM Sans", sans-serif;
color: #9945ff;
text-decoration: none;
border-radius: 6px;
height: 6vh !important;
}

.signature,
.publicKey {
font-family: "Roboto";
font-style: normal;
font-weight: 700;
font-size: 12px;
width: 76%;
color: #fff;
letter-spacing: 1px;
background: #1a1f2e;
padding: 10px 20px;
border: none;
border-radius: 20px;
outline: none;
box-sizing: border-box;
border: 2px solid rgba(0, 0, 0, 0.02);
margin-left: 46px;
text-align: center;
margin-bottom: 20px;
}

.logo {
width: 30%;
display: flex;
font-weight: 700;
font-size: 14px;
letter-spacing: 1px;
padding: 10px 10px;
box-sizing: border-box;
margin-bottom: 50px;
margin-left: auto;
margin-right: auto;
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;
}

p {
text-shadow: 0px 0px 3px rgba(117, 117, 117, 0.12);
color: #fff;
text-decoration: none;
}
  1. Start the app with
npm start

PS :- Depending on your webpack version, you may have to create a file called config-overrides.js with the following content :-

// config-overrides.js
module.exports = {
webpack: function (config, env) {
config.module.rules = config.module.rules.map((rule) => {
if (rule.oneOf instanceof Array) {
rule.oneOf[rule.oneOf.length - 1].exclude = [/\.(js|mjs|jsx|cjs|ts|tsx)$/, /\.html$/, /\.json$/];
}
return rule;
});
return config;
},
};