Skip to main content

with Ethereum

The LUKSO Browser Extension is compatible with EIP-4361: Sign-In with Ethereum. Therefore, if the message you want to sign complies with this standard, the LUKSO Browser Extension will show a custom login screen.

Example of Sign-In with Ethereum screen

1. Get the Universal Profile address

The browser extension injects the Universal Profile smart contract address into the page. We need this address to generate the message to sign.

import { ethers } from 'ethers';

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

const accountsRequest = await etherProvider.send('eth_requestAccounts', []);
const signer = etherProvider.getSigner();
const upAddress = await signer.getAddress();
// 0x3E39275Ed3B370E074534edeE13a166512AD32aB

2. Prepare the message

To enable the Sign-In With Ethereum (SIWE) screen, you need to prepare a message with a specific format, as you can see on the standard page or below.

SIWE Template
${domain} wants you to sign in with your Ethereum account:


URI: ${uri}
Version: ${version}
Chain ID: ${chain-id}
Nonce: ${nonce}
Issued At: ${issued-at}
Expiration Time: ${expiration-time}
Not Before: ${not-before}
Request ID: ${request-id}
- ${resources[0]}
- ${resources[1]}
- ${resources[n]}

In JavaScript, you can use the siwe library.

import { SiweMessage } from 'siwe';

// ...

const message = new SiweMessage({
address: upAddress,
statement: 'By logging in you agree to the terms and conditions.',
uri: window.location.origin,
version: '1',
chainId: '2828', // For LUKSO L16
resources: [''],

const siweMessage = message.prepareMessage();

3. Sign the message

Once you have access to the Universal Profile address, you can request a signature. The browser extension will sign the message with the controller key used by the extension (a smart contract can't sign).


When calling Ethers.js signer.signMessage( message ), it uses personal_sign RPC call under the hood. However, our extension only supports the latest version of eth_sign. Therefore, you need to use provider.send("eth_sign", [upAddress, message]) instead.

You can get more information here and here.

const signature = await etherProvider.send('eth_sign', [upAddress, siweMessage]);
// 0x38c53...

🎉 You received the signed message. Now, you need to verify it.

4. Verify the signature

Your Dapp has now received a message signed by the controller address of the Universal Profile. To finalise the login, you need to verify if the message was signed by an address which has the SIGN permission for this UP.

To do so, you can use the isValidSignature(...) function to check if the signature was signed (EIP-1271) by an EOA that has the SIGN permission over the Universal Profile.

import UniversalProfileContract from '@lukso/lsp-smart-contracts/artifacts/UniversalProfile.json';

// ...

const myUniversalProfileContract = new ethers.Contract(upAddress, UniversalProfileContract.abi, signer);

const hashedMessage = ethers.utils.hashMessage(siweMessage);

const isValidSignature = await myUniversalProfileContract.isValidSignature(hashedMessage, signature);

const MAGIC_VALUE = '0x1626ba7e'; //

if (isValidSignature === MAGIC_VALUE) {
console.log('🎉 Sign-In successful!');
} else {
// The EOA which signed the message has no SIGN permission over this UP.
console.log('😭 Log In failed');

If isValidSignature returns the magic value: 0x1626ba7e, then, the message was signed by an EOA which has a SIGN permission for this Universal Profile.