The address is the last 20 bytes of sha3(publicKey)

In Solidity, using assembly if needed, how could I splice the first 12 bytes from sha3(publicKey) and get the address? What I came up with runs out of gas:

bytes32 b = sha3(publicKey)
address signer;
assembly {
signer := mload(add(b, 0x0c) // skip the first 12 bytes, 0c in hex = 12

MLOAD(0XAB) loads the word (32 byte) located at the memory address 0XAB. Does my function fail because it is trying to load 20 byte only? Can MLOAD load only 20 byte?

  • What you're doing here is loading from memory location sha3(pk)+12, which is (very likely) a 256-bit number. You're running out of gas because expanding memory to such high a location eats all of it. – Noel Maersk Feb 25 '18 at 19:58
  • The "usual" way to achieve what you want is to AND with 0x[12 zero-bytes][20 ff-bytes]. An alternative (commonplace elsewhere in computing) is shifting the input left X bits, then back right X bits (without "rotation") - but the EVM doesn't have shift operations (yet). – Noel Maersk Feb 25 '18 at 20:02
  • 1
    @NoelMaersk That's not what that assembly does. add(b, 0x0c) is analogous to pointer arithmetic. (It gives you the location in memory 12 bytes past the beginning of b.) – user19510 Feb 25 '18 at 23:24

This works:

return address(keccak256(publicKey) & (2**(8*21)-1));

2**(8*21)-1 is just a trick to get 0xFFFFFF... (40 Fs) without typing it. :-)


As pointed out by @schnorr, there's no need for the mask:

return address(keccak256(publicKey));
  • 2
    return address(keccak256(_publicKey)); also works, how come it does not include the first 12 byte of the publickey-hash? – schnorr Feb 25 '18 at 23:16
  • 1
    Oh, that makes sense too. address is of size 20 bytes, so it can't store more bytes than that. It keeps the least significant bytes, like all casts to smaller-size numbers. – user19510 Feb 25 '18 at 23:22
  • Isn't the public key 64 bytes? How does this work? – Daniel Que May 26 '18 at 1:43
  • A public key in Ethereum is 32 bytes, but why does that matter? (keccak256 always returns 32 bytes.) – user19510 May 26 '18 at 1:44
  • public keys in Ethereum are 64 bytes, not 32! PRIVATE keys are 32 bytes though. Source: hackernoon.com/… – Xenonite Jun 16 at 10:17

From an answer to How to convert an bytes to address in Solidity? as well an answer to How to concatenate a bytes32[] array to a string?, I came up with this for converting a bytes32 keyHash to an address.

function getAddressFromPublicKey(bytes _publicKey) returns (address signer) {
    // Get address from public key
    bytes32 keyHash = keccak256(_publicKey);
    uint result = 0;
    for (uint i = keyHash.length-1; i+1 > 12; i--) {
      uint c = uint(keyHash[i]);
      uint to_inc = c * ( 16 ** ((keyHash.length - i-1) * 2));
      result += to_inc;
    return address(result);

In solidity 0.5:

function calculateAddress(bytes memory pub) public pure returns (address addr) {
    bytes32 hash = keccak256(pub);
    assembly {
        mstore(0, hash)
        addr := mload(0)

This should work for Solidity 0.5.x:

bytes32 hash = keccak256(publicKey);
address wallet = address(uint160(bytes20(hash)));

Your Answer

