eip-55.md
1 --- 2 eip: 55 3 title: Mixed-case checksum address encoding 4 author: Vitalik Buterin <vitalik.buterin@ethereum.org>, Alex Van de Sande <avsa@ethereum.org> 5 type: Standards Track 6 category: ERC 7 status: Final 8 created: 2016-01-14 9 --- 10 11 # Specification 12 13 Code: 14 15 ``` python 16 from ethereum import utils 17 18 def checksum_encode(addr): # Takes a 20-byte binary address as input 19 o = '' 20 v = utils.big_endian_to_int(utils.sha3(addr.hex())) 21 for i, c in enumerate(addr.hex()): 22 if c in '0123456789': 23 o += c 24 else: 25 o += c.upper() if (v & (2**(255 - 4*i))) else c.lower() 26 return '0x'+o 27 28 def test(addrstr): 29 assert(addrstr == checksum_encode(bytes.fromhex(addrstr[2:]))) 30 31 test('0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed') 32 test('0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359') 33 test('0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB') 34 test('0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb') 35 36 ``` 37 38 In English, convert the address to hex, but if the `i`th digit is a letter (ie. it's one of `abcdef`) print it in uppercase if the `4*i`th bit of the hash of the lowercase hexadecimal address is 1 otherwise print it in lowercase. 39 40 # Rationale 41 42 Benefits: 43 - Backwards compatible with many hex parsers that accept mixed case, allowing it to be easily introduced over time 44 - Keeps the length at 40 characters 45 - 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. 46 47 # Implementation 48 49 In javascript: 50 51 ```js 52 const createKeccakHash = require('keccak') 53 54 function toChecksumAddress (address) { 55 address = address.toLowerCase().replace('0x', '') 56 var hash = createKeccakHash('keccak256').update(address).digest('hex') 57 var ret = '0x' 58 59 for (var i = 0; i < address.length; i++) { 60 if (parseInt(hash[i], 16) >= 8) { 61 ret += address[i].toUpperCase() 62 } else { 63 ret += address[i] 64 } 65 } 66 67 return ret 68 } 69 ``` 70 71 ``` 72 > toChecksumAddress('0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359') 73 '0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359' 74 ``` 75 76 Note that the input to the Keccak256 hash is the lowercase hexadecimal string (i.e. the hex address encoded as ASCII): 77 78 ``` 79 var hash = createKeccakHash('keccak256').update(Buffer.from(address.toLowerCase(), 'ascii')).digest() 80 ``` 81 82 # Test Cases 83 84 ``` 85 # All caps 86 0x52908400098527886E0F7030069857D2E4169EE7 87 0x8617E340B3D01FA5F11F306F4090FD50E238070D 88 # All Lower 89 0xde709f2102306220921060314715629080e2fb77 90 0x27b1fdb04752bbc536007a920d24acb045561c26 91 # Normal 92 0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed 93 0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359 94 0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB 95 0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb 96 ``` 97 98 # Adoption 99 100 | Wallet | displays checksummed addresses | rejects invalid mixed-case | rejects too short | rejects too long | 101 |--------------------------|--------------------------------|----------------------------|-------------------|------------------| 102 | Etherwall 2.0.1 | Yes | Yes | Yes | Yes | 103 | Jaxx 1.2.17 | No | Yes | Yes | Yes | 104 | MetaMask 3.7.8 | Yes | Yes | Yes | Yes | 105 | Mist 0.8.10 | Yes | Yes | Yes | Yes | 106 | MyEtherWallet v3.9.4 | Yes | Yes | Yes | Yes | 107 | Parity 1.6.6-beta (UI) | Yes | Yes | Yes | Yes | 108 | Jaxx Liberty 2.0.0 | Yes | Yes | Yes | Yes | 109 | Coinomi 1.10 | Yes | Yes | Yes | Yes | 110 | Trust Wallet | Yes | Yes | Yes | Yes | 111 112 ### Exchange support for mixed-case address checksums, as of 2017-05-27: 113 114 | Exchange | displays checksummed deposit addresses | rejects invalid mixed-case | rejects too short | rejects too long | 115 |--------------|----------------------------------------|----------------------------|-------------------|------------------| 116 | Bitfinex | No | Yes | Yes | Yes | 117 | Coinbase | Yes | No | Yes | Yes | 118 | GDAX | Yes | Yes | Yes | Yes | 119 | Kraken | No | No | Yes | Yes | 120 | Poloniex | No | No | Yes | Yes | 121 | Shapeshift | No | No | Yes | Yes | 122 123 # References 124 125 1. EIP 55 issue and discussion https://github.com/ethereum/eips/issues/55 126 2. Python example by @Recmo https://github.com/ethereum/eips/issues/55#issuecomment-261521584 127 3. Python implementation in [`ethereum-utils`](https://github.com/pipermerriam/ethereum-utils#to_checksum_addressvalue---text) 128 4. Ethereumjs-util implementation https://github.com/ethereumjs/ethereumjs-util/blob/75f529458bc7dc84f85fd0446d0fac92d991c262/index.js#L452-L466 129 5. Swift implementation in [`EthereumKit`](https://github.com/yuzushioh/EthereumKit/blob/master/EthereumKit/Helper/EIP55.swift) 130 6. Kotlin implementation in [`KEthereum`](https://github.com/walleth/kethereum/tree/master/erc55)