mirror of
https://github.com/status-im/EIPs.git
synced 2025-02-23 20:28:21 +00:00
* Changes reference to `sha3` to `keccak256`. Ethereum doesn't use `sha3` anywhere, and later in the document it explicitly states that this is actually `keccak256`. To avoid people implementing SHA3 only to find out it doesn't work (like I did), I'm fixing this to be `keccak256`. * Use eth-utils for eip-55 example code Replace the pyethereum code, because it's deprecated, and because it uses the name "sha3" for the hashing function, rather than the clearer "keccak". Co-authored-by: Jason Carver <ut96caarrs@snkmail.com>
153 lines
6.7 KiB
Markdown
153 lines
6.7 KiB
Markdown
---
|
|
eip: 55
|
|
title: Mixed-case checksum address encoding
|
|
author: Vitalik Buterin <vitalik.buterin@ethereum.org>, Alex Van de Sande <avsa@ethereum.org>
|
|
type: Standards Track
|
|
category: ERC
|
|
status: Final
|
|
created: 2016-01-14
|
|
---
|
|
|
|
# Specification
|
|
|
|
Code:
|
|
|
|
``` python
|
|
import eth_utils
|
|
|
|
|
|
def checksum_encode(addr): # Takes a 20-byte binary address as input
|
|
hex_addr = addr.hex()
|
|
checksummed_buffer = ""
|
|
|
|
# Treat the hex address as ascii/utf-8 for keccak256 hashing
|
|
hashed_address = eth_utils.keccak(text=hex_addr).hex()
|
|
|
|
# Iterate over each character in the hex address
|
|
for nibble_index, character in enumerate(hex_addr):
|
|
|
|
if character in "0123456789":
|
|
# We can't upper-case the decimal digits
|
|
checksummed_buffer += character
|
|
elif character in "abcdef":
|
|
# Check if the corresponding hex digit (nibble) in the hash is 8 or higher
|
|
hashed_address_nibble = int(hashed_address[nibble_index], 16)
|
|
if hashed_address_nibble > 7:
|
|
checksummed_buffer += character.upper()
|
|
else:
|
|
checksummed_buffer += character
|
|
else:
|
|
raise eth_utils.ValidationError(
|
|
f"Unrecognized hex character {character!r} at position {nibble_index}"
|
|
)
|
|
|
|
return "0x" + checksummed_buffer
|
|
|
|
|
|
def test(addr_str):
|
|
addr_bytes = eth_utils.to_bytes(hexstr=addr_str)
|
|
checksum_encoded = checksum_encode(addr_bytes)
|
|
assert checksum_encoded == addr_str, f"{checksum_encoded} != expected {addr_str}"
|
|
|
|
|
|
test("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed")
|
|
test("0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359")
|
|
test("0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB")
|
|
test("0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb")
|
|
|
|
```
|
|
|
|
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.
|
|
|
|
# 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.
|
|
|
|
# Implementation
|
|
|
|
In javascript:
|
|
|
|
```js
|
|
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()
|
|
```
|
|
|
|
# Test Cases
|
|
|
|
```
|
|
# All caps
|
|
0x52908400098527886E0F7030069857D2E4169EE7
|
|
0x8617E340B3D01FA5F11F306F4090FD50E238070D
|
|
# All Lower
|
|
0xde709f2102306220921060314715629080e2fb77
|
|
0x27b1fdb04752bbc536007a920d24acb045561c26
|
|
# Normal
|
|
0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed
|
|
0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359
|
|
0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB
|
|
0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb
|
|
```
|
|
|
|
# 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 |
|
|
| Jaxx Liberty 2.0.0 | Yes | Yes | Yes | Yes |
|
|
| Coinomi 1.10 | Yes | Yes | Yes | Yes |
|
|
| Trust Wallet | 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`](https://github.com/pipermerriam/ethereum-utils#to_checksum_addressvalue---text)
|
|
4. Ethereumjs-util implementation https://github.com/ethereumjs/ethereumjs-util/blob/75f529458bc7dc84f85fd0446d0fac92d991c262/index.js#L452-L466
|
|
5. Swift implementation in [`EthereumKit`](https://github.com/yuzushioh/EthereumKit/blob/master/EthereumKit/Helper/EIP55.swift)
|
|
6. Kotlin implementation in [`KEthereum`](https://github.com/walleth/kethereum/tree/master/erc55)
|