EIPs/EIPS/eip-55.md

4.8 KiB

EIP: 55
Title: Mixed-case checksum address encoding
Author: Vitalik Buterin
Type: Standard Track
Category: ERC
Status: Accepted
Created: 2016-01-14

Specification

Code:

from ethereum import utils

def checksum_encode(addr): # Takes a 20-byte binary address as input
    o = ''
    v = utils.big_endian_to_int(utils.sha3(addr.hex()))
    for i, c in enumerate(addr.hex()):
        if c in '0123456789':
            o += c
        else:
            o += c.upper() if (v & (2**(255 - 4*i))) else c.lower()
    return '0x'+o

def test(addrstr):
    assert(addrstr == checksum_encode2(bytes.fromhex(addrstr[2:])))

test('0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed')
test('0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359')
test('0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB')
test('0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb')

In English, convert the address to hex, but if the ith digit is a letter (ie. it's one of abcdef) print it in uppercase if the 4*ith bit of the hash of the lowercase hexadecimal address is 1 otherwise print it in lowercase.

Implementation

In javascript:

const createKeccakHash = require('keccak')

function toChecksumAddress (address) {
  address = address.toLowerCase().replace('0x','');
  var hash = createKeccakHash('keccak256').update(address).digest('hex')
  var ret = '0x'

  for (var i = 0; i < address.length; i++) {
    if (parseInt(hash[i], 16) >= 8) {
      ret += address[i].toUpperCase()
    } else {
      ret += address[i]
    }
  }

  return ret
}
> toChecksumAddress('0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359')
'0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359'

Note that the input to the Keccak256 hash is the lowercase hexadecimal string (i.e. the hex address encoded as ASCII):

    var hash = createKeccakHash('keccak256').update(Buffer.from(address.toLowerCase(), 'ascii')).digest()

Rationale

Benefits:

  • Backwards compatible with many hex parsers that accept mixed case, allowing it to be easily introduced over time
  • Keeps the length at 40 characters
  • On average there will be 15 check bits per address, and the net probability that a randomly generated address if mistyped will accidentally pass a check is 0.0247%. This is a ~50x improvement over ICAP, but not as good as a 4-byte check code.

Adoption

Wallet displays checksummed addresses rejects invalid mixed-case rejects too short rejects too long
Etherwall 2.0.1 Yes Yes Yes Yes
Jaxx 1.2.17 No Yes Yes Yes
MetaMask 3.7.8 Yes Yes Yes Yes
Mist 0.8.10 Yes Yes Yes Yes
MyEtherWallet v3.9.4 Yes Yes Yes Yes
Parity 1.6.6-beta (UI) Yes Yes Yes Yes

Exchange support for mixed-case address checksums, as of 2017-05-27:

Exchange displays checksummed deposit addresses rejects invalid mixed-case rejects too short rejects too long
Bitfinex No Yes Yes Yes
Coinbase Yes No Yes Yes
GDAX Yes Yes Yes Yes
Kraken No No Yes Yes
Poloniex No No Yes Yes
Shapeshift No No Yes Yes

References

  1. EIP 55 issue and discussion https://github.com/ethereum/eips/issues/55
  2. Python example by @Recmo https://github.com/ethereum/eips/issues/55#issuecomment-261521584
  3. Python implementation in ethereum-utils
  4. Ethereumjs-util implementation 75f529458b/index.js (L452-L466)