Skip to content

Add Superchain interop message passing #595

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 58 commits into
base: master
Choose a base branch
from

Conversation

ericglau
Copy link
Member

@ericglau ericglau commented Jul 4, 2025

In Solidity's Custom tab, this PR adds a Cross-Chain Messaging option that adds an example of Superchain interop message passing when enabled. This includes a function that can be called from a different network on the Superchain.

For example, when enabled, this adds the following functions by default:

  • callMyFunction which takes a chain ID argument, and sends a message to myFunction at a contract at the same address on the given chain ID
  • myFunction which receives messages from other chains. The name of this function is customizable (which affects the name of the above function as well).

Implementation checklist:

  • Add UI elements to emphasize that this is only compatible with chains on the Superchain and requires deterministic deployments
  • Add unit tests
  • Add snapshot tests
  • Update AI assistant definitions and MCP schemas
  • Test/fix usage with AI assistant
  • Add Optimism contracts for compilation tests
  • Add Optimism contracts to Download Hardhat and Foundry packages
  • Test an example contract locally using supersim, non-upgradeable (see comments below for test steps)
  • Test an example contract locally using supersim, upgradeable (see comments below for test steps)
  • Add changesets
  • Add soldeer.lock

@ericglau ericglau requested a review from a team July 7, 2025 03:53
@ericglau ericglau requested a review from gonzaotc July 21, 2025 16:32
@ericglau
Copy link
Member Author

ericglau commented Jul 31, 2025

Also manually tested as follows. Note these are testcases only and do not represent best practices for actual deployments.

  • Test an example contract locally using supersim, non-upgradeable (Custom, only Cross-Chain Messaging enabled)
    • Test steps:
      1. After generating the contract, download as Foundry project, run setup script, then update myFunction to emit an event e.g. emit ReceivedCrossChainMessage(messenger.crossDomainMessageSender());
      2. Follow these steps except using MyContract instead of Greeter, and deploying to the same address on chains A and B without constructor parameters.
      3. After calling the crosschain function, use cast logs to look for the event defined in step 1 on chain B.
  • Test an example contract locally using supersim, upgradeable (Custom, Cross-Chain Messaging with Ownable and UUPS)
    • Test steps:
      1. After generating the contract, download as Hardhat project, run setup script, then update myFunction to emit an event, and update deploy.ts to set the owner address to the first address from supersim.
      2. Define supersim networks in Hardhat config, for example:
      3. Deploy to both chains:
        • npx hardhat run scripts/deploy.ts --network OPChainA
        • npx hardhat run scripts/deploy.ts --network OPChainB
      4. Create script callcrosschain.ts:
import { ethers } from "hardhat";
import { MyContract } from "../typechain-types";

async function main() {
  const ContractFactory = await ethers.getContractFactory("MyContract");
  const proxy = "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512"; // use address from step above
  const instance = await ContractFactory.attach(proxy) as MyContract;
  await instance.callMyFunction(902);
}

// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

e. Call the crosschain function using script: npx hardhat run scripts/callcrosschain.ts --network OPChainA
f. After calling the crosschain function, use cast logs to look for the event defined in step 1 on chain B.

@ericglau ericglau marked this pull request as ready for review August 2, 2025 03:45
@ericglau ericglau requested a review from a team as a code owner August 2, 2025 03:45
@ericglau
Copy link
Member Author

ericglau commented Aug 2, 2025

@SocketSecurity ignore-all
Not related

@ericglau ericglau requested a review from gonzaotc August 2, 2025 03:52
Copy link
Contributor

@gonzaotc gonzaotc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!. Tested in supersim and succeeded. Just added one non-blocking comment, great work Eric!.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious, why are we using Soldeer for Optimism contracts but Foundry git submodules for the OpenZeppelin dependencies on the Foundry zip downloads?

Wouldn't it be easier and cleaner for developers to get a single package managing method instead of two?, or there is a hard requirement that forces us to use both?

Copy link
Member Author

@ericglau ericglau Aug 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Optimism contracts, we have 4 options:

  1. Not import them, and just hardcode the messenger contract's predeploy address and interface's function calls.
  2. Use git submodules with forge install ethereum-optimism/optimism
  3. Use NPM package @eth-optimism/contracts-bedrock
  4. Use Soldeer package https://soldeer.xyz/project/@eth-optimism-contracts-bedrock which is a Soldeer-maintained mirror of the above NPM package.

For Foundry with Optimism contracts, here are the drawbacks of each option:

  1. This works, but it seems better practice to import the address and interface since they are available.
  2. This requires downloading the entire repo which is over 500 MB and seems excessive for what we need.
  3. This requires NPM to be installed, and is not Foundry native.
  4. Depends on Soldeer and its repository, but this is natively supported in Foundry.

Option 4 seems the most appropriate in this case.

For OpenZeppelin Contracts dependencies, current recommendations in https://github.com/OpenZeppelin/openzeppelin-contracts are to use forge install. We can consider being consistent in Wizard and use Soldeer for this as well, but we should review the recommended patterns when doing so, and this should occur in a separate PR.

Copy link
Contributor

@CoveMB CoveMB left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work 🔥
Great test coverage
I am wondering in the Custom contract it seem Optimism option add both emitter and receiver functions is it standar or does contract usually implement on or the other?

@@ -176,6 +177,11 @@ const script = (c: Contract, opts?: GenericOptions) => {
}
};

const OPTIMISM_NPM_PACKAGE = '@eth-optimism/contracts-bedrock';
const OPTIMISM_SOLDEER_PACKAGE = '@eth-optimism-contracts-bedrock';
const importsOptimism = (c: Contract) => c.imports.some(i => i.path.startsWith(OPTIMISM_NPM_PACKAGE));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

super nit: maybe "shouldImportOptimism" as it result in a boolean?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"imports" can be interpreted as a boolean in this case, which seems clear enough and is shorter

@ericglau
Copy link
Member Author

ericglau commented Aug 6, 2025

@CoveMB

I am wondering in the Custom contract it seem Optimism option add both emitter and receiver functions is it standar or does contract usually implement on or the other?

In this example, we include both functions. If only one function is included, the emitter and/or receiver would need to take or track other addresses, which would be a more complex example. That can be considered as an enhancement if there is a need for it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants