packages/contracts-core/script/DeployMessaging003Base.s.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
// ═════════════════════════════ CONTRACT IMPORTS ══════════════════════════════
import {AgentSecured, MessagingBase} from "../contracts/base/AgentSecured.sol";
import {BondingManager} from "../contracts/manager/BondingManager.sol";
import {Destination} from "../contracts/Destination.sol";
import {GasOracle} from "../contracts/GasOracle.sol";
import {Origin} from "../contracts/Origin.sol";
import {Summit} from "../contracts/Summit.sol";
// ═════════════════════════════ INTERNAL IMPORTS ══════════════════════════════
import {DeployerUtils} from "./utils/DeployerUtils.sol";
// ═════════════════════════════ EXTERNAL IMPORTS ══════════════════════════════
import {console, stdJson} from "forge-std/Script.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
// solhint-disable no-console
// solhint-disable ordering
abstract contract DeployMessaging003BaseScript is DeployerUtils {
using stdJson for string;
using Strings for uint256;
string public constant DESTINATION = "Destination";
string public constant GAS_ORACLE = "GasOracle";
string public constant ORIGIN = "Origin";
string public constant SUMMIT = "Summit";
/// @notice BondingManager or LightManager address
address public agentManager;
/// @notice Inbox or LightInbox address
address public statementInbox;
// Common deployments for all chains
address public destination;
address public gasOracle;
address public origin;
// Only present on SynChain
address public summit;
uint32 public localDomain;
uint256 public synapseDomain;
string public globalConfig;
constructor() {
setupPK("MESSAGING_DEPLOYER_PRIVATE_KEY");
localDomain = uint32(block.chainid);
deploymentSalt = keccak256("Messaging003");
setupDevnetIfEnabled();
}
/// @dev Function to exclude script from coverage report
function testScript() external {}
/// @notice Main function with the deploy logic.
/// @dev To deploy contracts on $chainName
/// Make sure "./script/configs/Messaging003.dc.json" exists, then call
/// forge script script/DeployMessaging003<?>.s.sol -f chainName --ffi --broadcast
/// Note: Verification will not natively work, need to verify externally
function run() external {
_deploy(true);
}
/// @notice Function to simulate the deployment procedure.
/// @dev To simulate deployment on $chainName
/// forge script script/DeployMessaging003<?>.s.sol -f chainName --sig "runDry()"
function runDry() external {
_deploy(false);
}
function isSynapseChain() public returns (bool) {
return globalConfig.readUint(".chainidSummit") == localDomain;
}
function agentManagerName() public returns (string memory) {
return isSynapseChain() ? "BondingManager" : "LightManager";
}
function statementInboxName() public returns (string memory) {
return isSynapseChain() ? "Inbox" : "LightInbox";
}
/// @dev Deploys Messaging contracts, transfer ownership and sanity check the new deployments.
/// Will save the deployments, if script is being broadcasted.
function _deploy(bool _isBroadcasted) internal {
globalConfig = loadGlobalDeployConfig("Messaging003");
synapseDomain = globalConfig.readUint(".chainidSummit");
startBroadcast(_isBroadcasted);
// assert this is the first thing deployed
getFactory();
// Predict deployments
agentManager = predictFactoryDeployment(agentManagerName());
statementInbox = predictFactoryDeployment(statementInboxName());
destination = predictFactoryDeployment(DESTINATION);
gasOracle = predictFactoryDeployment(GAS_ORACLE);
origin = predictFactoryDeployment(ORIGIN);
if (isSynapseChain()) {
summit = predictFactoryDeployment(SUMMIT);
}
// Deploy and initialize contracts
_deployAndCheckAddress(agentManagerName(), _deployAgentManager, _initializeAgentManager, agentManager);
_deployAndCheckAddress(statementInboxName(), _deployStatementInbox, _initializeStatementInbox, statementInbox);
_deployAndCheckAddress(DESTINATION, _deployDestination, _initializeDestination, destination);
_deployAndCheckAddress(GAS_ORACLE, _deployGasOracle, _initializeGasOracle, gasOracle);
_deployAndCheckAddress(ORIGIN, _deployOrigin, _initializeOrigin, origin);
if (isSynapseChain()) {
_deployAndCheckAddress(SUMMIT, _deploySummit, _initializeSummit, summit);
}
// Add agents to BondingManager
_addAgents();
// Transfer ownership of contracts
_transferOwnership();
stopBroadcast();
// Do some sanity checks
_checkDeployments();
_checkAgents();
}
function _deployAndCheckAddress(
string memory contractName,
function() internal returns (address, bytes memory) deployFunc,
function(address) internal initFunc,
address predictedDeployment
) internal {
(address deployment,) = deployContract(contractName, deployFunc, initFunc);
require(deployment == predictedDeployment, string.concat(contractName, ": wrong address"));
}
/// @dev Deploys BondingManager or LightManager
/// Note: requires Origin, Destination, StatementInbox (and Summit for BondingManager) addresses to be set
function _deployAgentManager() internal virtual returns (address deployment, bytes memory constructorArgs);
/// @dev Initializes BondingManager or LightManager
function _initializeAgentManager(address deployment) internal virtual;
/// @dev Deploys Inbox or LightInbox
/// Note: requires AgentManager, Origin, Destination (and Summit for Inbox) addresses to be set
function _deployStatementInbox() internal virtual returns (address deployment, bytes memory constructorArgs);
/// @dev Initializes Inbox or LightInbox
function _initializeStatementInbox(address deployment) internal virtual;
/// @dev Adds agents to BondingManager (no-op for LightManager)
function _addAgents() internal virtual;
// ═══════════════════════════════════════ DEPLOY AND INITIALIZE ROUTINE ═══════════════════════════════════════════
/// @dev Deploys Destination.
/// Note: requires AgentManager and StatementInbox addresses to have been set.
/// Note: requires AgentManager to have been deployed.
function _deployDestination() internal returns (address deployment, bytes memory constructorArgs) {
// new Destination(domain, agentManager, statementInbox)
require(agentManager != address(0), "Agent Manager not set");
require(statementInbox != address(0), "Statement Inbox not set");
require(agentManager.code.length > 0, "Agent Manager not deployed");
constructorArgs = abi.encode(synapseDomain, agentManager, statementInbox);
deployment = factoryDeploy(DESTINATION, type(Destination).creationCode, constructorArgs);
}
/// @dev Initializes Destination.
function _initializeDestination(address deployment) internal {
if (Destination(deployment).owner() == address(0)) {
console.log(" %s: initializing", DESTINATION);
Destination(deployment).initialize(_getInitialAgentRoot());
} else {
console.log(" %s: already initialized", DESTINATION);
}
}
/// @dev Deploys GasOracle.
/// Note: requires Destination address to have been set.
function _deployGasOracle() internal returns (address deployment, bytes memory constructorArgs) {
// new GasOracle(domain, destination)
require(destination != address(0), "Destination not set");
constructorArgs = abi.encode(synapseDomain, destination);
deployment = factoryDeploy(GAS_ORACLE, type(GasOracle).creationCode, constructorArgs);
}
/// @dev Initializes GasOracle.
function _initializeGasOracle(address deployment) internal {
if (GasOracle(deployment).owner() == address(0)) {
console.log(" %s: initializing", GAS_ORACLE);
GasOracle(deployment).initialize();
} else {
console.log(" %s: already initialized", GAS_ORACLE);
}
}
/// @dev Deploys Origin.
/// Note: requires AgentManager, StatementInbox and GasOracle addresses to have been set.
function _deployOrigin() internal returns (address deployment, bytes memory constructorArgs) {
// new Origin(domain, agentManager, statementInbox, gasOracle)
require(agentManager != address(0), "Agent Manager not set");
require(statementInbox != address(0), "Statement Inbox not set");
require(gasOracle != address(0), "Gas Oracle not set");
constructorArgs = abi.encode(synapseDomain, agentManager, statementInbox, gasOracle);
deployment = factoryDeploy(ORIGIN, type(Origin).creationCode, constructorArgs);
}
/// @dev Initializes Origin.
function _initializeOrigin(address deployment) internal {
if (Origin(deployment).owner() == address(0)) {
console.log(" %s: initializing", ORIGIN);
Origin(deployment).initialize();
} else {
console.log(" %s: already initialized", ORIGIN);
}
}
/// @dev Deploys Summit.
/// Note: requires AgentManager and StatementInbox addresses to have been set.
function _deploySummit() internal returns (address deployment, bytes memory constructorArgs) {
// new Summit(domain, agentManager, statementInbox)
require(agentManager != address(0), "Agent Manager not set");
require(statementInbox != address(0), "Statement Inbox not set");
constructorArgs = abi.encode(synapseDomain, agentManager, statementInbox);
deployment = factoryDeploy(SUMMIT, type(Summit).creationCode, constructorArgs);
}
/// @dev Initializes Summit.
function _initializeSummit(address deployment) internal {
if (Summit(deployment).owner() == address(0)) {
console.log(" %s: initializing", SUMMIT);
Summit(deployment).initialize();
} else {
console.log(" %s: already initialized", SUMMIT);
}
}
// ═════════════════════════════════════════════════ OWNERSHIP ═════════════════════════════════════════════════════
/// @dev Transfers ownership of contracts to the owner defined in the global config.
function _transferOwnership() internal {
address owner = globalConfig.readAddress(".owner");
console.log("Transferring ownership to %s", owner);
_transferOwnership(agentManagerName(), agentManager, owner);
_transferOwnership(statementInboxName(), statementInbox, owner);
_transferOwnership(DESTINATION, destination, owner);
_transferOwnership(GAS_ORACLE, gasOracle, owner);
_transferOwnership(ORIGIN, origin, owner);
_transferOwnership(SUMMIT, summit, owner);
}
/// @dev Transfers ownership of a given contract to a new owner.
function _transferOwnership(string memory name, address deployment, address newOwner) internal {
if (deployment == address(0)) {
console.log(" %s: skipped (not deployed)", name);
return;
}
if (Ownable(deployment).owner() == newOwner) {
console.log(" %s: skipped (already owned)", name);
return;
}
console.log(" %s: transferring ownership to %s", name, newOwner);
Ownable(deployment).transferOwnership(newOwner);
}
// ══════════════════════════════════════════ POST DEPLOYMENT CHECKS ═══════════════════════════════════════════════
/// @dev Checks that all agents have been added correctly to BondingManager
/// or that they could be added to LightManager.
function _checkAgents() internal virtual;
/// @dev Checks that all contracts have been deployed and initialized correctly
function _checkDeployments() internal {
console.log("Checking deployments");
// Check AgentSecured contracts
console.log(" Checking AgentSecured contracts");
_checkAgentSecured(DESTINATION, destination);
_checkAgentSecured(ORIGIN, origin);
if (isSynapseChain()) _checkAgentSecured(SUMMIT, summit);
// Check MessagingBase contracts
console.log(" Checking MessagingBase contracts");
_checkMessagingBase(agentManagerName(), agentManager);
_checkMessagingBase(statementInboxName(), statementInbox);
_checkMessagingBase(GAS_ORACLE, gasOracle);
// Check MessagingBase having .agentManager() getter
console.log(" Checking contracts having .agentManager() getter");
_checkGetterAgentManager(statementInboxName(), statementInbox);
// Check MessagingBase having .inbox() getter
console.log(" Checking contracts having .inbox() getter");
_checkGetterInbox(agentManagerName(), agentManager);
// Check contracts having .destination() getter
console.log(" Checking contracts having .destination() getter");
_checkGetterDestination(agentManagerName(), agentManager);
_checkGetterDestination(statementInboxName(), statementInbox);
_checkGetterDestination(GAS_ORACLE, gasOracle);
// Check contracts having .gasOracle() getter
console.log(" Checking contracts having .gasOracle() getter");
_checkGetterGasOracle(ORIGIN, origin);
// Check contracts having .origin() getter
console.log(" Checking contracts having .origin() getter");
_checkGetterOrigin(agentManagerName(), agentManager);
_checkGetterOrigin(statementInboxName(), statementInbox);
// Check contracts having .summit() getter
if (isSynapseChain()) {
console.log(" Checking contracts having .summit() getter");
_checkGetterSummit(agentManagerName(), agentManager);
_checkGetterSummit(statementInboxName(), statementInbox);
}
}
/// @dev Checks AgentSecured getters:
/// - .localDomain()
/// - .owner()
/// - .agentManager()
/// - .inbox()
function _checkAgentSecured(string memory name, address deployment) internal {
_checkGetterAgentManager(name, deployment);
_checkGetterInbox(name, deployment);
_checkMessagingBase(name, deployment);
}
/// @dev Checks MessagingBase getters:
/// - .localDomain()
/// - .owner()
function _checkMessagingBase(string memory name, address deployment) internal {
_logCheckedGetter(name, "localDomain", deployment, localDomain);
require(MessagingBase(deployment).localDomain() == localDomain, string.concat("Wrong localDomain: ", name));
address owner = globalConfig.readAddress(".owner");
_logCheckedGetter(name, "owner", deployment, owner);
require(Ownable(deployment).owner() == owner, string.concat("Wrong owner: ", name));
}
/// @dev Checks .agentManager() getter
function _checkGetterAgentManager(string memory name, address deployment) internal view {
_logCheckedGetter(name, "agentManager", deployment, agentManager);
require(AgentSecured(deployment).agentManager() == agentManager, string.concat("Wrong agentManager: ", name));
}
/// @dev Checks .inbox() getter
function _checkGetterInbox(string memory name, address deployment) internal view {
_logCheckedGetter(name, "inbox", deployment, statementInbox);
require(AgentSecured(deployment).inbox() == statementInbox, string.concat("Wrong statementInbox: ", name));
}
/// @dev Checks .destination() getter
function _checkGetterDestination(string memory name, address deployment) internal view {
_logCheckedGetter(name, "destination", deployment, destination);
require(GasOracle(deployment).destination() == destination, string.concat("Wrong destination: ", name));
}
/// @dev Checks .gasOracle() getter
function _checkGetterGasOracle(string memory name, address deployment) internal view {
_logCheckedGetter(name, "gasOracle", deployment, gasOracle);
require(Origin(deployment).gasOracle() == gasOracle, string.concat("Wrong gasOracle: ", name));
}
/// @dev Checks .origin() getter
function _checkGetterOrigin(string memory name, address deployment) internal view {
_logCheckedGetter(name, "origin", deployment, origin);
require(BondingManager(deployment).origin() == origin, string.concat("Wrong origin: ", name));
}
/// @dev Checks .summit() getter
function _checkGetterSummit(string memory name, address deployment) internal view {
_logCheckedGetter(name, "summit", deployment, summit);
require(BondingManager(deployment).summit() == summit, string.concat("Wrong summit: ", name));
}
/// @dev Shortcut for logging
function _logCheckedGetter(string memory name, string memory getter, address deployment, address expectedValue)
internal
view
{
console.log(" [%s][%s]: checking .%s()", name, deployment, getter);
console.log(" expecting value: %s", expectedValue);
}
/// @dev Shortcut for logging
function _logCheckedGetter(string memory name, string memory getter, address deployment, uint32 expectedValue)
internal
view
{
console.log(" [%s][%s]: checking .%s()", name, deployment, getter);
console.log(" expecting value: %s", expectedValue);
}
// ══════════════════════════════════════════════════ HELPERS ══════════════════════════════════════════════════════
/// @dev Reads initial agent root
function _getInitialAgentRoot() internal returns (bytes32) {
// No saved agent root exists when deploying to SynChain
if (isSynapseChain()) return 0;
string memory agentRootConfig = loadGlobalDeployConfig("Messaging003AgentRoot");
return agentRootConfig.readBytes32(".initialAgentRoot");
}
}