Last active
September 23, 2023 05:15
-
-
Save ardislu/73af6dcb35e16be4097cc38e24dae787 to your computer and use it in GitHub Desktop.
This is the minimum amount of JavaScript code to derive an Ethereum address from a signature, using the low-level noble-crypto libraries to minimize dependencies.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Signature } from '@noble/secp256k1'; | |
import { keccak_256 } from '@noble/hashes/sha3'; | |
// The message that will be signed | |
const message = 'Any arbitrary message here. 🏴☠️💯😂'; | |
const encodedMessage = new TextEncoder().encode(message); | |
// Assuming "signature" has been acquired from the user and is in string hex form. | |
// Note that MetaMask and most other high-level tools will automatically inject the ERC-191 prefix to messages. | |
// For example, here's the minimal code to request a signature from MetaMask (assuming the below code is run in a frontend): | |
// const hexMessage = `0x${[...encodedMessage].map(v => v.toString(16).padStart(2, '0')).join('')}`; | |
// const currentAccount = (await ethereum.request({ method: 'eth_requestAccounts' }))[0]; | |
// const signature = await ethereum.request({ | |
// method: 'personal_sign', | |
// params: [hexMessage, currentAccount] | |
// }); | |
// '0xd3fa...' | |
// Replace this with the user's signature. | |
const signature = '0xd3fa...'; | |
// Convert signature hex string into noble-curves Signature object | |
const signatureObj = Signature.fromCompact(signature.substring(2, 130)).addRecoveryBit(signature.slice(-2) === '1b' ? 0 : 1); | |
// ERC-191 | |
const personalMessage = `\x19Ethereum Signed Message:\n${encodedMessage.length}${message}`; // MUST use encodedMessage.length and not message.length | |
const personalMessageHash = keccak_256(new TextEncoder().encode(personalMessage)); | |
// Recover secp256k1 public key | |
const signingPublicKey = signatureObj.recoverPublicKey(personalMessageHash); | |
const serializedPublicKey = signingPublicKey.toRawBytes(false).slice(1); // MUST use uncompressed public key, AND drop the first byte (0x04) | |
// The public address is the last 20 bytes of the keccak256 hash of the public key | |
const signingAddress = `0x${[...keccak_256(serializedPublicKey).slice(-20)].map(v => v.toString(16).padStart(2, '0')).join('')}`; | |
console.log(signingAddress); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment