I am trying to create a function in python that can generate the pool address from the address of the tokens. The relevant portion of the solidity contracts is below:

contract SyncSwapClassicPool is IClassicPool, ERC20Permit2, ReentrancyGuard {
    constructor() {
        (bytes memory _deployData) = IPoolFactory(msg.sender).getDeployData();
        (address _token0, address _token1) = abi.decode(_deployData, (address, address));
        address _master = IPoolFactory(msg.sender).master();

        master = _master;
        vault = IPoolMaster(_master).vault();
        (token0, token1) = (_token0, _token1);

        // try to set symbols for the LP token
        (bool _success0, string memory _symbol0) = MetadataHelper.getSymbol(_token0);
        (bool _success1, string memory _symbol1) = MetadataHelper.getSymbol(_token1);
        if (_success0 && _success1) {
                string(abi.encodePacked("SyncSwap ", _symbol0, "/", _symbol1, " Classic LP")),
                string(abi.encodePacked(_symbol0, "/", _symbol1, " cSLP"))
        } else {
                "SyncSwap Classic LP",

contract SyncSwapClassicPoolFactory is BasePoolFactory {
    constructor(address _master) BasePoolFactory(_master) {

    function _createPool(address token0, address token1) internal override returns (address pool) {
        // Perform sanity checks.

        bytes memory deployData = abi.encode(token0, token1);
        cachedDeployData = deployData;

        // The salt is same with deployment data.
        bytes32 salt = keccak256(deployData);
        pool = address(new SyncSwapClassicPool{salt: salt}()); // this will prevent duplicated pools.

        // Register the pool. The config is same with deployment data.
        IPoolMaster(master).registerPool(pool, 1, deployData);

You can see the source code of the contracts on github.

As you can see, _createPool generates the pool address using the token addresses (deployData) and the salt is a keccak hash of the deploy data (using CREATE2 opcode, I think).

I tried the following in python to recreate this logic, but it returns me a different hash than if I query the getPool on the contract deployed to Scroll.

address1 = '0x5300000000000000000000000000000000000004' # weth
address2 = '0x06eFdBFf2a14a7c8E15944D1F4A48F9F95F663A4' # usdc
factory_address = '0x37BAc764494c8db4e54BDE72f6965beA9fa0AC2d' # Classic pool factory

deploy_data = HexBytes(address1) + HexBytes(address2)
salt = Web3.keccak(deploy_data)
pool_address = Web3.keccak(
    HexBytes('0xff') +
    HexBytes(factory_address) +
    salt +

This python code returns 0x8ac940d65cb35fb4e12ae97b7409f1b79374bcad while the getPool returns 0x814A23B053FD0f102AEEda0459215C2444799C70 which is the correct address.

Am I missing something here? I'm not sure how the hash of the address is computed, if the salt and deploy data need to be hashed or not before adding them together and hashing them.

1 Answer 1


You can use the get_create2_address() function of the utils module of web3.py by passing the corresponding 3 arguments as:

  1. address of the sender/deployer of SyncSwapClassicPool contract (i.e., SyncSwapClassicPoolFactory address in this case).
  2. salt.
  3. compiled bytecode of the SyncSwapClassicPool contract.

Here's the code snippet:

from web3 import utils

create2_address = utils.address.get_create2_address(
    "SENDER_ADDRESS",  # address of the factory contract in your case


P.S. v6.18.0 of web3.py (web3==6.18.0) is being used.

  • thanks for the answer, I tried it but I get a different address (not correct one either). I think I don't really understand what the bytecode should represent. From the SyncSwapClassicPoolFactory it is storing the bytecode in cachedDeployData which is then read by the getDeployData() inside the pool constructor. If I read the value in solidity it is something like this: 0x00000000000000000000000006efdbff2a14a7c8e15944d1f4a48f9f95f663a40000000000000000000000005300000000000000000000000000000000000004. Even with that value I get a different address.
    – Cristian
    Commented May 3 at 16:26
  • cachedDeployData is not the bytecode though. Can you try compiling your SyncSwapClassicPool contract on Remix IDE and then copy the bytecode from there, and use the same? Commented May 3 at 16:30
  • Ah. I see, I think that's my misunderstanding! Do I need the full bytecode of the whole contract? Or can I use a hashed version of it? I can try with the one from scrollscan: scrollscan.com/address/…
    – Cristian
    Commented May 3 at 16:39
  • Yeah, you might need to use the full bytecode. Because, I tested with the full compiled bytecode of dummy/empty contract like contract Test {}, and it returned the expected address. Commented May 3 at 17:22
  • @Cristian, let me know how it goes. Commented May 3 at 17:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.