// SPDX-License-Identifier: MIT pragma solidity >=0.6.12; pragma experimental ABIEncoderV2; import {StringUtils} from "../libs/StringUtils.sol"; import {ChainAwareReader} from "../reader/ChainAwareReader.sol"; import {ChainAwareWriter} from "../writer/ChainAwareWriter.sol"; import {stdJson} from "forge-std/Script.sol"; abstract contract DeploymentSaver is ChainAwareReader, ChainAwareWriter { using stdJson for string; using StringUtils for *; function checkBeforeDeploying(string memory contractName) internal view returns (address deployedAt) { return checkBeforeDeploying(contractName, contractName); } function checkBeforeDeploying( string memory contractName, string memory contractAlias ) internal view returns (address deployedAt) { printLog( contractName.equals(contractAlias) ? StringUtils.concat("Deploying: ", contractName) : StringUtils.concat("Deploying: ", contractName, " as ", contractAlias) ); // Check if the contract is already deployed deployedAt = getDeploymentAddress({contractName: contractAlias, revertIfNotFound: false}); if (deployedAt != address(0)) { printSkipWithIndent(StringUtils.concat("already deployed at ", vm.toString(deployedAt))); } } /// @notice Deploys a contract and saves the deployment JSON, if the contract hasn't been deployed yet. /// See `deployAndSaveAs` below for more details. function deployAndSave( string memory contractName, bytes memory constructorArgs, function(string memory, bytes memory) internal returns (address) deployCodeFunc ) internal returns (address deployedAt) { // Use contractName as contractAlias by default return deployAndSaveAs(contractName, contractName, constructorArgs, deployCodeFunc); } /// @notice Deploys a contract and saves the deployment JSON, if the contract hasn't been deployed yet. /// Needs to be passed a generic function that deploys the contract, that follows this signature: /// deployCodeFunc(string memory contractName, bytes memory constructorArgs) internal returns (address deployedAt); /// - Example: cbDeploy() and cbDeployCreate2() could be used here, as they follow the signature. Or anything /// else that could deploy raw bytecode of any contract, most likely using assembly/factory approach. /// - Note: contract should be configured outside of `deployCodeFunc`. function deployAndSaveAs( string memory contractName, string memory contractAlias, bytes memory constructorArgs, function(string memory, bytes memory) internal returns (address) deployCodeFunc ) internal returns (address deployedAt) { deployedAt = checkBeforeDeploying(contractName, contractAlias); if (deployedAt != address(0)) { return deployedAt; } // Trigger callback to deploy the contract deployedAt = deployCodeFunc(contractName, constructorArgs); printSuccessWithIndent(StringUtils.concat("Deployed at ", vm.toString(deployedAt))); // Save the deployment JSON saveDeployment(contractAlias, deployedAt, constructorArgs); } /// @notice Deploys a contract and saves the deployment JSON, if the contract hasn't been deployed yet. /// See `deployAndSaveAs` below for more details. function deployAndSave( string memory contractName, function() internal returns (address, bytes memory) deployContractFunc ) internal returns (address deployedAt) { // Use contractName as contractAlias by default return deployAndSaveAs(contractName, contractName, deployContractFunc); } /// @notice Deploys a contract and saves the deployment JSON, if the contract hasn't been deployed yet. /// Needs to be passed a contract-specific function that deploys the contract, that follows this signature: /// deployContractFunc() internal returns (address deployedAt, bytes memory constructorArgs); /// - Example: use anything that deploys a specific contract, most likely using `new Contract(...);` approach. /// - Note: contract should be configured outside of `deployContractFunc`. function deployAndSaveAs( string memory contractName, string memory contractAlias, function() internal returns (address, bytes memory) deployContractFunc ) internal returns (address deployedAt) { deployedAt = checkBeforeDeploying(contractName, contractAlias); if (deployedAt != address(0)) { return deployedAt; } // Trigger callback to deploy the specific contract bytes memory constructorArgs; (deployedAt, constructorArgs) = deployContractFunc(); printSuccessWithIndent(StringUtils.concat("Deployed at ", vm.toString(deployedAt))); // Save the deployment JSON saveDeployment(contractAlias, deployedAt, constructorArgs); } // ═══════════════════════════════════════════════════ UTILS ═══════════════════════════════════════════════════════ /// @notice Produces a JSON string that can be used to save a contract deployment. /// Note: contract ABI is not included in the output. function serializeDeploymentData( address deployedAt, bytes memory constructorArgs ) internal returns (string memory data) { data = "deployment"; data.serialize("address", deployedAt); if (constructorArgs.length == 0) { return data.serialize("constructorArgs", string("0x")); } else { return data.serialize("constructorArgs", constructorArgs); } } /// @notice Saves the deployment JSON for a contract on a given chain under the specified alias. /// Example: contractName = "LinkedPool", contractAlias = "LinkedPool.USDC" /// Note: writes the JSON file to the FRESH deployments directory. The written file needs to be moved /// to the correct location outside of the deployment script. /// Note: will not include the ABI in the output JSON. function saveDeployment( string memory contractAlias, address deployedAt, bytes memory constructorArgs ) internal withIndent { writeDeploymentArtifact({ contractAlias: contractAlias, artifact: serializeDeploymentData(deployedAt, constructorArgs) }); } }