Photo by GuerrillaBuzz Crypto PR on Unsplash
Ethereum Address Encoding and Verification: EIP-55
implementation in Javascript
Introduction
The blockchain has revolutionalized how humans interact with each other over the internet and has fundamentally changed how internet infrastructure is organized and used. At the core of this revolution is a technology that heavily uses cryptography to assign an identity, verify and authenticate at scale without any central administration.
Ethereum like many blockchain technologies uses cryptography to identify, verify and authenticate ownership over its network. Specifically, Ethereum uses the public key cryptography (also called asymmetric cryptography) to generate it private and public key pair. In this arrangement, the public key (address) is shared with others and the private key is kept a secret. As such while the public key served as an email address, the private key serve as the password to the email. Whoever has the private key to a public key controls whatever resources associated with it.
Even though the public addresses are easy to generate by the computer, they are not human friendly, and people could easily make mistakes typing them and as such send resources (eth) to nonexistent address. Strides have been made to address this situation in the latest form as ENS and one of the earliest form as EIP-55.
EIP-55
Ethereum Improvement Protocol (EIP)-55, authored by Vitalik Buterin and Alex Van de Sande provided a standard to encode and verify the validity of an Ethereum address. It used a letter capitalization comparison between the supplied address and a keccack256 hash of itself.
How it works - EIP-55 encoding an address
Encoding an address with EIP-55 involves two simple steps
- Take keccak-256 hash of the lowercase hexadecimal address (without the preceding 0x) to produce a hash that will be used as a checksum
Original address = "0x440e65EAAb7A23be8b1a61108643B2cff2E5A967"
// Note that the address is without the preceding 0x
// and also in lowercase
address = "440e65eaab7a23be8b1a61108643b2cff2e5a967"
keccak256(address) = "83c2f7dec3cbac4150b339797ab1af103bf4bbd10de7092120b9f0dc0aae7c3b"
- Compare and capitalize each alphabet in the hexadecimal address whose corresponding hexadecimal digit in the hash is greater than or equal to 0x8
address: "440e65eaab7a23be8b1a61108643b2cff2e5a967"
checksum: "83c2f7dec3cbac4150b339797ab1af103bf4bbd10de7092120b9f0dc0aae7c3b"
// Output "440e65EAAb7A23be8b1a61108643B2cff2E5A967"
NB: Although there are 64 Hexadecimal digits in the checksum only the first 40 are used since there are only 40 Hexadecimal digits in the address.
Detecting error in EIP-55 encoded address
EIP-55 compliant wallets can easily detect an error in an EIP-55 encoded Ethereum address by simply replaying the encoding process.
It hashes the the lowercase of the address with keccak256 and then checks with the original address for incorrect capitalization.
This is possible because of the non-correlation property of keccak256. For a single character change in the EIP-55 encoded address, there will be a major change in the resulting hash.
For instance: if the last character (7) of our address is changed to 8 so that the resulting address becomes 0x440e65EAAb7A23be8b1a61108643B2cff2E5A968 which an incorrect address. We get the output below.
address: "440e65eaab7a23be8b1a61108643b2cff2e5a968"
checksum: "6a427592359253da191152827181b4f55b00716d11ff91639825cba2055d34d2"
// Output "440e65Eaab7a23BE8B1a61108643B2Cff2e5a968"
We can see that there is a number of incorrect capitalization in the output when compared to the incorrect address.
NB: Not all wallets are EIP-55 compliant. If the wallet is not EIP-55 complaint, simply ignores the mixed-character capitalization in the address.
A simple EIP-55 implementation in Javascript
const keccak256 = require("keccak256");
function EIP55() {
let originalAddress = "440e65EAAb7A23be8b1a61108643B2cff2E5A968"; // without leading 0x
let hashBuffer = keccak256(originalAddress.toLowerCase()); // produces a buffer object
let checksum = hashBuffer.toString("hex"); // convert buffer to hex string
let checkAddress = originalAddress
.split("")
.map((char, idx) => {
// capitalize if the hex value is greater than 0x8
if (parseInt(checksum[idx], 16) >= 8 ) {
return char.toUpperCase();
}
return char.toLowerCase();
})
.join("");
//check if address is valid
console.log(checkAddress === originalAddress)
}