# Superfluid && Mainnet Fork

By [omnifient](https://paragraph.com/@omnifient) · 2022-11-08

---

Are you building a dApp on top of [Superfluid.finance](https://superfluid.finance/)? And now you want to test it with a [Mainnet Fork](https://docs.alchemy.com/docs/how-to-fork-ethereum-mainnet) instead of your local chain or testnets? But you don’t know how to do it?

GM, you’ve landed at the right place.

### Deploying to a Mainnet

Before [deploying your Superfluid smart contract (SuperApp) to a Mainnet, you need to be whitelisted](https://github.com/superfluid-finance/protocol-monorepo/wiki/Super-App-White-listing-Guide), AKA Superfluid needs to allow you to register your SuperApp with them.

There are 2 options; which one you select depends on the architecture of your system. **In this article I’ll explain how to use** **Option 1 in Mainnet Fork, i.e. registering a SuperApp using a registration key**. \[Click here to learn how to use Option 2\]

Basically, you request a registration key from the Superfluid team (through a GitHub issue). That key is for a specific chain, associated with a EOA (the deployer account), and has an expiration date. Once that key is created, you can use your account to deploy SuperApps to that chain.

### Deploying to a Mainnet Fork

Most likely you won’t have a registration key yet, when deploying to a Mainnet Fork. The workaround is to take advantage of the fact that you have forked the chain, and that you can impersonate and change the state as you like.

Ok, but what exact state do we need to change, how do we do it, and where do we start?

**The SuperApp Registration Process** First, let’s understand how the registration process of a SuperApp works.

1.  The registration is done through the `ISuperfluid` Host contract’s `registerAppWithKey` [\[code\]](https://github.com/superfluid-finance/protocol-monorepo/blob/dev/packages/ethereum-contracts/contracts/superfluid/Superfluid.sol#L319), which then calls `_gov.getConfigAsUint256(...)`.
    
2.  The `_gov` variable is of type `ISuperfluidGovernance`, and `getConfigAsUint256` [\[code\]](https://github.com/superfluid-finance/protocol-monorepo/blob/dev/packages/ethereum-contracts/contracts/gov/SuperfluidGovernanceBase.sol#L212) simply checks if the arguments are present in the Governance’s internal `_configs` mapping.
    

Was this confusing? Maybe this diagram can clarify things:

![The SuperApp Registration Floooow](https://storage.googleapis.com/papyrus_images/fa1da2a02034f57ac40be6d1b07fb73e5d76edea1b90623161aea5de11b255bd.png)

The SuperApp Registration Floooow

**Setting a Registration Key in a Mainnet Fork** Ok, now that we know how things work, it should be easy to understand what we need to do: we _simply_ have to set a valid value into the `SuperfluidGovernance`’s `_config` mapping!

By inspecting `SuperfluidGovernanceBase` code, we can see some interesting functions: `setConfig` [\[code\]](https://github.com/superfluid-finance/protocol-monorepo/blob/dev/packages/ethereum-contracts/contracts/gov/SuperfluidGovernanceBase.sol#L126), `setAppRegistrationKey` [\[code\]](https://github.com/superfluid-finance/protocol-monorepo/blob/dev/packages/ethereum-contracts/contracts/gov/SuperfluidGovernanceBase.sol#L457), and some others.

What value to pass to `setConfig`? If you paid attention, `ISuperfluid.registerAppWithKey` actually does a transformation before calling `ISuperfluidGovernance.getConfigAsUint256`. That transformation is a call to `SuperfluidGovernanceConfigs.getAppRegistrationConfigKey` [\[code\]](https://github.com/superfluid-finance/protocol-monorepo/blob/f80a180ffe55dc4951e9553637a6aa60803d25a8/packages/ethereum-contracts/contracts/interfaces/superfluid/Definitions.sol#L212) which simply does a hash of the deployer’s address and the registration key.

Cool, but how do we get access to the Governance contract? Well, the Host contract has a `getGovernance` function! [\[code\]](https://github.com/superfluid-finance/protocol-monorepo/blob/dev/packages/ethereum-contracts/contracts/superfluid/Superfluid.sol#L137)

**We have the main pieces now, let’s assemble things!**

*   [Grab the Host contract’s address](https://docs.superfluid.finance/superfluid/developers/networks) (e.g. `0x567c4B141ED61923967cA25Ef4906C8781069a10` for Optimism)
    
*   Define a function to get an instance of the Host contract
    

    const HOST_ABI = ["function getGovernance() external view returns (address)"];
    const HOST_ADDR = "0x567c4B141ED61923967cA25Ef4906C8781069a10";
    
    function getHost(hostAddr, providerOrSigner) {
      const hostInstance = new ethers.Contract(hostAddr, HOST_ABI, providerOrSigner);
      return hostInstance;
    }
    

*   Define a function to get an instance of the Governance contract
    

    const GOV_II_ABI = [
      "function setConfig(address host, address superToken, bytes32 key, uint256 value) external",
      "function setAppRegistrationKey(address host, address deployer, string memory registrationKey, uint256 expirationTs) external",
      "function getConfigAsUint256(address host, address superToken, bytes32 key) external view returns (uint256 value)",
      "function verifyAppRegistrationKey(address host, address deployer, string memory registrationKey) external view returns (bool validNow, uint256 expirationTs)",
      "function owner() public view returns (address)",
    ];
    
    function getGovernance(govAddr, providerOrSigner) {
      const govInstance = new ethers.Contract(
        govAddr,
        GOV_II_ABI,
        providerOrSigner
      );
    
      return govInstance;
    }
    

*   Define some helper functions
    

    const { network } = require("hardhat");
    const { hexValue } = require("@ethersproject/bytes");
    const { parseEther } = require("@ethersproject/units");
    
    async function impersonate(addr) {
      await network.provider.request({
        method: "hardhat_impersonateAccount",
        params: [addr],
      });
    
      await network.provider.send("hardhat_setBalance", [
        addr,
        hexValue(parseEther("1000000")),
      ]);
    
      return await ethers.getSigner(addr);
    }
    
    function getConfigKey(deployerAddr, registrationKey) { 
      return ethers.utils.keccak256(
        ethers.utils.defaultAbiCoder.encode(
          ["string", "address", "string"],
          [
            "org.superfluid-finance.superfluid.appWhiteListing.registrationKey",
            deployerAddr,
            registrationKey,
          ]
        )
      );
    }
    

*   Define the function to set the registration key, and another to verify it
    

    async function setRegistrationKey(
      hostAddr,
      govAddr,
      govOwnerAddr,
      deployerAddr
    ) {
      // impersonate the contract owner so we can modify things (also funds it with some balance)
      const govOwnerSigner = await impersonate(govOwnerAddr);
    
      // get the superfluid governance instance
      const govInstance = getGovernance(govAddr, govOwnerSigner);
    
      // generate a registration key, and pack it up
      const registrationKey = `GM-${Date.now()}`;
      const configKey = getConfigKey(deployerAddr, registrationKey);
      let tx = await govInstance.setConfig(
        hostAddr,
        "0x0000000000000000000000000000000000000000",
        configKey,
        Math.floor(Date.now() / 1000) + 3600 * 24 * 180 // 180 day expiration
      );
      await tx.wait();
    
      return registrationKey;
    }
    
    async function checkRegistrationKey(
      hostAddr,
      govAddr,
      govOwnerAddr,
      deployerAddr,
      registrationKey
    ) {
      const govOwnerSigner = await impersonate(govOwnerAddr);
      const govInstance = getGovernance(govAddr, govOwnerSigner);
    
      const configKey = getConfigKey(deployerAddr, registrationKey);
      let r = await govInstance.getConfigAsUint256(
        hostAddr,
        "0x0000000000000000000000000000000000000000",
        configKey
      );
      
      return r;
    }
    

*   Then just tie everything together in your main!
    

    async function main() {
      const signer = await hre.ethers.provider.getSigner();
      const signerAddr = await signer.getAddress();
    
      const hostInstance = getHost(HOST_ADDR, signer);
      const govAddr = await hostInstance.getGovernance();
      const govInstance = getGovernance(govAddr, signer);
      const govOwnerAddr = await govInstance.owner();
    
      const registrationKey = await setRegistrationKey(HOST_ADDR, govAddr, govOwnerAddr, signerAddr);
    
      const r = await checkRegistrationKey(HOST_ADDR, govAddr, govOwnerAddr, signerAddr, registrationKey);
    
      // THEN YOU DEPLOY YOUR CONTRACT WITH THE REGISTRATION KEY
    
      const factory = await hre.ethers.getContractFactory("MySuperApp");
      const mySuperApp = await factory.deploy(HOST_ADDR, registrationKey);
      await mySuperApp.deployed();
      console.log("MySuperApp contract deployed to", mySuperApp.address);
    }
    

And that’s it, you can now run the script against your Mainnet Fork chain (`npx hardhat run --network localhost scripts/deploy-my-super-app.js`), and you should be successful in deploying your SuperApp and registering it with Superfluid!

Here’s an example repo:

[https://github.com/omnifient/superfluid-mainnet-fork-example](https://github.com/omnifient/superfluid-mainnet-fork-example)

#WAGMI

---

*Originally published on [omnifient](https://paragraph.com/@omnifient/superfluid-mainnet-fork)*
