It is often easy to use ABI encoding toolkit that does so called "tight packing" or given arguments. You can perform this with one built-in function.
In Solidity
/**
* A test method exposed to be called from clients to compare that ABI packing and hashing
* is same across different programming languages.
*
* Does ABI encoding for an address and then calculates KECCAK-256 hash over the bytes.
*
* https://web3js.readthedocs.io/en/v1.2.0/web3-utils.html#soliditysha3
*
*/
function calculateAddressHash(address a) public pure returns (bytes32 hash, bytes memory data) {
// First we ABI encode the address to bytes.
// This is so called "tight packing"
// https://web3js.readthedocs.io/en/v1.2.0/web3-utils.html#soliditysha3
bytes memory packed = abi.encodePacked(a);
// Then we calculate keccak256 over the resulting bytes
bytes32 hashResult = keccak256(packed);
return(hashResult, packed);
}
In JavaScript/TypeScript
import { soliditySha3 } from 'web3-utils';
// Sign address
const { signature, v, r, s } = signAddress(user2);
// This is an address is a hexadecimal format
const ourData = user2.toLowerCase();
// https://web3js.readthedocs.io/en/v1.2.0/web3-utils.html#id23
// Convert address to bytes using "tight packing"
// and them calculates keccak-256 over the resulting bytes
const ourHash = soliditySha3({t: 'address', v: user2 });
// We hash data in similar in TypeScript and Solidity
const { hash, data } = await tokenSwap.calculateAddressHash(user2);
assert(ourData.toLowerCase() == data.toLowerCase());
assert(ourHash.toLowerCase() == hash.toLowerCase());