This guide will show you how to generate a public key, a signature, and a message that can be passed into a Cadence smart contract. This can be achieved using Ethereum JavaScript libraries such as ethers.js or any other Web3 API.
This function depends on the following libraries:
ethers.js: Ethereum's JavaScript library used for interacting with Ethereum's network and its smart contracts.@onflow/fcl: Flow's JavaScript library used for interacting with Flow's network and its smart contracts.
try { const provider = new ethers.providers.Web3Provider(window.ethereum, "any"); // Send a request to access the user's Ethereum accounts await provider.send("eth_requestAccounts", []); const signer = provider.getSigner(); // Define a string message to be signed const toSign = `Hello Cadence World!` const ethSig = await signer.signMessage(toSign); // Remove the '0x' prefix from the signature string const removedPrefix = ethSig.replace(/^0x/, ''); // Construct the sigObj object that consists of the following parts let sigObj = { r: removedPrefix.slice(0, 64), // first 32 bytes of the signature s: removedPrefix.slice(64, 128), // next 32 bytes of the signature recoveryParam: parseInt(removedPrefix.slice(128, 130), 16), // the final byte (called v), used for recovering the public key }; // Combine the 'r' and 's' parts to form the full signature const signature = sigObj.r + sigObj.s; // Construct the Ethereum signed message, following Ethereum's \x19Ethereum Signed Message:\n<length of message><message> convention. // The purpose of this convention is to prevent the signed data from being a valid Ethereum transaction const ethMessageVersion = `\x19Ethereum Signed Message:\n${toSign.length}${toSign}`; // Compute the Keccak-256 hash of the message, which is used to recover the public key const messageHash = ethers.utils.keccak256(ethers.utils.toUtf8Bytes(ethMessageVersion)); const pubKeyWithPrefix = ethers.utils.recoverPublicKey(messageHash, ethSig); // Remove the prefix of the recovered public key const pubKey = pubKeyWithPrefix.slice(4); // The pubKey, toSign, and signature can now be used to interact with Cadence } catch (err) { console.error(err); // Log any errors }This function does the following:
- Set up an Ethereum provider: Uses window.ethereum, which is provided by MetaMask or other Ethereum wallet extensions.
- Request account access: Asks the user to unlock their Ethereum wallet.
- Get a signer: A signer is an Ethereum account that can sign transactions and messages.
- Create a message to sign: For this example, the message is Hello Cadence World!.
- Sign the message: The signer signs the message.
- Clean up the signature: Removes the '0x' prefix from the signature string.
- Create a signature object: Constructs an object with the signature components.
- Create the full signature: Combines the 'r' and 's' components to form the full signature.
- Format the message: Follows Ethereum's signed message convention to prevent the signed data from being a valid Ethereum transaction.
- Hash the message: Computes the Keccak-256 hash of the message, which will be used to recover the public key.
- Recover the public key: Uses the hashed message and the signature to recover the public key.
- Clean up the public key: Removes the prefix of the recovered public key.
You can use this function in any situation where you need to authenticate and verify an Ethereum account in Cadence.
await fcl.query({ cadence: ` import ETHUtils from <ADDRESS_HERE> pub fun main(hexPublicKey: String, hexSignature: String, message : String): Bool { return ETHUtils.verifySignature(hexPublicKey: hexPublicKey, hexSignature: hexSignature, message: message) } `, args: (arg, t) => [arg(pubKey, t.String), arg(signature, t.String), arg(toSign, t.String)], });This example queries a Cadence smart contract to verify the Ethereum signature.