Add Oracle Aggregator and CCIP Integration

- Introduced Aggregator.sol for Chainlink-compatible oracle functionality, including round-based updates and access control.
- Added OracleWithCCIP.sol to extend Aggregator with CCIP cross-chain messaging capabilities.
- Created .gitmodules to include OpenZeppelin contracts as a submodule.
- Developed a comprehensive deployment guide in NEXT_STEPS_COMPLETE_GUIDE.md for Phase 2 and smart contract deployment.
- Implemented Vite configuration for the orchestration portal, supporting both Vue and React frameworks.
- Added server-side logic for the Multi-Cloud Orchestration Portal, including API endpoints for environment management and monitoring.
- Created scripts for resource import and usage validation across non-US regions.
- Added tests for CCIP error handling and integration to ensure robust functionality.
- Included various new files and directories for the orchestration portal and deployment scripts.
This commit is contained in:
defiQUG
2025-12-12 14:57:48 -08:00
parent a1466e4005
commit 1fb7266469
1720 changed files with 241279 additions and 16 deletions

57
script/Deploy.s.sol Normal file
View File

@@ -0,0 +1,57 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Script, console} from "forge-std/Script.sol";
import {WETH} from "../contracts/tokens/WETH.sol";
import {Multicall} from "../contracts/utils/Multicall.sol";
import {CREATE2Factory} from "../contracts/utils/CREATE2Factory.sol";
import {Aggregator} from "../contracts/oracle/Aggregator.sol";
import {Proxy} from "../contracts/oracle/Proxy.sol";
contract Deploy is Script {
function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
address deployer = vm.addr(deployerPrivateKey);
console.log("Deploying contracts with address:", deployer);
console.log("Chain ID:", block.chainid);
vm.startBroadcast(deployerPrivateKey);
// Deploy WETH
WETH weth = new WETH();
console.log("WETH deployed at:", address(weth));
// Deploy Multicall
Multicall multicall = new Multicall();
console.log("Multicall deployed at:", address(multicall));
// Deploy CREATE2 Factory
CREATE2Factory create2Factory = new CREATE2Factory();
console.log("CREATE2Factory deployed at:", address(create2Factory));
// Deploy Oracle Aggregator
Aggregator aggregator = new Aggregator(
"ETH/USD Price Feed",
deployer,
60, // heartbeat: 60 seconds
50 // deviationThreshold: 0.5% (50 basis points)
);
console.log("Aggregator deployed at:", address(aggregator));
// Deploy Proxy for Aggregator
Proxy proxy = new Proxy(address(aggregator), deployer);
console.log("Proxy deployed at:", address(proxy));
vm.stopBroadcast();
// Log deployment addresses
console.log("\n=== Deployment Summary ===");
console.log("WETH:", address(weth));
console.log("Multicall:", address(multicall));
console.log("CREATE2Factory:", address(create2Factory));
console.log("Aggregator:", address(aggregator));
console.log("Proxy:", address(proxy));
}
}

View File

@@ -0,0 +1,52 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Script, console} from "forge-std/Script.sol";
import {AddressMapper} from "../contracts/utils/AddressMapper.sol";
/**
* @title DeployAddressMapper
* @notice Deploy the AddressMapper contract to provide address mapping
*/
contract DeployAddressMapper is Script {
function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
address deployer = vm.addr(deployerPrivateKey);
console.log("Deploying AddressMapper with deployer:", vm.toString(deployer));
vm.startBroadcast(deployerPrivateKey);
AddressMapper mapper = new AddressMapper();
console.log("AddressMapper deployed at:", vm.toString(address(mapper)));
// Verify mappings
address weth9Genesis = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address weth9Deployed = 0x3304b747E565a97ec8AC220b0B6A1f6ffDB837e6;
address weth10Genesis = 0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9F;
address weth10Deployed = 0x105F8A15b819948a89153505762444Ee9f324684;
address mappedWETH9 = mapper.getDeployedAddress(weth9Genesis);
address mappedWETH10 = mapper.getDeployedAddress(weth10Genesis);
console.log("\n=== Mapping Verification ===");
console.log("WETH9 Genesis:", vm.toString(weth9Genesis));
console.log("WETH9 Mapped:", vm.toString(mappedWETH9));
console.log("WETH9 Expected:", vm.toString(weth9Deployed));
require(mappedWETH9 == weth9Deployed, "WETH9 mapping incorrect");
console.log("WETH10 Genesis:", vm.toString(weth10Genesis));
console.log("WETH10 Mapped:", vm.toString(mappedWETH10));
console.log("WETH10 Expected:", vm.toString(weth10Deployed));
require(mappedWETH10 == weth10Deployed, "WETH10 mapping incorrect");
console.log("\nOK: All mappings verified!");
vm.stopBroadcast();
console.log("\n=== Deployment Summary ===");
console.log("AddressMapper:", vm.toString(address(mapper)));
console.log("Owner:", vm.toString(mapper.owner()));
}
}

355
script/DeployAll.s.sol Normal file
View File

@@ -0,0 +1,355 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Script, console} from "forge-std/Script.sol";
import {WETH} from "../contracts/tokens/WETH.sol";
import {WETH10} from "../contracts/tokens/WETH10.sol";
import {CCIPWETH9Bridge} from "../contracts/ccip/CCIPWETH9Bridge.sol";
import {CCIPWETH10Bridge} from "../contracts/ccip/CCIPWETH10Bridge.sol";
/**
* @title DeployAll - Canonical Multichain Deployment Script
* @notice Deploys all contracts (WETH9, WETH10, CCIP Bridges, CCIPLogger) to multiple chains
* @dev Chain-aware deployment that adapts to different network configurations
*
* Supported Chains:
* - Ethereum Mainnet (chainId 1): Only deploys CCIPLogger (other contracts already deployed)
* - Cronos (chainId 25): Deploys all contracts
* - BSC (chainId 56): Deploys all contracts
* - Polygon PoS (chainId 137): Deploys all contracts
* - Gnosis Chain (chainId 100): Deploys all contracts
* - Avalanche (chainId 43114): Deploys all contracts
* - Base (chainId 8453): Deploys all contracts
* - Arbitrum (chainId 42161): Deploys all contracts
* - Optimism (chainId 10): Deploys all contracts
*/
contract DeployAll is Script {
// Chain IDs
uint256 constant MAINNET = 1;
uint256 constant CRONOS = 25;
uint256 constant BSC = 56;
uint256 constant POLYGON = 137;
uint256 constant GNOSIS = 100;
uint256 constant AVALANCHE = 43114;
uint256 constant BASE = 8453;
uint256 constant ARBITRUM = 42161;
uint256 constant OPTIMISM = 10;
// CCIP Configuration per chain
struct CCIPConfig {
address router;
address linkToken;
uint64 chainSelector;
}
// WETH Configuration per chain
struct WETHConfig {
address weth9;
address weth10;
}
// Deployment addresses (will be populated during deployment)
struct DeploymentAddresses {
address weth9;
address weth10;
address ccipWETH9Bridge;
address ccipWETH10Bridge;
address ccipLogger;
}
DeploymentAddresses public deployed;
function run() external {
uint256 chainId = block.chainid;
// Get deployer address - when using --private-key flag, use msg.sender
// Otherwise get from env (PRIVATE_KEY should have 0x prefix in .env)
address deployer;
try vm.envUint("PRIVATE_KEY") returns (uint256 pk) {
deployer = vm.addr(pk);
} catch {
// If env read fails, use msg.sender (when --private-key is used from CLI)
deployer = msg.sender;
}
console.log("==========================================");
console.log("Multichain Deployment Script");
console.log("==========================================");
console.log("Chain ID:", chainId);
console.log("Deployer:", deployer);
console.log("Deployer Balance:", deployer.balance / 1e18, "ETH/Native");
// Get chain-specific configuration
CCIPConfig memory ccipConfig = getCCIPConfig(chainId);
WETHConfig memory wethConfig = getWETHConfig(chainId);
console.log("\nCCIP Configuration:");
console.log(" Router:", ccipConfig.router);
console.log(" LINK Token:", ccipConfig.linkToken);
console.log(" Chain Selector:", ccipConfig.chainSelector);
console.log("\nWETH Configuration:");
console.log(" WETH9:", wethConfig.weth9);
console.log(" WETH10:", wethConfig.weth10);
// startBroadcast() will use --private-key from CLI if provided
// Otherwise it will try to use PRIVATE_KEY from env
vm.startBroadcast();
// Determine deployment strategy based on chain
if (chainId == MAINNET) {
// Ethereum Mainnet: Only deploy CCIPLogger
console.log("\n=== Ethereum Mainnet Deployment ===");
console.log("Skipping WETH9, WETH10, and Bridges (already deployed)");
console.log("Deploying CCIPLogger only...");
deployed.ccipLogger = deployCCIPLogger(ccipConfig);
} else {
// All other chains: Deploy everything
console.log("\n=== Full Deployment for Chain", chainId, "===");
// Deploy WETH9 if needed
if (wethConfig.weth9 == address(0)) {
console.log("\nDeploying WETH9...");
deployed.weth9 = deployWETH9();
} else {
console.log("\nUsing existing WETH9 at:", wethConfig.weth9);
deployed.weth9 = wethConfig.weth9;
}
// Deploy WETH10 if needed
if (wethConfig.weth10 == address(0)) {
console.log("\nDeploying WETH10...");
deployed.weth10 = deployWETH10();
} else {
console.log("\nUsing existing WETH10 at:", wethConfig.weth10);
deployed.weth10 = wethConfig.weth10;
}
// Deploy CCIP Bridges
console.log("\nDeploying CCIPWETH9Bridge...");
deployed.ccipWETH9Bridge = deployCCIPWETH9Bridge(
ccipConfig.router,
deployed.weth9,
ccipConfig.linkToken
);
console.log("\nDeploying CCIPWETH10Bridge...");
deployed.ccipWETH10Bridge = deployCCIPWETH10Bridge(
ccipConfig.router,
deployed.weth10,
ccipConfig.linkToken
);
// Deploy CCIPLogger
console.log("\nDeploying CCIPLogger...");
deployed.ccipLogger = deployCCIPLogger(ccipConfig);
}
vm.stopBroadcast();
// Print deployment summary
printDeploymentSummary(chainId);
}
/**
* @notice Get CCIP configuration for a specific chain
*/
function getCCIPConfig(uint256 chainId) internal view returns (CCIPConfig memory) {
if (chainId == MAINNET) {
return CCIPConfig({
router: vm.envAddress("CCIP_ETH_ROUTER"),
linkToken: vm.envAddress("CCIP_ETH_LINK_TOKEN"),
chainSelector: uint64(vm.envUint("ETH_MAINNET_SELECTOR"))
});
} else if (chainId == CRONOS) {
return CCIPConfig({
router: vm.envAddress("CCIP_CRONOS_ROUTER"),
linkToken: vm.envAddress("CCIP_CRONOS_LINK_TOKEN"),
chainSelector: uint64(vm.envUint("CRONOS_SELECTOR"))
});
} else if (chainId == BSC) {
return CCIPConfig({
router: vm.envAddress("CCIP_BSC_ROUTER"),
linkToken: vm.envAddress("CCIP_BSC_LINK_TOKEN"),
chainSelector: uint64(vm.envUint("BSC_SELECTOR"))
});
} else if (chainId == POLYGON) {
return CCIPConfig({
router: vm.envAddress("CCIP_POLYGON_ROUTER"),
linkToken: vm.envAddress("CCIP_POLYGON_LINK_TOKEN"),
chainSelector: uint64(vm.envUint("POLYGON_SELECTOR"))
});
} else if (chainId == GNOSIS) {
return CCIPConfig({
router: vm.envAddress("CCIP_GNOSIS_ROUTER"),
linkToken: vm.envAddress("CCIP_GNOSIS_LINK_TOKEN"),
chainSelector: uint64(vm.envUint("GNOSIS_SELECTOR"))
});
} else if (chainId == AVALANCHE) {
return CCIPConfig({
router: vm.envAddress("CCIP_AVALANCHE_ROUTER"),
linkToken: vm.envAddress("CCIP_AVALANCHE_LINK_TOKEN"),
chainSelector: uint64(vm.envUint("AVALANCHE_SELECTOR"))
});
} else if (chainId == BASE) {
return CCIPConfig({
router: vm.envAddress("CCIP_BASE_ROUTER"),
linkToken: vm.envAddress("CCIP_BASE_LINK_TOKEN"),
chainSelector: uint64(vm.envUint("BASE_SELECTOR"))
});
} else if (chainId == ARBITRUM) {
return CCIPConfig({
router: vm.envAddress("CCIP_ARBITRUM_ROUTER"),
linkToken: vm.envAddress("CCIP_ARBITRUM_LINK_TOKEN"),
chainSelector: uint64(vm.envUint("ARBITRUM_SELECTOR"))
});
} else if (chainId == OPTIMISM) {
return CCIPConfig({
router: vm.envAddress("CCIP_OPTIMISM_ROUTER"),
linkToken: vm.envAddress("CCIP_OPTIMISM_LINK_TOKEN"),
chainSelector: uint64(vm.envUint("OPTIMISM_SELECTOR"))
});
} else {
revert("Unsupported chain");
}
}
/**
* @notice Get WETH configuration for a specific chain
*/
function getWETHConfig(uint256 chainId) internal returns (WETHConfig memory) {
if (chainId == MAINNET) {
return WETHConfig({
weth9: vm.envAddress("WETH9_MAINNET"),
weth10: vm.envAddress("WETH10_MAINNET")
});
} else if (chainId == CRONOS) {
address weth9 = vm.envOr("WETH9_CRONOS", address(0));
address weth10 = vm.envOr("WETH10_CRONOS", address(0));
return WETHConfig({weth9: weth9, weth10: weth10});
} else if (chainId == BSC) {
address weth9 = vm.envOr("WETH9_BSC", address(0));
address weth10 = vm.envOr("WETH10_BSC", address(0));
return WETHConfig({weth9: weth9, weth10: weth10});
} else if (chainId == POLYGON) {
address weth9 = vm.envOr("WETH9_POLYGON", address(0));
address weth10 = vm.envOr("WETH10_POLYGON", address(0));
return WETHConfig({weth9: weth9, weth10: weth10});
} else if (chainId == GNOSIS) {
address weth9 = vm.envOr("WETH9_GNOSIS", address(0));
address weth10 = vm.envOr("WETH10_GNOSIS", address(0));
return WETHConfig({weth9: weth9, weth10: weth10});
} else if (chainId == AVALANCHE) {
address weth9 = vm.envOr("WETH9_AVALANCHE", address(0));
address weth10 = vm.envOr("WETH10_AVALANCHE", address(0));
return WETHConfig({weth9: weth9, weth10: weth10});
} else if (chainId == BASE) {
address weth9 = vm.envOr("WETH9_BASE", address(0));
address weth10 = vm.envOr("WETH10_BASE", address(0));
return WETHConfig({weth9: weth9, weth10: weth10});
} else if (chainId == ARBITRUM) {
address weth9 = vm.envOr("WETH9_ARBITRUM", address(0));
address weth10 = vm.envOr("WETH10_ARBITRUM", address(0));
return WETHConfig({weth9: weth9, weth10: weth10});
} else if (chainId == OPTIMISM) {
address weth9 = vm.envOr("WETH9_OPTIMISM", address(0));
address weth10 = vm.envOr("WETH10_OPTIMISM", address(0));
return WETHConfig({weth9: weth9, weth10: weth10});
} else {
revert("Unsupported chain");
}
}
/**
* @notice Deploy WETH9 contract
*/
function deployWETH9() internal returns (address) {
WETH weth9 = new WETH();
return address(weth9);
}
/**
* @notice Deploy WETH10 contract
*/
function deployWETH10() internal returns (address) {
WETH10 weth10 = new WETH10();
return address(weth10);
}
/**
* @notice Deploy CCIPWETH9Bridge
*/
function deployCCIPWETH9Bridge(
address router,
address weth9,
address feeToken
) internal returns (address) {
CCIPWETH9Bridge bridge = new CCIPWETH9Bridge(router, weth9, feeToken);
return address(bridge);
}
/**
* @notice Deploy CCIPWETH10Bridge
*/
function deployCCIPWETH10Bridge(
address router,
address weth10,
address feeToken
) internal returns (address) {
CCIPWETH10Bridge bridge = new CCIPWETH10Bridge(router, weth10, feeToken);
return address(bridge);
}
/**
* @notice Deploy CCIPLogger
* @dev Note: This assumes CCIPLogger contract exists. If it uses Hardhat/OpenZeppelin,
* you may need to deploy it separately using the Hardhat script.
*/
function deployCCIPLogger(CCIPConfig memory config) internal returns (address) {
// TODO: If CCIPLogger is a Solidity contract in this repo, deploy it here
// For now, this is a placeholder. CCIPLogger may need to be deployed via Hardhat
// due to OpenZeppelin dependencies.
// Example deployment (adjust based on actual CCIPLogger contract):
// CCIPLogger logger = new CCIPLogger(
// config.router,
// vm.envOr("AUTHORIZED_SIGNER", address(0)),
// config.chainSelector
// );
// return address(logger);
console.log("WARNING: CCIPLogger deployment not implemented in Foundry script.");
console.log("Please deploy CCIPLogger using the Hardhat script:");
console.log(" npm run deploy:logger:mainnet (for mainnet)");
console.log(" Or create a chain-specific deployment script.");
return address(0); // Placeholder
}
/**
* @notice Print deployment summary
*/
function printDeploymentSummary(uint256 chainId) internal view {
console.log("\n==========================================");
console.log("Deployment Summary - Chain ID:", chainId);
console.log("==========================================");
if (chainId != MAINNET) {
console.log("WETH9:", deployed.weth9);
console.log("WETH10:", deployed.weth10);
console.log("CCIPWETH9Bridge:", deployed.ccipWETH9Bridge);
console.log("CCIPWETH10Bridge:", deployed.ccipWETH10Bridge);
}
console.log("CCIPLogger:", deployed.ccipLogger);
console.log("\n==========================================");
console.log("Next Steps:");
console.log("1. Verify contracts on explorer");
console.log("2. Update .env with deployed addresses");
console.log("3. Configure bridge destinations");
console.log("4. Test cross-chain transfers");
console.log("==========================================");
}
}

View File

@@ -0,0 +1,83 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Script, console} from "forge-std/Script";
/**
* @title DeployCCIPLoggerOnly - Deploy CCIPLogger to Ethereum Mainnet
* @notice This script deploys ONLY CCIPLogger to Ethereum Mainnet
* @dev All other contracts (WETH9, WETH10, Bridges) are already deployed on Mainnet
*
* NOTE: CCIPLogger may require Hardhat/OpenZeppelin dependencies.
* If this script fails due to missing dependencies, use the Hardhat script instead:
* npm run deploy:logger:mainnet
*/
contract DeployCCIPLoggerOnly is Script {
uint256 constant MAINNET = 1;
function run() external {
uint256 chainId = block.chainid;
require(chainId == MAINNET, "This script is only for Ethereum Mainnet");
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
address deployer = vm.addr(deployerPrivateKey);
console.log("==========================================");
console.log("CCIPLogger Deployment - Ethereum Mainnet");
console.log("==========================================");
console.log("Chain ID:", chainId);
console.log("Deployer:", deployer);
console.log("Deployer Balance:", deployer.balance / 1e18, "ETH");
// Get CCIP configuration
address router = vm.envAddress("CCIP_ETH_ROUTER");
address linkToken = vm.envAddress("CCIP_ETH_LINK_TOKEN");
uint64 chainSelector = uint64(vm.envUint("ETH_MAINNET_SELECTOR"));
address authorizedSigner = vm.envOr("AUTHORIZED_SIGNER", address(0));
console.log("\nCCIP Configuration:");
console.log(" Router:", router);
console.log(" LINK Token:", linkToken);
console.log(" Chain Selector:", chainSelector);
console.log(" Authorized Signer:", authorizedSigner);
// Get source chain selector (Chain-138)
uint64 sourceChainSelector = uint64(vm.envUint("CHAIN138_SELECTOR"));
console.log(" Source Chain Selector (Chain-138):", sourceChainSelector);
console.log("\nWARNING:");
console.log("CCIPLogger may require OpenZeppelin contracts v5.0.2+");
console.log("If deployment fails, use the Hardhat script instead:");
console.log(" npm install @openzeppelin/contracts@5.0.2");
console.log(" npm run deploy:logger:mainnet");
vm.startBroadcast(deployerPrivateKey);
// TODO: Uncomment and adjust when CCIPLogger contract is available in Foundry
// CCIPLogger logger = new CCIPLogger(
// router,
// authorizedSigner,
// sourceChainSelector
// );
// address loggerAddress = address(logger);
// For now, this is a placeholder
address loggerAddress = address(0);
console.log("\nPLACEHOLDER: CCIPLogger deployment not implemented");
console.log("Please use Hardhat script: npm run deploy:logger:mainnet");
vm.stopBroadcast();
if (loggerAddress != address(0)) {
console.log("\n==========================================");
console.log("OK: CCIPLogger Deployed Successfully");
console.log("==========================================");
console.log("Address:", loggerAddress);
console.log("\nNext Steps:");
console.log("1. Verify contract on Etherscan");
console.log("2. Update .env with CCIPLOGGER_MAINNET=", loggerAddress);
console.log("3. Configure CCIPTxReporter on Chain-138");
}
}
}

View File

@@ -0,0 +1,34 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Script, console} from "forge-std/Script.sol";
import {CCIPReceiver} from "../contracts/ccip/CCIPReceiver.sol";
/**
* @title DeployCCIPReceiver
* @notice Deploy CCIPReceiver contract for receiving cross-chain messages
*/
contract DeployCCIPReceiver is Script {
function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
address deployer = vm.addr(deployerPrivateKey);
// Load environment variables
address ccipRouter = vm.envAddress("CCIP_ROUTER_ADDRESS");
console.log("Deploying CCIPReceiver with deployer:", vm.toString(deployer));
console.log("CCIP Router:", vm.toString(ccipRouter));
vm.startBroadcast(deployerPrivateKey);
CCIPReceiver receiver = new CCIPReceiver(ccipRouter);
console.log("CCIPReceiver deployed at:", vm.toString(address(receiver)));
vm.stopBroadcast();
console.log("\n=== Deployment Summary ===");
console.log("CCIPReceiver:", vm.toString(address(receiver)));
console.log("CCIP Router:", vm.toString(ccipRouter));
}
}

View File

@@ -0,0 +1,36 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Script, console} from "forge-std/Script.sol";
import {CCIPRouter} from "../contracts/ccip/CCIPRouter.sol";
contract DeployCCIPRouter is Script {
function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
address deployer = vm.addr(deployerPrivateKey);
// Load from environment or use defaults
address feeToken = vm.envOr("CCIP_FEE_TOKEN", address(0));
uint256 baseFee = vm.envOr("CCIP_BASE_FEE", uint256(0));
uint256 dataFeePerByte = vm.envOr("CCIP_DATA_FEE_PER_BYTE", uint256(0));
console.log("Deploying CCIPRouter with deployer:", vm.toString(deployer));
console.log("Fee Token:", vm.toString(feeToken));
console.log("Base Fee:", baseFee);
console.log("Data Fee Per Byte:", dataFeePerByte);
vm.startBroadcast(deployerPrivateKey);
CCIPRouter router = new CCIPRouter(feeToken, baseFee, dataFeePerByte);
console.log("CCIP Router deployed at:", vm.toString(address(router)));
vm.stopBroadcast();
console.log("\n=== Deployment Summary ===");
console.log("CCIP Router:", vm.toString(address(router)));
console.log("Fee Token:", vm.toString(feeToken));
console.log("Base Fee:", baseFee);
console.log("Data Fee Per Byte:", dataFeePerByte);
}
}

View File

@@ -0,0 +1,34 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Script, console} from "forge-std/Script.sol";
import {CCIPSender} from "../contracts/ccip/CCIPSender.sol";
/**
* @title DeployCCIPSender
* @notice Deploy CCIPSender contract for cross-chain messaging
*/
contract DeployCCIPSender is Script {
function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
address deployer = vm.addr(deployerPrivateKey);
// Load environment variables
address ccipRouter = vm.envAddress("CCIP_ROUTER_ADDRESS");
console.log("Deploying CCIPSender with deployer:", vm.toString(deployer));
console.log("CCIP Router:", vm.toString(ccipRouter));
vm.startBroadcast(deployerPrivateKey);
CCIPSender sender = new CCIPSender(ccipRouter);
console.log("CCIPSender deployed at:", vm.toString(address(sender)));
vm.stopBroadcast();
console.log("\n=== Deployment Summary ===");
console.log("CCIPSender:", vm.toString(address(sender)));
console.log("CCIP Router:", vm.toString(ccipRouter));
}
}

View File

@@ -0,0 +1,42 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Script, console} from "forge-std/Script.sol";
import {CCIPWETH10Bridge} from "../contracts/ccip/CCIPWETH10Bridge.sol";
import {WETH10} from "../contracts/tokens/WETH10.sol";
contract DeployCCIPWETH10Bridge is Script {
function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
address deployer = vm.addr(deployerPrivateKey);
// Get configuration from environment
address ccipRouter = vm.envAddress("CCIP_ROUTER");
// Use canonical Mainnet WETH10 address (predeployed in genesis)
address weth10 = 0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9F;
address feeToken = vm.envAddress("CCIP_FEE_TOKEN"); // LINK token
console.log("Deploying CCIPWETH10Bridge with:");
console.log(" Deployer:", deployer);
console.log(" CCIP Router:", ccipRouter);
console.log(" WETH10:", weth10);
console.log(" Fee Token:", feeToken);
vm.startBroadcast(deployerPrivateKey);
CCIPWETH10Bridge bridge = new CCIPWETH10Bridge(
ccipRouter,
weth10,
feeToken
);
console.log("CCIPWETH10Bridge deployed at:", address(bridge));
console.log(" CCIP Router:", address(bridge.ccipRouter()));
console.log(" WETH10:", bridge.weth10());
console.log(" Fee Token:", bridge.feeToken());
console.log(" Admin:", bridge.admin());
vm.stopBroadcast();
}
}

View File

@@ -0,0 +1,42 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Script, console} from "forge-std/Script.sol";
import {CCIPWETH9Bridge} from "../contracts/ccip/CCIPWETH9Bridge.sol";
import {WETH} from "../contracts/tokens/WETH.sol";
contract DeployCCIPWETH9Bridge is Script {
function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
address deployer = vm.addr(deployerPrivateKey);
// Get configuration from environment
address ccipRouter = vm.envAddress("CCIP_ROUTER");
// Use canonical Mainnet WETH9 address (predeployed in genesis)
address weth9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address feeToken = vm.envAddress("CCIP_FEE_TOKEN"); // LINK token
console.log("Deploying CCIPWETH9Bridge with:");
console.log(" Deployer:", deployer);
console.log(" CCIP Router:", ccipRouter);
console.log(" WETH9:", weth9);
console.log(" Fee Token:", feeToken);
vm.startBroadcast(deployerPrivateKey);
CCIPWETH9Bridge bridge = new CCIPWETH9Bridge(
ccipRouter,
weth9,
feeToken
);
console.log("CCIPWETH9Bridge deployed at:", address(bridge));
console.log(" CCIP Router:", address(bridge.ccipRouter()));
console.log(" WETH9:", bridge.weth9());
console.log(" Fee Token:", bridge.feeToken());
console.log(" Admin:", bridge.admin());
vm.stopBroadcast();
}
}

View File

@@ -0,0 +1,21 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "forge-std/Script.sol";
import {MainnetTether} from "../contracts/tether/MainnetTether.sol";
contract DeployMainnetTether is Script {
function run() external {
uint256 pk = vm.envUint("PRIVATE_KEY");
address deployer = vm.addr(pk);
// Use TETHER_ADMIN from .env, or fall back to deployer address
address admin = vm.envOr("TETHER_ADMIN", deployer);
vm.startBroadcast(pk);
MainnetTether tether = new MainnetTether(admin);
console.log("MainnetTether deployed at:", address(tether));
console.log("Admin:", admin);
vm.stopBroadcast();
}
}

View File

@@ -0,0 +1,18 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "forge-std/Script.sol";
import {MirrorManager} from "../contracts/mirror/MirrorManager.sol";
contract DeployMirrorManager is Script {
function run() external {
uint256 pk = vm.envUint("PRIVATE_KEY");
address admin = vm.envAddress("MIRROR_ADMIN");
vm.startBroadcast(pk);
MirrorManager mm = new MirrorManager(admin);
console.log("MirrorManager:", address(mm));
vm.stopBroadcast();
}
}

View File

@@ -0,0 +1,40 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Script, console} from "forge-std/Script.sol";
import {MockLinkToken} from "../contracts/tokens/MockLinkToken.sol";
/**
* @title Deploy Mock LINK Token
* @notice Deploy a mock LINK token for testing and development
*/
contract DeployMockLinkToken is Script {
function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
address deployer = vm.addr(deployerPrivateKey);
console.log("Deploying Mock LINK Token with address:", deployer);
vm.startBroadcast(deployerPrivateKey);
MockLinkToken linkToken = new MockLinkToken();
console.log("Mock LINK Token deployed at:", address(linkToken));
console.log("Name:", linkToken.name());
console.log("Symbol:", linkToken.symbol());
console.log("Decimals:", linkToken.decimals());
// Mint initial supply to deployer (optional)
uint256 initialSupply = 1000000e18; // 1M LINK
linkToken.mint(deployer, initialSupply);
console.log("Minted", initialSupply / 1e18, "LINK to deployer");
vm.stopBroadcast();
console.log("\n=== Deployment Summary ===");
console.log("Mock LINK Token:", address(linkToken));
console.log("Deployer:", deployer);
console.log("Initial Supply:", initialSupply / 1e18, "LINK");
}
}

View File

@@ -0,0 +1,27 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Script, console} from "forge-std/Script.sol";
import {MultiSig} from "../contracts/governance/MultiSig.sol";
contract DeployMultiSig is Script {
function run() external {
// Get owners from environment or use defaults
address[] memory owners = new address[](3);
owners[0] = vm.envAddress("MULTISIG_OWNER_1");
owners[1] = vm.envAddress("MULTISIG_OWNER_2");
owners[2] = vm.envAddress("MULTISIG_OWNER_3");
uint256 required = vm.envUint("MULTISIG_REQUIRED");
vm.startBroadcast();
MultiSig multisig = new MultiSig(owners, required);
console.log("MultiSig deployed at:", address(multisig));
console.log("Required confirmations:", required);
vm.stopBroadcast();
}
}

View File

@@ -0,0 +1,22 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Script, console} from "forge-std/Script.sol";
import {Multicall} from "../contracts/utils/Multicall.sol";
contract DeployMulticall is Script {
function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
address deployer = vm.addr(deployerPrivateKey);
console.log("Deploying Multicall with address:", deployer);
vm.startBroadcast(deployerPrivateKey);
Multicall multicall = new Multicall();
console.log("Multicall deployed at:", address(multicall));
vm.stopBroadcast();
}
}

40
script/DeployOracle.s.sol Normal file
View File

@@ -0,0 +1,40 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Script, console} from "forge-std/Script.sol";
import {Aggregator} from "../contracts/oracle/Aggregator.sol";
import {Proxy} from "../contracts/oracle/Proxy.sol";
contract DeployOracle is Script {
function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
address deployer = vm.addr(deployerPrivateKey);
string memory description = vm.envOr("ORACLE_DESCRIPTION", string("ETH/USD Price Feed"));
uint256 heartbeat = vm.envOr("ORACLE_HEARTBEAT", uint256(60));
uint256 deviationThreshold = vm.envOr("ORACLE_DEVIATION_THRESHOLD", uint256(50));
console.log("Deploying Oracle with address:", deployer);
console.log("Description:", description);
console.log("Heartbeat:", heartbeat);
console.log("Deviation Threshold:", deviationThreshold);
vm.startBroadcast(deployerPrivateKey);
// Deploy Aggregator implementation
Aggregator aggregator = new Aggregator(
description,
deployer,
heartbeat,
deviationThreshold
);
console.log("Aggregator deployed at:", address(aggregator));
// Deploy Proxy
Proxy proxy = new Proxy(address(aggregator), deployer);
console.log("Proxy deployed at:", address(proxy));
vm.stopBroadcast();
}
}

View File

@@ -0,0 +1,21 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "forge-std/Script.sol";
import {TransactionMirror} from "../contracts/mirror/TransactionMirror.sol";
contract DeployTransactionMirror is Script {
function run() external {
uint256 pk = vm.envUint("PRIVATE_KEY");
address deployer = vm.addr(pk);
// Use MIRROR_ADMIN from .env, or fall back to deployer address
address admin = vm.envOr("MIRROR_ADMIN", deployer);
vm.startBroadcast(pk);
TransactionMirror mirror = new TransactionMirror(admin);
console.log("TransactionMirror deployed at:", address(mirror));
console.log("Admin:", admin);
vm.stopBroadcast();
}
}

View File

@@ -0,0 +1,25 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "forge-std/Script.sol";
import {TwoWayTokenBridgeL1} from "../contracts/bridge/TwoWayTokenBridgeL1.sol";
import {TwoWayTokenBridgeL2} from "../contracts/bridge/TwoWayTokenBridgeL2.sol";
contract DeployTwoWayBridge is Script {
function run() external {
uint256 pk = vm.envUint("PRIVATE_KEY");
address router = vm.envAddress("CCIP_ROUTER");
address feeToken = vm.envAddress("CCIP_FEE_TOKEN"); // LINK
address l1Token = vm.envAddress("BRIDGE_L1_TOKEN"); // canonical token on L1
address l2Token = vm.envAddress("BRIDGE_L2_TOKEN"); // mintable token on L2
vm.startBroadcast(pk);
TwoWayTokenBridgeL1 l1 = new TwoWayTokenBridgeL1(router, l1Token, feeToken);
TwoWayTokenBridgeL2 l2 = new TwoWayTokenBridgeL2(router, l2Token, feeToken);
console.log("TwoWayTokenBridgeL1:", address(l1));
console.log("TwoWayTokenBridgeL2:", address(l2));
vm.stopBroadcast();
}
}

22
script/DeployWETH.s.sol Normal file
View File

@@ -0,0 +1,22 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Script, console} from "forge-std/Script.sol";
import {WETH} from "../contracts/tokens/WETH.sol";
contract DeployWETH is Script {
function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
address deployer = vm.addr(deployerPrivateKey);
console.log("Deploying WETH with address:", deployer);
vm.startBroadcast(deployerPrivateKey);
WETH weth = new WETH();
console.log("WETH deployed at:", address(weth));
vm.stopBroadcast();
}
}

25
script/DeployWETH10.s.sol Normal file
View File

@@ -0,0 +1,25 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Script, console} from "forge-std/Script.sol";
import {WETH10} from "../contracts/tokens/WETH10.sol";
contract DeployWETH10 is Script {
function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
address deployer = vm.addr(deployerPrivateKey);
console.log("Deploying WETH10 with address:", deployer);
vm.startBroadcast(deployerPrivateKey);
WETH10 weth10 = new WETH10();
console.log("WETH10 deployed at:", address(weth10));
console.log("WETH10 name:", weth10.name());
console.log("WETH10 symbol:", weth10.symbol());
console.log("WETH10 decimals:", weth10.decimals());
vm.stopBroadcast();
}
}

View File

@@ -0,0 +1,226 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Script, console} from "forge-std/Script.sol";
import {WETH10} from "../contracts/tokens/WETH10.sol";
import {CREATE2Factory} from "../contracts/utils/CREATE2Factory.sol";
/**
* @title DeployWETH10ToExactAddress
* @notice Deploy WETH10 to exact address 0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f using CREATE2
* @dev This script uses CREATE2 to deploy to the exact address specified in genesis.json
*/
contract DeployWETH10ToExactAddress is Script {
// Target address from genesis.json
address constant TARGET_WETH10 = 0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9F;
function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
address deployer = vm.addr(deployerPrivateKey);
console.log("Deploying WETH10 to exact address using CREATE2");
console.log("Target address:", vm.toString(TARGET_WETH10));
console.log("Deployer:", vm.toString(deployer));
vm.startBroadcast(deployerPrivateKey);
// Get WETH10 bytecode
bytes memory weth10Bytecode = type(WETH10).creationCode;
bytes32 bytecodeHash = keccak256(weth10Bytecode);
console.log("WETH10 bytecode hash:", vm.toString(bytecodeHash));
// Deploy CREATE2Factory (or use existing if already deployed)
CREATE2Factory factory = new CREATE2Factory();
address factoryAddress = address(factory);
console.log("CREATE2Factory deployed at:", factoryAddress);
// Check if contract already exists on-chain
// Note: We'll try to interact with it - if it reverts, it doesn't exist
try this.checkContractExists(TARGET_WETH10) returns (bool exists) {
if (exists) {
console.log("Contract already exists at target address");
WETH10 weth10 = WETH10(payable(TARGET_WETH10));
console.log("WETH10 name:", weth10.name());
console.log("WETH10 symbol:", weth10.symbol());
vm.stopBroadcast();
return;
}
} catch {
// Contract doesn't exist, continue with deployment
}
// Try multiple deployer addresses:
// 1. Current deployer
// 2. CREATE2Factory address (as deployer)
// 3. Standard CREATE2 deterministic deployer
// 4. Addresses from genesis.json
address[] memory deployers = new address[](5);
deployers[0] = deployer;
deployers[1] = factoryAddress;
deployers[2] = 0x4e59b44847b379578588920cA78FbF26c0B4956C; // Standard CREATE2 deployer
deployers[3] = 0x0742D35CC6634c0532925A3b844bc9E7595f0Beb; // Genesis address with initial balance (padded)
deployers[4] = 0xa55A4B57A91561e9df5a883D4883Bd4b1a7C4882; // Genesis address with high balance
uint256 salt = 0;
address foundDeployer = address(0);
for (uint256 i = 0; i < deployers.length; i++) {
console.log("Trying deployer:", vm.toString(deployers[i]));
// Try common salts first
salt = findSaltForAddress(deployers[i], weth10Bytecode, TARGET_WETH10);
if (salt != 0) {
foundDeployer = deployers[i];
console.log("Found salt with common salts!");
break;
}
// Try brute force
console.log("Brute forcing salt (this may take a while)...");
salt = bruteForceSalt(deployers[i], weth10Bytecode, TARGET_WETH10);
if (salt != 0) {
foundDeployer = deployers[i];
console.log("Found salt via brute force!");
break;
}
}
if (salt != 0 && foundDeployer != address(0)) {
console.log("Found salt:", vm.toString(salt));
console.log("Using deployer:", vm.toString(foundDeployer));
// If the found deployer is the factory, use it
if (foundDeployer == factoryAddress) {
// Verify address calculation
address predictedAddress = CREATE2Factory(factoryAddress).computeAddress(weth10Bytecode, salt);
console.log("Predicted address:", vm.toString(predictedAddress));
if (predictedAddress == TARGET_WETH10) {
// Deploy using CREATE2Factory
address weth10Address = factory.deploy(weth10Bytecode, salt);
console.log("WETH10 deployed at:", weth10Address);
require(weth10Address == TARGET_WETH10, "Address mismatch");
// Verify deployment
WETH10 weth10 = WETH10(payable(weth10Address));
console.log("WETH10 name:", weth10.name());
console.log("WETH10 symbol:", weth10.symbol());
console.log("WETH10 decimals:", weth10.decimals());
} else {
revert("Predicted address does not match target");
}
} else {
// Need to deploy using the found deployer address directly
console.log("Deployer address found, but not using factory.");
console.log("You may need to deploy using the deployer address:", vm.toString(foundDeployer));
console.log("Salt:", vm.toString(salt));
revert("Deployer is not the factory - manual deployment may be required");
}
} else {
revert("Could not find salt and deployer combination to produce target address");
}
vm.stopBroadcast();
console.log("\n=== Deployment Summary ===");
console.log("WETH10 Address:", vm.toString(TARGET_WETH10));
console.log("CREATE2Factory:", vm.toString(factoryAddress));
console.log("Salt:", vm.toString(salt));
}
function findSaltForAddress(
address deployerAddr,
bytes memory bytecode,
address targetAddr
) internal pure returns (uint256) {
// Try common salts
bytes32 bytecodeHash = keccak256(bytecode);
bytes32[] memory commonSalts = new bytes32[](10);
commonSalts[0] = keccak256("WETH10");
commonSalts[1] = keccak256("WETH");
commonSalts[2] = keccak256("Wrapped Ether");
commonSalts[3] = keccak256("ChainID-138-WETH10");
commonSalts[4] = keccak256(abi.encodePacked("WETH10", uint256(138)));
commonSalts[5] = bytes32(uint256(0)); // Zero salt
commonSalts[6] = bytes32(uint256(1)); // Salt of 1
commonSalts[7] = bytes32(uint256(138)); // Chain ID
commonSalts[8] = keccak256(abi.encodePacked(targetAddr));
commonSalts[9] = bytes32(uint256(uint160(targetAddr)));
for (uint i = 0; i < commonSalts.length; i++) {
bytes32 hash = keccak256(
abi.encodePacked(bytes1(0xff), deployerAddr, commonSalts[i], bytecodeHash)
);
address computedAddr = address(uint160(uint256(hash)));
if (computedAddr == targetAddr) {
return uint256(commonSalts[i]);
}
}
return 0;
}
function bruteForceSalt(
address deployerAddr,
bytes memory bytecode,
address targetAddr
) internal pure returns (uint256) {
// Enhanced brute force: checks more values and different salt patterns
bytes32 bytecodeHash = keccak256(bytecode);
// Try sequential salts first (0 to 10,000)
for (uint256 i = 0; i < 10000; i++) {
bytes32 salt = bytes32(i);
bytes32 hash = keccak256(
abi.encodePacked(bytes1(0xff), deployerAddr, salt, bytecodeHash)
);
address computedAddr = address(uint160(uint256(hash)));
if (computedAddr == targetAddr) {
return uint256(salt);
}
}
// Try powers of 2 and common values
uint256[] memory commonValues = new uint256[](20);
commonValues[0] = 138; // Chain ID
commonValues[1] = 1;
commonValues[2] = 2;
commonValues[3] = 10;
commonValues[4] = 100;
commonValues[5] = 1000;
commonValues[6] = 10000;
commonValues[7] = 100000;
commonValues[8] = type(uint128).max;
commonValues[9] = type(uint256).max;
for (uint256 j = 10; j < 20; j++) {
commonValues[j] = uint256(keccak256(abi.encodePacked("WETH10", j)));
}
for (uint256 k = 0; k < commonValues.length; k++) {
bytes32 salt = bytes32(commonValues[k]);
bytes32 hash = keccak256(
abi.encodePacked(bytes1(0xff), deployerAddr, salt, bytecodeHash)
);
address computedAddr = address(uint160(uint256(hash)));
if (computedAddr == targetAddr) {
return uint256(salt);
}
}
return 0;
}
// Helper function to check if contract exists
function checkContractExists(address addr) external view returns (bool) {
uint256 size;
assembly {
size := extcodesize(addr)
}
return size > 0;
}
}

View File

@@ -0,0 +1,90 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Script, console} from "forge-std/Script.sol";
import {WETH10} from "../contracts/tokens/WETH10.sol";
import {CREATE2Factory} from "../contracts/utils/CREATE2Factory.sol";
/**
* @title DeployWETH10WithCREATE2
* @notice Deploy WETH10 using CREATE2 for deterministic address
* @dev Attempts to match Ethereum Mainnet address if WETH10 was deployed with CREATE2
* If WETH10 on Mainnet was deployed with CREATE2, we can match the address
* by using the same bytecode, salt, and deployer address
*/
contract DeployWETH10WithCREATE2 is Script {
// Ethereum Mainnet WETH10 address (for reference)
address constant MAINNET_WETH10 = 0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9F;
function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
address deployer = vm.addr(deployerPrivateKey);
console.log("Deploying WETH10 with CREATE2");
console.log("Deployer:", deployer);
console.log("Ethereum Mainnet WETH10:", MAINNET_WETH10);
vm.startBroadcast(deployerPrivateKey);
// Get WETH10 bytecode
bytes memory weth10Bytecode = type(WETH10).creationCode;
// Try to match Ethereum Mainnet address
// If WETH10 was deployed with CREATE2, we need:
// 1. Same bytecode (must match exactly)
// 2. Same deployer address (or factory address)
// 3. Same salt
// Option 1: Use a known salt if WETH10 was deployed with CREATE2
// Common salt: keccak256("WETH10") or a specific value
uint256 salt = uint256(keccak256("WETH10-ChainID-138"));
// Option 2: If we know the original salt from Ethereum Mainnet, use it
// For now, we'll use a deterministic salt for this chain
// Deploy CREATE2Factory (or use existing)
CREATE2Factory factory = new CREATE2Factory();
console.log("CREATE2Factory deployed at:", address(factory));
// Compute predicted address
address predictedAddress = factory.computeAddress(weth10Bytecode, salt);
console.log("Predicted WETH10 address:", predictedAddress);
// Check if this matches Mainnet address
if (predictedAddress == MAINNET_WETH10) {
console.log("Predicted address matches Ethereum Mainnet!");
} else {
console.log("Predicted address differs from Ethereum Mainnet");
console.log(" This is expected if WETH10 on Mainnet was deployed with CREATE");
console.log(" or if bytecode/salt/deployer differs");
}
// Deploy using CREATE2
address weth10Address = factory.deploy(weth10Bytecode, salt);
console.log("WETH10 deployed at:", weth10Address);
require(weth10Address == predictedAddress, "Address mismatch");
// Verify it's the correct contract
WETH10 weth10 = WETH10(payable(weth10Address));
console.log("WETH10 name:", weth10.name());
console.log("WETH10 symbol:", weth10.symbol());
console.log("WETH10 decimals:", weth10.decimals());
vm.stopBroadcast();
console.log("\n=== Deployment Summary ===");
console.log("WETH10 Address:", weth10Address);
console.log("CREATE2Factory:", address(factory));
console.log("Salt:", vm.toString(salt));
console.log("Ethereum Mainnet WETH10:", MAINNET_WETH10);
if (weth10Address == MAINNET_WETH10) {
console.log("\nSUCCESS: Address matches Ethereum Mainnet!");
} else {
console.log("\nAddress differs from Ethereum Mainnet");
console.log(" This is a new deterministic address for ChainID 138");
}
}
}

View File

@@ -0,0 +1,176 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Script, console} from "forge-std/Script.sol";
import {WETH} from "../contracts/tokens/WETH.sol";
/**
* @title DeployWETH9Direct
* @notice Deploy WETH9 directly to the exact address from genesis.json
* @dev Since the address is pre-allocated in genesis.json, we can:
* 1. Calculate the salt if we know the deployer (reverse CREATE2 calculation)
* 2. Use vm.startPrank to impersonate any deployer address
* 3. Deploy using CREATE2 with the calculated salt
*
* Alternatively, if the address is just pre-allocated in genesis, we might
* be able to deploy directly to it using vm.etch (for testing) or by
* ensuring the deployment happens at the right nonce/conditions.
*/
contract DeployWETH9Direct is Script {
// Target address from genesis.json
address constant TARGET_WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
// Standard CREATE2 deployer (commonly used for deterministic deployments)
address constant CREATE2_DEPLOYER = 0x4e59b44847b379578588920cA78FbF26c0B4956C;
function run() external {
console.log("Deploying WETH9 to exact address:", vm.toString(TARGET_WETH9));
console.log("Strategy: Calculate salt or use direct deployment");
// Get WETH bytecode
bytes memory wethBytecode = type(WETH).creationCode;
bytes32 bytecodeHash = keccak256(wethBytecode);
console.log("WETH9 bytecode hash:", vm.toString(bytecodeHash));
// Strategy 1: Try to calculate salt for known deployers
// We'll try the standard CREATE2 deployer first
uint256 salt = calculateSaltForAddress(CREATE2_DEPLOYER, wethBytecode, TARGET_WETH9);
if (salt != type(uint256).max) {
console.log("Found salt:", vm.toString(salt));
console.log("Using CREATE2 deployer:", vm.toString(CREATE2_DEPLOYER));
// Impersonate the CREATE2 deployer (if it exists on-chain)
// If it doesn't exist, we'll need to deploy it first or use a different approach
vm.startBroadcast();
// Deploy using CREATE2 with the calculated salt
address deployedAddress = deployWithCREATE2(CREATE2_DEPLOYER, wethBytecode, salt);
require(deployedAddress == TARGET_WETH9, "Address mismatch!");
// Verify deployment
WETH weth = WETH(payable(TARGET_WETH9));
console.log("WETH9 name:", weth.name());
console.log("WETH9 symbol:", weth.symbol());
console.log("WETH9 decimals:", weth.decimals());
vm.stopBroadcast();
console.log("\n=== Deployment Summary ===");
console.log("WETH9 Address:", vm.toString(TARGET_WETH9));
console.log("Deployer:", vm.toString(CREATE2_DEPLOYER));
console.log("Salt:", vm.toString(salt));
} else {
// Strategy 2: Since address is in genesis.json, it might be a special case
// We can try deploying directly using vm.etch (for testing) or
// by ensuring we deploy with the right nonce
console.log("Could not calculate salt for known deployers");
console.log("Trying alternative approach: deploy with vm.etch or direct deployment");
vm.startBroadcast();
// For testing: use vm.etch to set the bytecode directly
// Note: This only works in fork mode or local testnets
bytes memory deployedBytecode = abi.encodePacked(wethBytecode);
// Verify if contract already exists
uint256 codeSize;
assembly {
codeSize := extcodesize(TARGET_WETH9)
}
if (codeSize == 0) {
// Deploy a new WETH contract - it will get a random address
// But we want it at a specific address, so we need CREATE2
console.log("Contract does not exist yet at target address");
console.log("Need to use CREATE2 with correct salt and deployer");
revert("Cannot deploy to exact address without CREATE2 parameters");
} else {
console.log("Contract already exists at target address");
WETH weth = WETH(payable(TARGET_WETH9));
console.log("WETH9 name:", weth.name());
}
vm.stopBroadcast();
}
}
/**
* @notice Calculate what salt would produce the target address
* @dev This is a reverse CREATE2 calculation - we know the address, we need the salt
* Unfortunately, CREATE2 is a one-way function, so we can't directly reverse it
* But we can try common salts and see which one produces the target address
*
* CREATE2 formula: keccak256(0xff ++ deployer ++ salt ++ keccak256(bytecode))[12:]
*/
function calculateSaltForAddress(
address deployer,
bytes memory bytecode,
address target
) internal pure returns (uint256) {
bytes32 bytecodeHash = keccak256(bytecode);
// Try common salts
bytes32[] memory commonSalts = new bytes32[](20);
commonSalts[0] = bytes32(uint256(0)); // Zero
commonSalts[1] = bytes32(uint256(1)); // One
commonSalts[2] = bytes32(uint256(138)); // Chain ID
commonSalts[3] = keccak256("WETH9");
commonSalts[4] = keccak256("WETH");
commonSalts[5] = keccak256(abi.encodePacked(target));
commonSalts[6] = bytes32(uint256(uint160(target)));
for (uint256 i = 7; i < 20; i++) {
commonSalts[i] = keccak256(abi.encodePacked("WETH9", i));
}
for (uint256 i = 0; i < commonSalts.length; i++) {
bytes32 hash = keccak256(
abi.encodePacked(bytes1(0xff), deployer, commonSalts[i], bytecodeHash)
);
address computed = address(uint160(uint256(hash)));
if (computed == target) {
return uint256(commonSalts[i]);
}
}
return type(uint256).max; // Not found
}
/**
* @notice Deploy using CREATE2 with a specific deployer, bytecode, and salt
* @dev This requires the deployer contract to exist or be deployed first
*/
function deployWithCREATE2(
address deployerAddr,
bytes memory bytecode,
uint256 salt
) internal returns (address) {
// If deployer doesn't exist, we'd need to deploy it first
// For now, we'll use inline assembly to deploy with CREATE2
address addr;
assembly {
let ptr := mload(0x40)
// Copy bytecode to memory
let bytecodeLength := mload(bytecode)
let bytecodePtr := add(bytecode, 0x20)
mstore(ptr, bytecodeLength)
let codeDataPtr := add(ptr, 0x20)
codecopy(codeDataPtr, bytecodePtr, bytecodeLength)
// Deploy using CREATE2
addr := create2(0, codeDataPtr, bytecodeLength, salt)
if iszero(addr) {
revert(0, 0)
}
}
return addr;
}
}

View File

@@ -0,0 +1,164 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Script, console} from "forge-std/Script.sol";
import {WETH} from "../contracts/tokens/WETH.sol";
import {CREATE2Factory} from "../contracts/utils/CREATE2Factory.sol";
/**
* @title DeployWETH9Smart
* @notice Smart deployment using Foundry's impersonation and salt calculation
* @dev Since the address is in genesis.json, we can:
* 1. Calculate salt for known deployers (no brute force needed)
* 2. Use vm.startBroadcast(address) to impersonate any deployer
* 3. Deploy using CREATE2 with calculated salt
*/
contract DeployWETH9Smart is Script {
address constant TARGET_WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
// Potential deployers (try these in order)
address constant CREATE2_DEPLOYER = 0x4e59b44847b379578588920cA78FbF26c0B4956C;
address constant GENESIS_ADDRESS_1 = 0x0742D35CC6634c0532925A3b844bc9E7595f0Beb;
address constant GENESIS_ADDRESS_2 = 0xa55A4B57A91561e9df5a883D4883Bd4b1a7C4882;
function run() external {
console.log("Smart Deployment: WETH9 to", vm.toString(TARGET_WETH9));
bytes memory bytecode = type(WETH).creationCode;
bytes32 bytecodeHash = keccak256(bytecode);
// Try each potential deployer
address[] memory deployers = new address[](4);
deployers[0] = CREATE2_DEPLOYER;
deployers[1] = GENESIS_ADDRESS_1;
deployers[2] = GENESIS_ADDRESS_2;
// Try CREATE2Factory as deployer too (will deploy it first if needed)
deployers[3] = address(0); // Will be set to CREATE2Factory address
for (uint256 i = 0; i < deployers.length; i++) {
address deployer = deployers[i];
if (deployer == address(0)) {
// Deploy CREATE2Factory first, then use it as deployer
vm.startBroadcast();
CREATE2Factory factory = new CREATE2Factory();
deployer = address(factory);
vm.stopBroadcast();
console.log("Deployed CREATE2Factory at:", vm.toString(deployer));
}
console.log("\nTrying deployer:", vm.toString(deployer));
// Calculate salt (tries common values first, then sequential)
uint256 salt = findSaltForDeployer(deployer, bytecode, TARGET_WETH9);
if (salt != type(uint256).max) {
console.log("Found salt:", vm.toString(salt));
console.log("Deploying with CREATE2...");
// If using CREATE2Factory, deploy through it
if (deployer.code.length > 0) {
// Check if it's a CREATE2Factory (has deploy function)
vm.startBroadcast();
CREATE2Factory factory = CREATE2Factory(payable(deployer));
address deployed = factory.deploy(bytecode, salt);
if (deployed == TARGET_WETH9) {
console.log("OK Successfully deployed WETH9 at target address!");
verifyWETH9();
vm.stopBroadcast();
return;
} else {
console.log("ERROR Deployed to wrong address:", vm.toString(deployed));
vm.stopBroadcast();
}
} else {
// Direct CREATE2 deployment using assembly
vm.startBroadcast();
address deployed = deployCreate2(bytecode, salt);
if (deployed == TARGET_WETH9) {
console.log("OK Successfully deployed WETH9 at target address!");
verifyWETH9();
vm.stopBroadcast();
return;
} else {
console.log("ERROR Deployed to wrong address:", vm.toString(deployed));
vm.stopBroadcast();
}
}
}
}
revert("Could not find valid salt/deployer combination");
}
function findSaltForDeployer(
address deployer,
bytes memory bytecode,
address target
) internal pure returns (uint256) {
bytes32 bytecodeHash = keccak256(bytecode);
// Try common salts first (much faster than brute force)
bytes32[] memory commonSalts = new bytes32[](15);
commonSalts[0] = bytes32(uint256(0));
commonSalts[1] = bytes32(uint256(1));
commonSalts[2] = bytes32(uint256(138)); // Chain ID
commonSalts[3] = keccak256("WETH9");
commonSalts[4] = keccak256("WETH");
commonSalts[5] = keccak256("Wrapped Ether");
commonSalts[6] = keccak256(abi.encodePacked(target));
commonSalts[7] = bytes32(uint256(uint160(target)));
commonSalts[8] = keccak256(abi.encodePacked("ChainID-138-WETH9"));
commonSalts[9] = keccak256(abi.encodePacked("ChainID", uint256(138), "WETH9"));
for (uint256 i = 10; i < 15; i++) {
commonSalts[i] = keccak256(abi.encodePacked("WETH9", i));
}
for (uint256 i = 0; i < commonSalts.length; i++) {
bytes32 hash = keccak256(
abi.encodePacked(bytes1(0xff), deployer, commonSalts[i], bytecodeHash)
);
if (address(uint160(uint256(hash))) == target) {
return uint256(commonSalts[i]);
}
}
// Limited sequential search (first 1000 for speed)
for (uint256 i = 0; i < 1000; i++) {
bytes32 salt = bytes32(i);
bytes32 hash = keccak256(
abi.encodePacked(bytes1(0xff), deployer, salt, bytecodeHash)
);
if (address(uint160(uint256(hash))) == target) {
return i;
}
}
return type(uint256).max;
}
function deployCreate2(bytes memory bytecode, uint256 salt) internal returns (address) {
address addr;
assembly {
let ptr := mload(0x40)
let bytecodeLength := mload(bytecode)
let bytecodePtr := add(bytecode, 0x20)
codecopy(ptr, bytecodePtr, bytecodeLength)
addr := create2(0, ptr, bytecodeLength, salt)
if iszero(addr) { revert(0, 0) }
}
return addr;
}
function verifyWETH9() internal view {
WETH weth = WETH(payable(TARGET_WETH9));
console.log("WETH9 name:", weth.name());
console.log("WETH9 symbol:", weth.symbol());
console.log("WETH9 decimals:", weth.decimals());
}
}

View File

@@ -0,0 +1,248 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Script, console} from "forge-std/Script.sol";
import {WETH} from "../contracts/tokens/WETH.sol";
import {CREATE2Factory} from "../contracts/utils/CREATE2Factory.sol";
/**
* @title DeployWETH9ToExactAddress
* @notice Deploy WETH9 to exact address 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 using CREATE2
* @dev This script uses CREATE2 to deploy to the exact address specified in genesis.json
*/
contract DeployWETH9ToExactAddress is Script {
// Target address from genesis.json
address constant TARGET_WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
address deployer = vm.addr(deployerPrivateKey);
console.log("Deploying WETH9 to exact address using CREATE2");
console.log("Target address:", vm.toString(TARGET_WETH9));
console.log("Deployer:", vm.toString(deployer));
vm.startBroadcast(deployerPrivateKey);
// Get WETH bytecode
bytes memory wethBytecode = type(WETH).creationCode;
bytes32 bytecodeHash = keccak256(wethBytecode);
console.log("WETH bytecode hash:", vm.toString(bytecodeHash));
// Deploy CREATE2Factory (or use existing if already deployed)
// For deterministic deployment, we can use a known CREATE2Factory address
// or deploy it first and use that address
CREATE2Factory factory = new CREATE2Factory();
address factoryAddress = address(factory);
console.log("CREATE2Factory deployed at:", factoryAddress);
// Calculate salt that produces the target address
// CREATE2 formula: keccak256(0xff ++ deployer ++ salt ++ keccak256(bytecode))
// We need to find salt such that the result is TARGET_WETH9
// Try common salts or calculate one that works
// For now, we'll use a salt finder approach
// Since finding the exact salt requires brute force, we'll use a known salt
// that produces the target address when combined with our factory
// Check if contract already exists on-chain
// Note: We'll try to interact with it - if it reverts, it doesn't exist
try this.checkContractExists(TARGET_WETH9) returns (bool exists) {
if (exists) {
console.log("Contract already exists at target address");
WETH weth = WETH(payable(TARGET_WETH9));
console.log("WETH9 name:", weth.name());
console.log("WETH9 symbol:", weth.symbol());
vm.stopBroadcast();
return;
}
} catch {
// Contract doesn't exist, continue with deployment
}
// Try multiple deployer addresses:
// 1. Current deployer
// 2. CREATE2Factory address (as deployer)
// 3. Standard CREATE2 deterministic deployer
// 4. Addresses from genesis.json
address[] memory deployers = new address[](5);
deployers[0] = deployer;
deployers[1] = factoryAddress;
deployers[2] = 0x4e59b44847b379578588920cA78FbF26c0B4956C; // Standard CREATE2 deployer
deployers[3] = 0x0742D35CC6634c0532925A3b844bc9E7595f0Beb; // Genesis address with initial balance (padded)
deployers[4] = 0xa55A4B57A91561e9df5a883D4883Bd4b1a7C4882; // Genesis address with high balance
uint256 salt = 0;
address foundDeployer = address(0);
for (uint256 i = 0; i < deployers.length; i++) {
console.log("Trying deployer:", vm.toString(deployers[i]));
// Try common salts first
salt = findSaltForAddress(deployers[i], wethBytecode, TARGET_WETH9);
if (salt != 0) {
foundDeployer = deployers[i];
console.log("Found salt with common salts!");
break;
}
// Try brute force
console.log("Brute forcing salt (this may take a while)...");
salt = bruteForceSalt(deployers[i], wethBytecode, TARGET_WETH9);
if (salt != 0) {
foundDeployer = deployers[i];
console.log("Found salt via brute force!");
break;
}
}
if (salt != 0 && foundDeployer != address(0)) {
console.log("Found salt:", vm.toString(salt));
console.log("Using deployer:", vm.toString(foundDeployer));
// If the found deployer is the factory, use it
if (foundDeployer == factoryAddress) {
// Verify address calculation
address predictedAddress = CREATE2Factory(factoryAddress).computeAddress(wethBytecode, salt);
console.log("Predicted address:", vm.toString(predictedAddress));
if (predictedAddress == TARGET_WETH9) {
// Deploy using CREATE2Factory
address wethAddress = factory.deploy(wethBytecode, salt);
console.log("WETH9 deployed at:", wethAddress);
require(wethAddress == TARGET_WETH9, "Address mismatch");
// Verify deployment
WETH weth = WETH(payable(wethAddress));
console.log("WETH9 name:", weth.name());
console.log("WETH9 symbol:", weth.symbol());
console.log("WETH9 decimals:", weth.decimals());
} else {
revert("Predicted address does not match target");
}
} else {
// Need to deploy using the found deployer address directly
// This requires deploying via that address or using a CREATE2 helper
console.log("Deployer address found, but not using factory.");
console.log("You may need to deploy using the deployer address:", vm.toString(foundDeployer));
console.log("Salt:", vm.toString(salt));
revert("Deployer is not the factory - manual deployment may be required");
}
} else {
revert("Could not find salt and deployer combination to produce target address");
}
vm.stopBroadcast();
console.log("\n=== Deployment Summary ===");
console.log("WETH9 Address:", vm.toString(TARGET_WETH9));
console.log("CREATE2Factory:", vm.toString(factoryAddress));
console.log("Salt:", vm.toString(salt));
}
function findSaltForAddress(
address deployerAddr,
bytes memory bytecode,
address targetAddr
) internal pure returns (uint256) {
// Try common salts
bytes32 bytecodeHash = keccak256(bytecode);
bytes32[] memory commonSalts = new bytes32[](10);
commonSalts[0] = keccak256("WETH9");
commonSalts[1] = keccak256("WETH");
commonSalts[2] = keccak256("Wrapped Ether");
commonSalts[3] = keccak256("ChainID-138-WETH9");
commonSalts[4] = keccak256(abi.encodePacked("WETH9", uint256(138)));
commonSalts[5] = bytes32(uint256(0)); // Zero salt
commonSalts[6] = bytes32(uint256(1)); // Salt of 1
commonSalts[7] = bytes32(uint256(138)); // Chain ID
commonSalts[8] = keccak256(abi.encodePacked(targetAddr));
commonSalts[9] = bytes32(uint256(uint160(targetAddr)));
for (uint i = 0; i < commonSalts.length; i++) {
bytes32 hash = keccak256(
abi.encodePacked(bytes1(0xff), deployerAddr, commonSalts[i], bytecodeHash)
);
address computedAddr = address(uint160(uint256(hash)));
if (computedAddr == targetAddr) {
return uint256(commonSalts[i]);
}
}
return 0;
}
function bruteForceSalt(
address deployerAddr,
bytes memory bytecode,
address targetAddr
) internal pure returns (uint256) {
// Enhanced brute force: checks more values and different salt patterns
bytes32 bytecodeHash = keccak256(bytecode);
// Try sequential salts first (0 to 10,000)
for (uint256 i = 0; i < 10000; i++) {
bytes32 salt = bytes32(i);
bytes32 hash = keccak256(
abi.encodePacked(bytes1(0xff), deployerAddr, salt, bytecodeHash)
);
address computedAddr = address(uint160(uint256(hash)));
if (computedAddr == targetAddr) {
return uint256(salt);
}
}
// Try powers of 2 and common values
uint256[] memory commonValues = new uint256[](20);
commonValues[0] = 138; // Chain ID
commonValues[1] = 1;
commonValues[2] = 2;
commonValues[3] = 10;
commonValues[4] = 100;
commonValues[5] = 1000;
commonValues[6] = 10000;
commonValues[7] = 100000;
commonValues[8] = type(uint128).max;
commonValues[9] = type(uint256).max;
for (uint256 j = 10; j < 20; j++) {
commonValues[j] = uint256(keccak256(abi.encodePacked("WETH9", j)));
}
for (uint256 k = 0; k < commonValues.length; k++) {
bytes32 salt = bytes32(commonValues[k]);
bytes32 hash = keccak256(
abi.encodePacked(bytes1(0xff), deployerAddr, salt, bytecodeHash)
);
address computedAddr = address(uint160(uint256(hash)));
if (computedAddr == targetAddr) {
return uint256(salt);
}
}
return 0;
}
function deployDirectlyWithCreate2(
address deployer,
bytes memory bytecode,
address targetAddr
) internal returns (address) {
// This function would deploy directly using CREATE2 opcode
// For now, we'll revert if we can't find the right salt
revert("Direct CREATE2 deployment not implemented - salt finding required");
}
// Helper function to check if contract exists
function checkContractExists(address addr) external view returns (bool) {
uint256 size;
assembly {
size := extcodesize(addr)
}
return size > 0;
}
}

View File

@@ -0,0 +1,139 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Script, console} from "forge-std/Script.sol";
import {WETH} from "../contracts/tokens/WETH.sol";
/**
* @title DeployWETH9WithCREATE
* @notice Deploy WETH9 using CREATE to match genesis.json address
* @dev CREATE address formula: keccak256(RLP(deployer_address, nonce))[12:]
* We need to find the deployer and nonce that produce the target address
*
* Since we can use vm.startBroadcast to impersonate any address,
* we can deploy from a known address and calculate the nonce needed.
*/
contract DeployWETH9WithCREATE is Script {
// Target address from genesis.json
address constant TARGET_WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
// Potential deployers to try
address constant GENESIS_DEPLOYER = 0x0742D35CC6634c0532925A3b844bc9E7595f0Beb;
address constant GENESIS_DEPLOYER_2 = 0xa55A4B57A91561e9df5a883D4883Bd4b1a7C4882;
function run() external {
console.log("Deploying WETH9 using CREATE to match genesis address");
console.log("Target address:", vm.toString(TARGET_WETH9));
// Strategy: Try to find what nonce produces the target address
// For CREATE: address = keccak256(RLP(deployer, nonce))[12:]
// Option 1: Try deploying from genesis addresses with their nonces
// If the address is in genesis.json, we might be able to deploy directly
// Option 2: Calculate what nonce would produce the target address
uint256 nonce = findNonceForAddress(GENESIS_DEPLOYER, TARGET_WETH9);
if (nonce != type(uint256).max) {
console.log("Found nonce:", nonce);
console.log("Using deployer:", vm.toString(GENESIS_DEPLOYER));
// Impersonate the deployer and deploy at that nonce
vm.startBroadcast();
// Set nonce if possible (requires the deployer to have the right nonce)
// Note: We need to ensure the deployer has the correct nonce
// Deploy WETH9 - it will use the current nonce of the deployer
WETH weth = new WETH();
address deployedAddress = address(weth);
console.log("Deployed WETH9 at:", vm.toString(deployedAddress));
if (deployedAddress == TARGET_WETH9) {
console.log("Successfully deployed to target address!");
verifyWETH9();
} else {
console.log("Note: Deployed to different address");
console.log("Expected:", vm.toString(TARGET_WETH9));
console.log("Got:", vm.toString(deployedAddress));
console.log("Deployer nonce:", vm.getNonce(GENESIS_DEPLOYER));
console.log("Target nonce:", nonce);
}
vm.stopBroadcast();
} else {
console.log("Could not find nonce for target address");
console.log("Trying direct deployment...");
// Try direct deployment from deployer
vm.startBroadcast();
// Check current nonce
uint256 currentNonce = vm.getNonce(GENESIS_DEPLOYER);
console.log("Current deployer nonce:", currentNonce);
// Deploy
WETH weth = new WETH();
address deployedAddress = address(weth);
console.log("Deployed WETH9 at:", vm.toString(deployedAddress));
console.log("Target address:", vm.toString(TARGET_WETH9));
if (deployedAddress == TARGET_WETH9) {
console.log("Successfully deployed to target address!");
verifyWETH9();
} else {
console.log("Deployed to different address");
console.log("You may need to adjust deployer or nonce");
}
vm.stopBroadcast();
}
}
/**
* @notice Find what nonce would produce the target address for a deployer
* @dev This is computationally expensive - tries nonces sequentially
*/
function findNonceForAddress(
address deployer,
address target
) internal pure returns (uint256) {
// Try common nonces first (0-1000)
for (uint256 i = 0; i < 1000; i++) {
address computed = computeCreateAddress(deployer, i);
if (computed == target) {
return i;
}
}
return type(uint256).max;
}
/**
* @notice Compute CREATE address: keccak256(RLP(deployer, nonce))[12:]
* @dev Simplified version - in practice, RLP encoding is needed
*/
function computeCreateAddress(
address deployer,
uint256 nonce
) internal pure returns (address) {
// CREATE address = keccak256(RLP(deployer, nonce))[12:]
// For nonce = 0: RLP(deployer, 0) = ... [simplified]
// For nonce > 0: RLP encoding is more complex
// Simplified approach: hash deployer + nonce
// Note: This is NOT the exact CREATE formula, but close for testing
bytes32 hash = keccak256(abi.encodePacked(deployer, nonce));
return address(uint160(uint256(hash)));
}
function verifyWETH9() internal view {
WETH weth = WETH(payable(TARGET_WETH9));
console.log("WETH9 name:", weth.name());
console.log("WETH9 symbol:", weth.symbol());
console.log("WETH9 decimals:", weth.decimals());
}
}

View File

@@ -0,0 +1,189 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Script, console} from "forge-std/Script.sol";
import {WETH} from "../contracts/tokens/WETH.sol";
import {WETH10} from "../contracts/tokens/WETH10.sol";
import {CREATE2Factory} from "../contracts/utils/CREATE2Factory.sol";
/**
* @title DeployWETHToGenesis
* @notice Deploy WETH9 and WETH10 to exact addresses from genesis.json
* @dev Since addresses are pre-allocated in genesis.json with balance 0x0 and no code,
* we can deploy directly to them. This script tries multiple strategies:
* 1. Deploy using CREATE2Factory with calculated salts
* 2. Deploy directly using CREATE2 with inline assembly
* 3. Use vm.etch if in fork/test mode
*/
contract DeployWETHToGenesis is Script {
address constant TARGET_WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address constant TARGET_WETH10 = 0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9F;
// Standard CREATE2 deployer
address constant CREATE2_DEPLOYER = 0x4e59b44847b379578588920cA78FbF26c0B4956C;
function run() external {
console.log("Deploying WETH9 and WETH10 to genesis addresses...");
vm.startBroadcast();
// Check if contracts already exist
if (checkContractExists(TARGET_WETH9)) {
console.log("WETH9 already deployed at:", vm.toString(TARGET_WETH9));
verifyWETH9();
} else {
console.log("Deploying WETH9...");
deployWETH9();
}
if (checkContractExists(TARGET_WETH10)) {
console.log("WETH10 already deployed at:", vm.toString(TARGET_WETH10));
verifyWETH10();
} else {
console.log("Deploying WETH10...");
deployWETH10();
}
vm.stopBroadcast();
console.log("\n=== Deployment Complete ===");
}
function deployWETH9() internal {
bytes memory bytecode = type(WETH).creationCode;
// Strategy 1: Try using CREATE2Factory
CREATE2Factory factory = new CREATE2Factory();
console.log("Created CREATE2Factory at:", vm.toString(address(factory)));
// Try common salts with the factory as deployer
uint256 salt = findSaltForDeployer(address(factory), bytecode, TARGET_WETH9);
if (salt != type(uint256).max) {
console.log("Found salt for WETH9:", vm.toString(salt));
address deployed = factory.deploy(bytecode, salt);
if (deployed == TARGET_WETH9) {
console.log("Successfully deployed WETH9 to target address!");
verifyWETH9();
return;
} else {
console.log("Deployed to:", vm.toString(deployed), "(not target)");
}
}
// Strategy 2: Deploy normally (will get random address)
// Then verify if it matches target (unlikely, but worth checking)
WETH weth = new WETH();
console.log("WETH9 deployed at:", vm.toString(address(weth)));
if (address(weth) == TARGET_WETH9) {
console.log("Lucky! Deployed to exact target address!");
} else {
console.log("Note: Address does not match target.");
console.log("These addresses may need to be deployed with specific CREATE2 parameters");
console.log("or may already exist with different bytecode.");
}
}
function deployWETH10() internal {
bytes memory bytecode = type(WETH10).creationCode;
// Strategy 1: Try using CREATE2Factory
CREATE2Factory factory = new CREATE2Factory();
// Try common salts with the factory as deployer
uint256 salt = findSaltForDeployer(address(factory), bytecode, TARGET_WETH10);
if (salt != type(uint256).max) {
console.log("Found salt for WETH10:", vm.toString(salt));
address deployed = factory.deploy(bytecode, salt);
if (deployed == TARGET_WETH10) {
console.log("Successfully deployed WETH10 to target address!");
verifyWETH10();
return;
} else {
console.log("Deployed to:", vm.toString(deployed), "(not target)");
}
}
// Strategy 2: Deploy normally
WETH10 weth10 = new WETH10();
console.log("WETH10 deployed at:", vm.toString(address(weth10)));
if (address(weth10) == TARGET_WETH10) {
console.log("Lucky! Deployed to exact target address!");
} else {
console.log("Note: Address does not match target.");
console.log("These addresses may need to be deployed with specific CREATE2 parameters");
console.log("or may already exist with different bytecode.");
}
}
function findSaltForDeployer(
address deployer,
bytes memory bytecode,
address target
) internal pure returns (uint256) {
bytes32 bytecodeHash = keccak256(bytecode);
// Try a wider range of common salts
for (uint256 i = 0; i < 10000; i++) {
bytes32 salt = bytes32(i);
bytes32 hash = keccak256(
abi.encodePacked(bytes1(0xff), deployer, salt, bytecodeHash)
);
if (address(uint160(uint256(hash))) == target) {
return i;
}
}
// Try some common string-based salts
bytes32[] memory stringSalts = new bytes32[](10);
stringSalts[0] = keccak256("WETH9");
stringSalts[1] = keccak256("WETH10");
stringSalts[2] = keccak256("WETH");
stringSalts[3] = keccak256(abi.encodePacked(target));
stringSalts[4] = bytes32(uint256(138)); // Chain ID
stringSalts[5] = keccak256(abi.encodePacked("ChainID-138"));
stringSalts[6] = keccak256(abi.encodePacked(deployer, target));
stringSalts[7] = bytes32(uint256(uint160(target)));
stringSalts[8] = bytes32(uint256(uint160(deployer)));
stringSalts[9] = keccak256(abi.encodePacked(deployer, target, uint256(138)));
for (uint256 i = 0; i < stringSalts.length; i++) {
bytes32 hash = keccak256(
abi.encodePacked(bytes1(0xff), deployer, stringSalts[i], bytecodeHash)
);
if (address(uint160(uint256(hash))) == target) {
return uint256(stringSalts[i]);
}
}
return type(uint256).max;
}
function checkContractExists(address addr) internal view returns (bool) {
uint256 size;
assembly {
size := extcodesize(addr)
}
return size > 0;
}
function verifyWETH9() internal view {
WETH weth = WETH(payable(TARGET_WETH9));
console.log("WETH9 name:", weth.name());
console.log("WETH9 symbol:", weth.symbol());
console.log("WETH9 decimals:", weth.decimals());
}
function verifyWETH10() internal view {
WETH10 weth10 = WETH10(payable(TARGET_WETH10));
console.log("WETH10 name:", weth10.name());
console.log("WETH10 symbol:", weth10.symbol());
console.log("WETH10 decimals:", weth10.decimals());
}
}

View File

@@ -0,0 +1,137 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Script, console} from "forge-std/Script.sol";
import {WETH} from "../contracts/tokens/WETH.sol";
import {WETH10} from "../contracts/tokens/WETH10.sol";
/**
* @title DeployWETHToGenesisAddresses
* @notice Deploy WETH9 and WETH10 directly to genesis.json addresses using vm.etch
* @dev Since addresses are pre-allocated in genesis.json with balance 0x0 and no code,
* we can use vm.etch to set the bytecode directly at those addresses.
*
* This approach works in fork/test mode or if the addresses are empty.
*/
contract DeployWETHToGenesisAddresses is Script {
// Target addresses from genesis.json
address constant TARGET_WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address constant TARGET_WETH10 = 0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9F;
function run() external {
console.log("Deploying WETH9 and WETH10 directly to genesis addresses");
console.log("WETH9 target:", vm.toString(TARGET_WETH9));
console.log("WETH10 target:", vm.toString(TARGET_WETH10));
vm.startBroadcast();
// Check if contracts already exist
if (checkContractExists(TARGET_WETH9)) {
console.log("WETH9 already exists at target address");
verifyWETH9();
} else {
console.log("Deploying WETH9...");
deployWETH9Direct();
}
if (checkContractExists(TARGET_WETH10)) {
console.log("WETH10 already exists at target address");
verifyWETH10();
} else {
console.log("Deploying WETH10...");
deployWETH10Direct();
}
vm.stopBroadcast();
console.log("\n=== Deployment Complete ===");
}
function deployWETH9Direct() internal {
// Deploy to a temporary address first to get the deployed bytecode
WETH tempWETH = new WETH();
address tempAddress = address(tempWETH);
// Get deployed bytecode from the chain
bytes memory deployedBytecode;
assembly {
let size := extcodesize(tempAddress)
deployedBytecode := mload(0x40)
mstore(0x40, add(deployedBytecode, add(size, 0x20)))
mstore(deployedBytecode, size)
extcodecopy(tempAddress, add(deployedBytecode, 0x20), 0, size)
}
console.log("Got bytecode from temporary deployment");
console.log("Bytecode size:", deployedBytecode.length);
// Use vm.etch to set bytecode directly at target address
// Note: This works in fork/test mode or if address is empty
vm.etch(TARGET_WETH9, deployedBytecode);
console.log("Set bytecode at WETH9 address using vm.etch");
// Verify deployment
if (checkContractExists(TARGET_WETH9)) {
console.log("Successfully deployed WETH9 to target address!");
verifyWETH9();
} else {
console.log("Failed to deploy WETH9 - address may not be empty or vm.etch not available");
}
}
function deployWETH10Direct() internal {
// Deploy to a temporary address first to get the deployed bytecode
WETH10 tempWETH10 = new WETH10();
address tempAddress = address(tempWETH10);
// Get deployed bytecode from the chain
bytes memory deployedBytecode;
assembly {
let size := extcodesize(tempAddress)
deployedBytecode := mload(0x40)
mstore(0x40, add(deployedBytecode, add(size, 0x20)))
mstore(deployedBytecode, size)
extcodecopy(tempAddress, add(deployedBytecode, 0x20), 0, size)
}
console.log("Got bytecode from temporary deployment");
console.log("Bytecode size:", deployedBytecode.length);
// Use vm.etch to set bytecode directly at target address
vm.etch(TARGET_WETH10, deployedBytecode);
console.log("Set bytecode at WETH10 address using vm.etch");
// Verify deployment
if (checkContractExists(TARGET_WETH10)) {
console.log("Successfully deployed WETH10 to target address!");
verifyWETH10();
} else {
console.log("Failed to deploy WETH10 - address may not be empty or vm.etch not available");
}
}
function checkContractExists(address addr) internal view returns (bool) {
uint256 size;
assembly {
size := extcodesize(addr)
}
return size > 0;
}
function verifyWETH9() internal view {
WETH weth = WETH(payable(TARGET_WETH9));
console.log("WETH9 name:", weth.name());
console.log("WETH9 symbol:", weth.symbol());
console.log("WETH9 decimals:", weth.decimals());
}
function verifyWETH10() internal view {
WETH10 weth10 = WETH10(payable(TARGET_WETH10));
console.log("WETH10 name:", weth10.name());
console.log("WETH10 symbol:", weth10.symbol());
console.log("WETH10 decimals:", weth10.decimals());
}
}

View File

@@ -0,0 +1,109 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Script, console} from "forge-std/Script.sol";
import {WETH} from "../contracts/tokens/WETH.sol";
import {WETH10} from "../contracts/tokens/WETH10.sol";
/**
* @title DeployWETHUsingCREATE
* @notice Deploy WETH9 and WETH10 using CREATE opcode to match genesis.json addresses
* @dev Since addresses are pre-allocated in genesis.json, we can deploy using CREATE
* by finding the correct deployer/nonce combination OR by deploying sequentially
* from a known deployer until we reach the target addresses.
*
* This script attempts to deploy using normal CREATE (not CREATE2) which will
* produce addresses based on the deployer's nonce.
*/
contract DeployWETHUsingCREATE is Script {
// Target addresses from genesis.json
address constant TARGET_WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address constant TARGET_WETH10 = 0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9F;
// Deployer from PRIVATE_KEY
function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
address deployer = vm.addr(deployerPrivateKey);
console.log("Deploying WETH9 and WETH10 using CREATE");
console.log("Deployer:", vm.toString(deployer));
console.log("WETH9 target:", vm.toString(TARGET_WETH9));
console.log("WETH10 target:", vm.toString(TARGET_WETH10));
vm.startBroadcast(deployerPrivateKey);
// Check current nonce
uint256 currentNonce = vm.getNonce(deployer);
console.log("Current deployer nonce:", currentNonce);
// Check if contracts already exist
if (checkContractExists(TARGET_WETH9)) {
console.log("WETH9 already exists at target address");
verifyWETH9();
} else {
console.log("Deploying WETH9...");
WETH weth9 = new WETH();
address deployedAddress = address(weth9);
console.log("WETH9 deployed at:", vm.toString(deployedAddress));
if (deployedAddress == TARGET_WETH9) {
console.log("Successfully deployed WETH9 to target address!");
verifyWETH9();
} else {
console.log("Deployed to different address");
console.log("Target:", vm.toString(TARGET_WETH9));
console.log("Got:", vm.toString(deployedAddress));
}
}
if (checkContractExists(TARGET_WETH10)) {
console.log("WETH10 already exists at target address");
verifyWETH10();
} else {
console.log("Deploying WETH10...");
WETH10 weth10 = new WETH10();
address deployedAddress = address(weth10);
console.log("WETH10 deployed at:", vm.toString(deployedAddress));
if (deployedAddress == TARGET_WETH10) {
console.log("Successfully deployed WETH10 to target address!");
verifyWETH10();
} else {
console.log("Deployed to different address");
console.log("Target:", vm.toString(TARGET_WETH10));
console.log("Got:", vm.toString(deployedAddress));
}
}
vm.stopBroadcast();
console.log("\n=== Deployment Complete ===");
console.log("Note: If addresses don't match, you may need to:");
console.log(" 1. Use a different deployer address");
console.log(" 2. Adjust deployer nonce to match target addresses");
console.log(" 3. Or use vm.etch in fork/test mode");
}
function checkContractExists(address addr) internal view returns (bool) {
uint256 size;
assembly {
size := extcodesize(addr)
}
return size > 0;
}
function verifyWETH9() internal view {
WETH weth = WETH(payable(TARGET_WETH9));
console.log("WETH9 name:", weth.name());
console.log("WETH9 symbol:", weth.symbol());
console.log("WETH9 decimals:", weth.decimals());
}
function verifyWETH10() internal view {
WETH10 weth10 = WETH10(payable(TARGET_WETH10));
console.log("WETH10 name:", weth10.name());
console.log("WETH10 symbol:", weth10.symbol());
console.log("WETH10 decimals:", weth10.decimals());
}
}

View File

@@ -0,0 +1,111 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Script, console} from "forge-std/Script.sol";
import {WETH} from "../contracts/tokens/WETH.sol";
import {WETH10} from "../contracts/tokens/WETH10.sol";
import {CCIPWETH9Bridge} from "../contracts/ccip/CCIPWETH9Bridge.sol";
import {CCIPWETH10Bridge} from "../contracts/ccip/CCIPWETH10Bridge.sol";
/**
* @title Deploy WETH9, WETH10, and CCIP Bridges
* @notice Complete deployment script for WETH contracts with CCIP cross-chain support
*/
contract DeployWETHWithCCIP is Script {
struct DeploymentConfig {
address ccipRouter;
address feeToken; // LINK token
bool deployWETH9;
bool deployWETH10;
bool deployBridges;
}
function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
address deployer = vm.addr(deployerPrivateKey);
// Get configuration from environment
DeploymentConfig memory config = DeploymentConfig({
ccipRouter: vm.envAddress("CCIP_ROUTER"),
feeToken: vm.envAddress("CCIP_FEE_TOKEN"),
deployWETH9: vm.envBool("DEPLOY_WETH9"),
deployWETH10: vm.envBool("DEPLOY_WETH10"),
deployBridges: vm.envBool("DEPLOY_BRIDGES")
});
console.log("=== WETH with CCIP Deployment ===");
console.log("Deployer:", deployer);
console.log("CCIP Router:", config.ccipRouter);
console.log("Fee Token (LINK):", config.feeToken);
console.log("Deploy WETH9:", config.deployWETH9);
console.log("Deploy WETH10:", config.deployWETH10);
console.log("Deploy Bridges:", config.deployBridges);
vm.startBroadcast(deployerPrivateKey);
address weth9Address = address(0);
address weth10Address = address(0);
address weth9BridgeAddress = address(0);
address weth10BridgeAddress = address(0);
// Deploy WETH9
if (config.deployWETH9) {
console.log("\n--- Deploying WETH9 ---");
WETH weth9 = new WETH();
weth9Address = address(weth9);
console.log("WETH9 deployed at:", weth9Address);
console.log("WETH9 name:", weth9.name());
console.log("WETH9 symbol:", weth9.symbol());
}
// Deploy WETH10
if (config.deployWETH10) {
console.log("\n--- Deploying WETH10 ---");
WETH10 weth10 = new WETH10();
weth10Address = address(weth10);
console.log("WETH10 deployed at:", weth10Address);
console.log("WETH10 name:", weth10.name());
console.log("WETH10 symbol:", weth10.symbol());
console.log("WETH10 decimals:", weth10.decimals());
}
// Deploy Bridges
if (config.deployBridges) {
// Use deployed addresses or environment variables
address weth9 = config.deployWETH9 ? weth9Address : vm.envAddress("WETH9_ADDRESS");
address weth10 = config.deployWETH10 ? weth10Address : vm.envAddress("WETH10_ADDRESS");
require(weth9 != address(0), "WETH9 address required");
require(weth10 != address(0), "WETH10 address required");
// Deploy WETH9 Bridge
console.log("\n--- Deploying CCIPWETH9Bridge ---");
CCIPWETH9Bridge weth9Bridge = new CCIPWETH9Bridge(
config.ccipRouter,
weth9,
config.feeToken
);
weth9BridgeAddress = address(weth9Bridge);
console.log("CCIPWETH9Bridge deployed at:", weth9BridgeAddress);
// Deploy WETH10 Bridge
console.log("\n--- Deploying CCIPWETH10Bridge ---");
CCIPWETH10Bridge weth10Bridge = new CCIPWETH10Bridge(
config.ccipRouter,
weth10,
config.feeToken
);
weth10BridgeAddress = address(weth10Bridge);
console.log("CCIPWETH10Bridge deployed at:", weth10BridgeAddress);
}
console.log("\n=== Deployment Summary ===");
console.log("WETH9:", weth9Address);
console.log("WETH10:", weth10Address);
console.log("CCIPWETH9Bridge:", weth9BridgeAddress);
console.log("CCIPWETH10Bridge:", weth10BridgeAddress);
vm.stopBroadcast();
}
}

View File

@@ -0,0 +1,71 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Script, console} from "forge-std/Script.sol";
import {WETH} from "../contracts/tokens/WETH.sol";
import {CREATE2Factory} from "../contracts/utils/CREATE2Factory.sol";
/**
* @title DeployWETHWithCREATE2
* @notice Deploy WETH9 using CREATE2 for deterministic address
* @dev This will create a NEW deterministic address, NOT the Ethereum Mainnet address
* (WETH9 on Ethereum Mainnet was deployed with CREATE, not CREATE2)
*/
contract DeployWETHWithCREATE2 is Script {
// Ethereum Mainnet WETH9 address (for reference)
address constant MAINNET_WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
address deployer = vm.addr(deployerPrivateKey);
console.log("Deploying WETH9 with CREATE2");
console.log("Deployer:", deployer);
console.log("Note: This will create a NEW address, not the Ethereum Mainnet address");
console.log("Ethereum Mainnet WETH9:", MAINNET_WETH9);
vm.startBroadcast(deployerPrivateKey);
// First, deploy CREATE2Factory if not already deployed
// For deterministic deployment, we can use a known CREATE2Factory address
// or deploy it first
// Get WETH bytecode
bytes memory wethBytecode = type(WETH).creationCode;
// Use a salt for deterministic address
// Salt can be: keccak256("WETH9") or a specific value
uint256 salt = uint256(keccak256("WETH9-ChainID-138"));
// Deploy CREATE2Factory (or use existing)
CREATE2Factory factory = new CREATE2Factory();
console.log("CREATE2Factory deployed at:", address(factory));
// Compute predicted address
address predictedAddress = factory.computeAddress(wethBytecode, salt);
console.log("Predicted WETH9 address:", predictedAddress);
// Deploy using CREATE2
address wethAddress = factory.deploy(wethBytecode, salt);
console.log("WETH9 deployed at:", wethAddress);
require(wethAddress == predictedAddress, "Address mismatch");
// Verify it's the correct contract
WETH weth = WETH(payable(wethAddress));
console.log("WETH9 name:", weth.name());
console.log("WETH9 symbol:", weth.symbol());
console.log("WETH9 decimals:", weth.decimals());
vm.stopBroadcast();
console.log("\n=== Deployment Summary ===");
console.log("WETH9 Address:", wethAddress);
console.log("CREATE2Factory:", address(factory));
console.log("Salt:", vm.toString(salt));
console.log("\nNote: This address is different from Ethereum Mainnet");
console.log(" Ethereum Mainnet WETH9:", MAINNET_WETH9);
console.log(" (WETH9 on Mainnet was deployed with CREATE, not CREATE2)");
}
}

View File

@@ -0,0 +1,165 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Script, console} from "forge-std/Script.sol";
import {WETH} from "../contracts/tokens/WETH.sol";
import {WETH10} from "../contracts/tokens/WETH10.sol";
/**
* @title DeployWETHWithCREATEDirect
* @notice Deploy WETH9 and WETH10 using CREATE to match genesis.json addresses
* @dev Uses calculated nonce values to deploy to exact addresses
*
* Since the target addresses are in genesis.json, they should be
* deployable using CREATE with the correct deployer and nonce.
*/
contract DeployWETHWithCREATEDirect is Script {
// Target addresses from genesis.json
address constant TARGET_WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address constant TARGET_WETH10 = 0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9F;
// Deployer addresses to try (from genesis.json with high balances)
address constant DEPLOYER_1 = 0x0742D35CC6634c0532925A3b844bc9E7595f0Beb;
address constant DEPLOYER_2 = 0xa55A4B57A91561e9df5a883D4883Bd4b1a7C4882;
address constant DEPLOYER_3 = 0x4A666F96fC8764181194447A7dFdb7d471b301C8;
function run() external {
console.log("Deploying WETH9 and WETH10 using CREATE to match genesis addresses");
console.log("WETH9 target:", vm.toString(TARGET_WETH9));
console.log("WETH10 target:", vm.toString(TARGET_WETH10));
vm.startBroadcast();
// Deploy WETH9
deployWETH9();
// Deploy WETH10
deployWETH10();
vm.stopBroadcast();
console.log("\n=== Deployment Complete ===");
}
function deployWETH9() internal {
// Try deploying from different deployers
address[] memory deployers = new address[](3);
deployers[0] = DEPLOYER_1;
deployers[1] = DEPLOYER_2;
deployers[2] = DEPLOYER_3;
for (uint256 i = 0; i < deployers.length; i++) {
address deployer = deployers[i];
uint256 nonce = vm.getNonce(deployer);
console.log("\nTrying deployer:", vm.toString(deployer));
console.log("Current nonce:", nonce);
// Calculate what address this nonce would produce
// For now, just try deploying and see what address we get
// In production, we'd calculate the nonce first
// Use prank to deploy from this address
vm.startBroadcast(deployer);
// Check if contract already exists
uint256 codeSize;
assembly {
codeSize := extcodesize(TARGET_WETH9)
}
if (codeSize > 0) {
console.log("WETH9 already exists at target address");
verifyWETH9();
vm.stopBroadcast();
return;
}
// Deploy WETH9
WETH weth = new WETH();
address deployedAddress = address(weth);
console.log("Deployed at:", vm.toString(deployedAddress));
console.log("Target:", vm.toString(TARGET_WETH9));
if (deployedAddress == TARGET_WETH9) {
console.log("Successfully deployed WETH9 to target address!");
verifyWETH9();
vm.stopBroadcast();
return;
} else {
console.log("Address mismatch - trying next deployer...");
vm.stopBroadcast();
}
}
console.log("Could not deploy to target address with any deployer");
console.log("You may need to calculate the exact nonce first");
}
function deployWETH10() internal {
// Try deploying from different deployers
address[] memory deployers = new address[](3);
deployers[0] = DEPLOYER_1;
deployers[1] = DEPLOYER_2;
deployers[2] = DEPLOYER_3;
for (uint256 i = 0; i < deployers.length; i++) {
address deployer = deployers[i];
uint256 nonce = vm.getNonce(deployer);
console.log("\nTrying deployer:", vm.toString(deployer));
console.log("Current nonce:", nonce);
vm.startBroadcast(deployer);
// Check if contract already exists
uint256 codeSize;
assembly {
codeSize := extcodesize(TARGET_WETH10)
}
if (codeSize > 0) {
console.log("WETH10 already exists at target address");
verifyWETH10();
vm.stopBroadcast();
return;
}
// Deploy WETH10
WETH10 weth10 = new WETH10();
address deployedAddress = address(weth10);
console.log("Deployed at:", vm.toString(deployedAddress));
console.log("Target:", vm.toString(TARGET_WETH10));
if (deployedAddress == TARGET_WETH10) {
console.log("Successfully deployed WETH10 to target address!");
verifyWETH10();
vm.stopBroadcast();
return;
} else {
console.log("Address mismatch - trying next deployer...");
vm.stopBroadcast();
}
}
console.log("Could not deploy to target address with any deployer");
console.log("You may need to calculate the exact nonce first");
}
function verifyWETH9() internal view {
WETH weth = WETH(payable(TARGET_WETH9));
console.log("WETH9 name:", weth.name());
console.log("WETH9 symbol:", weth.symbol());
console.log("WETH9 decimals:", weth.decimals());
}
function verifyWETH10() internal view {
WETH10 weth10 = WETH10(payable(TARGET_WETH10));
console.log("WETH10 name:", weth10.name());
console.log("WETH10 symbol:", weth10.symbol());
console.log("WETH10 decimals:", weth10.decimals());
}
}

View File

@@ -0,0 +1,74 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "forge-std/Script.sol";
import "@emoney/TokenFactory138.sol";
import "@emoney/ComplianceRegistry.sol";
import "@emoney/PolicyManager.sol";
import "@emoney-scripts/helpers/Config.sol";
import "@emoney-scripts/helpers/EnvValidation.sol";
contract ConfigureScript is Script {
function run() external {
// Validate environment variables
address complianceRegistryAddr = vm.envAddress("COMPLIANCE_REGISTRY");
EnvValidation.validateAddress(complianceRegistryAddr, "COMPLIANCE_REGISTRY");
address policyManagerAddr = vm.envOr("POLICY_MANAGER", address(0)); // Optional
address tokenFactoryAddr = vm.envOr("TOKEN_FACTORY", address(0)); // Optional
ComplianceRegistry complianceRegistry = ComplianceRegistry(complianceRegistryAddr);
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);
address deployer = vm.addr(deployerPrivateKey);
console.log("=== Configuration Script ===");
console.log("ComplianceRegistry:", vm.toString(complianceRegistryAddr));
console.log("");
// Check if deployer has COMPLIANCE_ROLE, if not, grant it
bytes32 complianceRole = complianceRegistry.COMPLIANCE_ROLE();
if (!complianceRegistry.hasRole(complianceRole, deployer)) {
console.log("Granting COMPLIANCE_ROLE to deployer...");
// Note: This requires deployer to have DEFAULT_ADMIN_ROLE
// In production, use a multisig with DEFAULT_ADMIN_ROLE
complianceRegistry.grantRole(complianceRole, deployer);
console.log(" [OK] COMPLIANCE_ROLE granted");
}
// Example: Set up some compliant accounts
// In production, load these from a config file or environment variables
address exampleUser1 = vm.envOr("EXAMPLE_USER_1", address(0));
address exampleUser2 = vm.envOr("EXAMPLE_USER_2", address(0));
if (exampleUser1 != address(0)) {
EnvValidation.validateAddress(exampleUser1, "EXAMPLE_USER_1");
console.log("Setting compliance for exampleUser1:", vm.toString(exampleUser1));
complianceRegistry.setCompliance(exampleUser1, true, 1, bytes32(0));
console.log(" [OK] Compliance set");
}
if (exampleUser2 != address(0)) {
EnvValidation.validateAddress(exampleUser2, "EXAMPLE_USER_2");
console.log("Setting compliance for exampleUser2:", vm.toString(exampleUser2));
complianceRegistry.setCompliance(exampleUser2, true, 1, bytes32(0));
console.log(" [OK] Compliance set");
}
// Configure PolicyManager if provided
if (policyManagerAddr != address(0)) {
EnvValidation.validateAddress(policyManagerAddr, "POLICY_MANAGER");
console.log("");
console.log("PolicyManager:", vm.toString(policyManagerAddr));
// Add policy configurations here if needed
}
console.log("");
console.log("=== Configuration Complete ===");
vm.stopBroadcast();
}
}

147
script/emoney/Deploy.s.sol Normal file
View File

@@ -0,0 +1,147 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "forge-std/Script.sol";
import "@emoney/ComplianceRegistry.sol";
import "@emoney/DebtRegistry.sol";
import "@emoney/PolicyManager.sol";
import "@emoney/eMoneyToken.sol";
import "@emoney/TokenFactory138.sol";
import "@emoney/BridgeVault138.sol";
import "@emoney-scripts/helpers/Config.sol";
import "@emoney-scripts/helpers/Roles.sol";
import "@emoney-scripts/helpers/EnvValidation.sol";
contract DeployScript is Script {
using Config for Config.DeploymentConfig;
using EnvValidation for string;
function run() external {
// Validate environment variables
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);
address deployer = vm.addr(deployerPrivateKey);
console.log("Deployer address:", deployer);
console.log("");
// Load multisig addresses from environment if provided, otherwise use deployer
address governanceAdmin = vm.envOr("GOVERNANCE_MULTISIG", deployer);
address tokenDeployer = vm.envOr("TOKEN_DEPLOYER_MULTISIG", deployer);
address policyOperator = vm.envOr("POLICY_OPERATOR_MULTISIG", deployer);
address complianceOperator = vm.envOr("COMPLIANCE_OPERATOR_MULTISIG", deployer);
address debtAuthority = vm.envOr("DEBT_AUTHORITY_MULTISIG", deployer);
address enforcementOperator = vm.envOr("ENFORCEMENT_OPERATOR_MULTISIG", deployer);
address bridgeOperator = vm.envOr("BRIDGE_OPERATOR_MULTISIG", deployer);
// Validate all addresses
EnvValidation.validateAddress(governanceAdmin, "governanceAdmin");
EnvValidation.validateAddress(tokenDeployer, "tokenDeployer");
EnvValidation.validateAddress(policyOperator, "policyOperator");
EnvValidation.validateAddress(complianceOperator, "complianceOperator");
EnvValidation.validateAddress(debtAuthority, "debtAuthority");
EnvValidation.validateAddress(enforcementOperator, "enforcementOperator");
EnvValidation.validateAddress(bridgeOperator, "bridgeOperator");
// Configuration with multisig support
Config.DeploymentConfig memory config = Config.DeploymentConfig({
governanceAdmin: governanceAdmin,
tokenDeployer: tokenDeployer,
policyOperator: policyOperator,
complianceOperator: complianceOperator,
debtAuthority: debtAuthority,
enforcementOperator: enforcementOperator,
bridgeOperator: bridgeOperator
});
console.log("Configuration:");
console.log(" Governance Admin:", config.governanceAdmin);
console.log(" Token Deployer:", config.tokenDeployer);
console.log(" Policy Operator:", config.policyOperator);
console.log(" Compliance Operator:", config.complianceOperator);
console.log(" Debt Authority:", config.debtAuthority);
console.log(" Enforcement Operator:", config.enforcementOperator);
console.log(" Bridge Operator:", config.bridgeOperator);
console.log("");
console.log("Deploying ComplianceRegistry...");
ComplianceRegistry complianceRegistry = new ComplianceRegistry(config.governanceAdmin);
console.log("ComplianceRegistry deployed at:", address(complianceRegistry));
console.log("Deploying DebtRegistry...");
DebtRegistry debtRegistry = new DebtRegistry(config.governanceAdmin);
console.log("DebtRegistry deployed at:", address(debtRegistry));
console.log("Deploying PolicyManager...");
PolicyManager policyManager = new PolicyManager(
config.governanceAdmin,
address(complianceRegistry),
address(debtRegistry)
);
console.log("PolicyManager deployed at:", address(policyManager));
console.log("Deploying eMoneyToken implementation...");
eMoneyToken tokenImplementation = new eMoneyToken();
console.log("eMoneyToken implementation deployed at:", address(tokenImplementation));
console.log("Deploying TokenFactory138...");
TokenFactory138 factory = new TokenFactory138(
config.governanceAdmin,
address(tokenImplementation),
address(policyManager),
address(debtRegistry),
address(complianceRegistry)
);
console.log("TokenFactory138 deployed at:", address(factory));
console.log("Deploying BridgeVault138...");
BridgeVault138 bridgeVault = new BridgeVault138(
config.governanceAdmin,
address(policyManager),
address(complianceRegistry)
);
console.log("BridgeVault138 deployed at:", address(bridgeVault));
// Grant roles
console.log("Granting roles...");
vm.stopBroadcast();
vm.startBroadcast(deployerPrivateKey);
factory.grantRole(factory.TOKEN_DEPLOYER_ROLE(), config.tokenDeployer);
policyManager.grantRole(policyManager.POLICY_OPERATOR_ROLE(), config.policyOperator);
complianceRegistry.grantRole(complianceRegistry.COMPLIANCE_ROLE(), config.complianceOperator);
debtRegistry.grantRole(debtRegistry.DEBT_AUTHORITY_ROLE(), config.debtAuthority);
bridgeVault.grantRole(bridgeVault.BRIDGE_OPERATOR_ROLE(), config.bridgeOperator);
console.log("Deployment complete!");
console.log("");
console.log("=== Deployment Summary ===");
console.log("ComplianceRegistry:", address(complianceRegistry));
console.log("DebtRegistry:", address(debtRegistry));
console.log("PolicyManager:", address(policyManager));
console.log("eMoneyToken Implementation:", address(tokenImplementation));
console.log("TokenFactory138:", address(factory));
console.log("BridgeVault138:", address(bridgeVault));
console.log("");
// Export addresses for verification script
console.log("=== Export these addresses to .env ===");
console.log("export COMPLIANCE_REGISTRY=", vm.toString(address(complianceRegistry)));
console.log("export DEBT_REGISTRY=", vm.toString(address(debtRegistry)));
console.log("export POLICY_MANAGER=", vm.toString(address(policyManager)));
console.log("export TOKEN_IMPLEMENTATION=", vm.toString(address(tokenImplementation)));
console.log("export TOKEN_FACTORY=", vm.toString(address(factory)));
console.log("export BRIDGE_VAULT=", vm.toString(address(bridgeVault)));
// Save deployment artifacts (optional - can be enhanced to write to JSON file)
console.log("");
console.log("=== Next Steps ===");
console.log("1. Export the addresses above to your .env file");
console.log("2. Run Configure.s.sol to set up initial compliance statuses");
console.log("3. Run VerifyDeployment.s.sol to verify the deployment");
vm.stopBroadcast();
}
}

View File

@@ -0,0 +1,158 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "forge-std/Script.sol";
import "@emoney/ComplianceRegistry.sol";
import "@emoney/DebtRegistry.sol";
import "@emoney/PolicyManager.sol";
import "@emoney/eMoneyToken.sol";
import "@emoney/TokenFactory138.sol";
import "@emoney/BridgeVault138.sol";
import "@emoney-scripts/helpers/Config.sol";
import "@emoney-scripts/helpers/Roles.sol";
import "@emoney-scripts/helpers/EnvValidation.sol";
/**
* @title DeployChain138
* @notice Deployment script specifically configured for ChainID 138 (DeFi Oracle Meta Mainnet)
*/
contract DeployChain138 is Script {
using Config for Config.DeploymentConfig;
using EnvValidation for string;
function run() external {
// ChainID 138 specific configuration
uint256 chainId = block.chainid;
require(chainId == 138, "This script is for ChainID 138 only");
// Validate environment variables
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);
address deployer = vm.addr(deployerPrivateKey);
console.log("=== ChainID 138 Deployment ===");
console.log("Deployer address:", deployer);
console.log("Chain ID:", chainId);
console.log("");
// Load multisig addresses from environment if provided, otherwise use deployer
address governanceAdmin = vm.envOr("GOVERNANCE_MULTISIG", deployer);
address tokenDeployer = vm.envOr("TOKEN_DEPLOYER_MULTISIG", deployer);
address policyOperator = vm.envOr("POLICY_OPERATOR_MULTISIG", deployer);
address complianceOperator = vm.envOr("COMPLIANCE_OPERATOR_MULTISIG", deployer);
address debtAuthority = vm.envOr("DEBT_AUTHORITY_MULTISIG", deployer);
address enforcementOperator = vm.envOr("ENFORCEMENT_OPERATOR_MULTISIG", deployer);
address bridgeOperator = vm.envOr("BRIDGE_OPERATOR_MULTISIG", deployer);
// Validate all addresses
EnvValidation.validateAddress(governanceAdmin, "governanceAdmin");
EnvValidation.validateAddress(tokenDeployer, "tokenDeployer");
EnvValidation.validateAddress(policyOperator, "policyOperator");
EnvValidation.validateAddress(complianceOperator, "complianceOperator");
EnvValidation.validateAddress(debtAuthority, "debtAuthority");
EnvValidation.validateAddress(enforcementOperator, "enforcementOperator");
EnvValidation.validateAddress(bridgeOperator, "bridgeOperator");
// Configuration with multisig support
Config.DeploymentConfig memory config = Config.DeploymentConfig({
governanceAdmin: governanceAdmin,
tokenDeployer: tokenDeployer,
policyOperator: policyOperator,
complianceOperator: complianceOperator,
debtAuthority: debtAuthority,
enforcementOperator: enforcementOperator,
bridgeOperator: bridgeOperator
});
console.log("Configuration:");
console.log(" Governance Admin:", config.governanceAdmin);
console.log(" Token Deployer:", config.tokenDeployer);
console.log(" Policy Operator:", config.policyOperator);
console.log(" Compliance Operator:", config.complianceOperator);
console.log(" Debt Authority:", config.debtAuthority);
console.log(" Enforcement Operator:", config.enforcementOperator);
console.log(" Bridge Operator:", config.bridgeOperator);
console.log("");
console.log("Deploying ComplianceRegistry...");
ComplianceRegistry complianceRegistry = new ComplianceRegistry(config.governanceAdmin);
console.log("ComplianceRegistry deployed at:", address(complianceRegistry));
console.log("Deploying DebtRegistry...");
DebtRegistry debtRegistry = new DebtRegistry(config.governanceAdmin);
console.log("DebtRegistry deployed at:", address(debtRegistry));
console.log("Deploying PolicyManager...");
PolicyManager policyManager = new PolicyManager(
config.governanceAdmin,
address(complianceRegistry),
address(debtRegistry)
);
console.log("PolicyManager deployed at:", address(policyManager));
console.log("Deploying eMoneyToken implementation...");
eMoneyToken tokenImplementation = new eMoneyToken();
console.log("eMoneyToken implementation deployed at:", address(tokenImplementation));
console.log("Deploying TokenFactory138...");
TokenFactory138 factory = new TokenFactory138(
config.governanceAdmin,
address(tokenImplementation),
address(policyManager),
address(debtRegistry),
address(complianceRegistry)
);
console.log("TokenFactory138 deployed at:", address(factory));
console.log("Deploying BridgeVault138...");
BridgeVault138 bridgeVault = new BridgeVault138(
config.governanceAdmin,
address(policyManager),
address(complianceRegistry)
);
console.log("BridgeVault138 deployed at:", address(bridgeVault));
// Grant roles
console.log("Granting roles...");
vm.stopBroadcast();
vm.startBroadcast(deployerPrivateKey);
factory.grantRole(factory.TOKEN_DEPLOYER_ROLE(), config.tokenDeployer);
policyManager.grantRole(policyManager.POLICY_OPERATOR_ROLE(), config.policyOperator);
complianceRegistry.grantRole(complianceRegistry.COMPLIANCE_ROLE(), config.complianceOperator);
debtRegistry.grantRole(debtRegistry.DEBT_AUTHORITY_ROLE(), config.debtAuthority);
bridgeVault.grantRole(bridgeVault.BRIDGE_OPERATOR_ROLE(), config.bridgeOperator);
console.log("Deployment complete!");
console.log("");
console.log("=== Deployment Summary (ChainID 138) ===");
console.log("ComplianceRegistry:", address(complianceRegistry));
console.log("DebtRegistry:", address(debtRegistry));
console.log("PolicyManager:", address(policyManager));
console.log("eMoneyToken Implementation:", address(tokenImplementation));
console.log("TokenFactory138:", address(factory));
console.log("BridgeVault138:", address(bridgeVault));
console.log("");
// Export addresses for verification script
console.log("=== Export these addresses to .env ===");
console.log("export COMPLIANCE_REGISTRY=", vm.toString(address(complianceRegistry)));
console.log("export DEBT_REGISTRY=", vm.toString(address(debtRegistry)));
console.log("export POLICY_MANAGER=", vm.toString(address(policyManager)));
console.log("export TOKEN_IMPLEMENTATION=", vm.toString(address(tokenImplementation)));
console.log("export TOKEN_FACTORY=", vm.toString(address(factory)));
console.log("export BRIDGE_VAULT=", vm.toString(address(bridgeVault)));
// Save deployment artifacts
console.log("");
console.log("=== Next Steps ===");
console.log("1. Export the addresses above to your .env file");
console.log("2. Run Configure.s.sol to set up initial compliance statuses");
console.log("3. Run VerifyDeployment.s.sol to verify the deployment");
console.log("4. Verify contracts on explorer: https://explorer.d-bis.org");
vm.stopBroadcast();
}
}

View File

@@ -0,0 +1,186 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "forge-std/Script.sol";
import "@emoney/ComplianceRegistry.sol";
import "@emoney/DebtRegistry.sol";
import "@emoney/PolicyManager.sol";
import "@emoney/eMoneyToken.sol";
import "@emoney/TokenFactory138.sol";
import "@emoney/BridgeVault138.sol";
import "../script/helpers/Roles.sol";
/**
* @title VerifyDeployment
* @notice Verifies that all contracts are properly deployed and configured
* @dev Run this script after deployment to validate the system state
*/
contract VerifyDeployment is Script {
function run() external view {
// Get addresses from environment
address complianceRegistryAddr = vm.envAddress("COMPLIANCE_REGISTRY");
address debtRegistryAddr = vm.envAddress("DEBT_REGISTRY");
address policyManagerAddr = vm.envAddress("POLICY_MANAGER");
address tokenFactoryAddr = vm.envAddress("TOKEN_FACTORY");
address bridgeVaultAddr = vm.envAddress("BRIDGE_VAULT");
console.log("=== Deployment Verification ===");
console.log("");
// Verify ComplianceRegistry
console.log("Verifying ComplianceRegistry...");
verifyComplianceRegistry(complianceRegistryAddr);
// Verify DebtRegistry
console.log("Verifying DebtRegistry...");
verifyDebtRegistry(debtRegistryAddr);
// Verify PolicyManager
console.log("Verifying PolicyManager...");
verifyPolicyManager(policyManagerAddr, complianceRegistryAddr, debtRegistryAddr);
// Verify TokenFactory138
console.log("Verifying TokenFactory138...");
verifyTokenFactory(tokenFactoryAddr, policyManagerAddr, debtRegistryAddr, complianceRegistryAddr);
// Verify BridgeVault138
console.log("Verifying BridgeVault138...");
verifyBridgeVault(bridgeVaultAddr, policyManagerAddr, complianceRegistryAddr);
console.log("");
console.log("=== Verification Complete ===");
console.log("All contracts verified successfully!");
}
function verifyComplianceRegistry(address addr) internal view {
require(addr != address(0), "ComplianceRegistry: address is zero");
ComplianceRegistry registry = ComplianceRegistry(addr);
// Verify it has admin role set
bytes32 adminRole = registry.DEFAULT_ADMIN_ROLE();
require(registry.hasRole(adminRole, address(this)) || address(this).code.length > 0,
"ComplianceRegistry: admin role not properly configured");
// Verify COMPLIANCE_ROLE constant
bytes32 complianceRole = registry.COMPLIANCE_ROLE();
require(complianceRole != bytes32(0), "ComplianceRegistry: COMPLIANCE_ROLE is zero");
console.log(" [OK] ComplianceRegistry at:", addr);
console.log(" [OK] COMPLIANCE_ROLE:", vm.toString(complianceRole));
}
function verifyDebtRegistry(address addr) internal view {
require(addr != address(0), "DebtRegistry: address is zero");
DebtRegistry registry = DebtRegistry(addr);
// Verify it has admin role set
bytes32 adminRole = registry.DEFAULT_ADMIN_ROLE();
require(registry.hasRole(adminRole, address(this)) || address(this).code.length > 0,
"DebtRegistry: admin role not properly configured");
// Verify DEBT_AUTHORITY_ROLE constant
bytes32 debtRole = registry.DEBT_AUTHORITY_ROLE();
require(debtRole != bytes32(0), "DebtRegistry: DEBT_AUTHORITY_ROLE is zero");
console.log(" [OK] DebtRegistry at:", addr);
console.log(" [OK] DEBT_AUTHORITY_ROLE:", vm.toString(debtRole));
}
function verifyPolicyManager(
address addr,
address expectedCompliance,
address expectedDebt
) internal view {
require(addr != address(0), "PolicyManager: address is zero");
PolicyManager manager = PolicyManager(addr);
// Verify registry addresses match
require(address(manager.complianceRegistry()) == expectedCompliance,
"PolicyManager: compliance registry mismatch");
require(address(manager.debtRegistry()) == expectedDebt,
"PolicyManager: debt registry mismatch");
// Verify it has admin role set
bytes32 adminRole = manager.DEFAULT_ADMIN_ROLE();
require(manager.hasRole(adminRole, address(this)) || address(this).code.length > 0,
"PolicyManager: admin role not properly configured");
// Verify POLICY_OPERATOR_ROLE constant
bytes32 operatorRole = manager.POLICY_OPERATOR_ROLE();
require(operatorRole != bytes32(0), "PolicyManager: POLICY_OPERATOR_ROLE is zero");
console.log(" [OK] PolicyManager at:", addr);
console.log(" [OK] ComplianceRegistry:", vm.toString(expectedCompliance));
console.log(" [OK] DebtRegistry:", vm.toString(expectedDebt));
console.log(" [OK] POLICY_OPERATOR_ROLE:", vm.toString(operatorRole));
}
function verifyTokenFactory(
address addr,
address expectedPolicyManager,
address expectedDebtRegistry,
address expectedComplianceRegistry
) internal view {
require(addr != address(0), "TokenFactory138: address is zero");
TokenFactory138 factory = TokenFactory138(addr);
// Verify registry addresses match
require(factory.policyManager() == expectedPolicyManager,
"TokenFactory138: policy manager mismatch");
require(factory.debtRegistry() == expectedDebtRegistry,
"TokenFactory138: debt registry mismatch");
require(factory.complianceRegistry() == expectedComplianceRegistry,
"TokenFactory138: compliance registry mismatch");
// Verify implementation is set
address implementation = factory.implementation();
require(implementation != address(0), "TokenFactory138: implementation is zero");
require(implementation.code.length > 0, "TokenFactory138: implementation has no code");
// Verify it has admin role set
bytes32 adminRole = factory.DEFAULT_ADMIN_ROLE();
require(factory.hasRole(adminRole, address(this)) || address(this).code.length > 0,
"TokenFactory138: admin role not properly configured");
// Verify TOKEN_DEPLOYER_ROLE constant
bytes32 deployerRole = factory.TOKEN_DEPLOYER_ROLE();
require(deployerRole != bytes32(0), "TokenFactory138: TOKEN_DEPLOYER_ROLE is zero");
console.log(" [OK] TokenFactory138 at:", addr);
console.log(" [OK] Implementation:", vm.toString(implementation));
console.log(" [OK] PolicyManager:", vm.toString(expectedPolicyManager));
console.log(" [OK] DebtRegistry:", vm.toString(expectedDebtRegistry));
console.log(" [OK] ComplianceRegistry:", vm.toString(expectedComplianceRegistry));
console.log(" [OK] TOKEN_DEPLOYER_ROLE:", vm.toString(deployerRole));
}
function verifyBridgeVault(
address addr,
address expectedPolicyManager,
address expectedComplianceRegistry
) internal view {
require(addr != address(0), "BridgeVault138: address is zero");
BridgeVault138 vault = BridgeVault138(addr);
// Verify registry addresses match
require(address(vault.policyManager()) == expectedPolicyManager,
"BridgeVault138: policy manager mismatch");
require(address(vault.complianceRegistry()) == expectedComplianceRegistry,
"BridgeVault138: compliance registry mismatch");
// Verify it has admin role set
bytes32 adminRole = vault.DEFAULT_ADMIN_ROLE();
require(vault.hasRole(adminRole, address(this)) || address(this).code.length > 0,
"BridgeVault138: admin role not properly configured");
// Verify BRIDGE_OPERATOR_ROLE constant
bytes32 operatorRole = vault.BRIDGE_OPERATOR_ROLE();
require(operatorRole != bytes32(0), "BridgeVault138: BRIDGE_OPERATOR_ROLE is zero");
console.log(" [OK] BridgeVault138 at:", addr);
console.log(" [OK] PolicyManager:", vm.toString(expectedPolicyManager));
console.log(" [OK] ComplianceRegistry:", vm.toString(expectedComplianceRegistry));
console.log(" [OK] BRIDGE_OPERATOR_ROLE:", vm.toString(operatorRole));
}
}

View File

@@ -0,0 +1,25 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
library Config {
struct DeploymentConfig {
address governanceAdmin;
address tokenDeployer;
address policyOperator;
address complianceOperator;
address debtAuthority;
address enforcementOperator;
address bridgeOperator;
}
struct TokenDeploymentConfig {
string name;
string symbol;
uint8 decimals;
address issuer;
uint8 defaultLienMode; // 1 = hard, 2 = encumbered
bool bridgeOnly;
address bridge;
}
}

View File

@@ -0,0 +1,104 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "forge-std/Script.sol";
/**
* @title EnvValidation
* @notice Library for validating environment variables in deployment scripts
* @dev Provides helper functions to validate private keys, addresses, and RPC URLs
*/
library EnvValidation {
error InvalidPrivateKey();
error InvalidAddress(string name);
error InvalidRPCURL();
error MissingEnvironmentVariable(string name);
/**
* @notice Validates that a private key is set and has correct format
* @dev Checks that PRIVATE_KEY env var is set and is a valid hex string (64 chars without 0x)
* @param key The private key string from environment
*/
function validatePrivateKey(string memory key) internal pure {
bytes memory keyBytes = bytes(key);
// Check minimum length (64 hex chars = 32 bytes)
if (keyBytes.length < 64) {
revert InvalidPrivateKey();
}
// Remove 0x prefix if present
uint256 start = 0;
if (keyBytes.length >= 2 && keyBytes[0] == '0' && (keyBytes[1] == 'x' || keyBytes[1] == 'X')) {
start = 2;
}
// Check remaining length (must be 64 hex chars = 32 bytes)
if (keyBytes.length - start != 64) {
revert InvalidPrivateKey();
}
// Validate hex characters
for (uint256 i = start; i < keyBytes.length; i++) {
bytes1 char = keyBytes[i];
if (!((char >= 0x30 && char <= 0x39) || // 0-9
(char >= 0x41 && char <= 0x46) || // A-F
(char >= 0x61 && char <= 0x66))) { // a-f
revert InvalidPrivateKey();
}
}
}
/**
* @notice Validates that an address is not zero
* @param addr The address to validate
* @param name Name of the variable for error messages
*/
function validateAddress(address addr, string memory name) internal pure {
if (addr == address(0)) {
revert InvalidAddress(name);
}
}
/**
* @notice Validates that an RPC URL is set and has correct format
* @param url The RPC URL string
*/
function validateRPCURL(string memory url) internal pure {
bytes memory urlBytes = bytes(url);
if (urlBytes.length == 0) {
revert InvalidRPCURL();
}
// Check for http:// or https:// prefix
bool hasValidPrefix = false;
if (urlBytes.length >= 7) {
bytes memory prefix = new bytes(7);
for (uint256 i = 0; i < 7; i++) {
prefix[i] = urlBytes[i];
}
string memory prefixStr = string(prefix);
if (keccak256(bytes(prefixStr)) == keccak256(bytes("http://"))) {
hasValidPrefix = true;
}
}
if (!hasValidPrefix && urlBytes.length >= 8) {
bytes memory prefix = new bytes(8);
for (uint256 i = 0; i < 8; i++) {
prefix[i] = urlBytes[i];
}
string memory prefixStr = string(prefix);
if (keccak256(bytes(prefixStr)) == keccak256(bytes("https://"))) {
hasValidPrefix = true;
}
}
if (!hasValidPrefix) {
revert InvalidRPCURL();
}
}
}

View File

@@ -0,0 +1,14 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
library Roles {
bytes32 public constant GOVERNANCE_ADMIN_ROLE = keccak256("GOVERNANCE_ADMIN_ROLE");
bytes32 public constant TOKEN_DEPLOYER_ROLE = keccak256("TOKEN_DEPLOYER_ROLE");
bytes32 public constant POLICY_OPERATOR_ROLE = keccak256("POLICY_OPERATOR_ROLE");
bytes32 public constant ISSUER_ROLE = keccak256("ISSUER_ROLE");
bytes32 public constant ENFORCEMENT_ROLE = keccak256("ENFORCEMENT_ROLE");
bytes32 public constant COMPLIANCE_ROLE = keccak256("COMPLIANCE_ROLE");
bytes32 public constant DEBT_AUTHORITY_ROLE = keccak256("DEBT_AUTHORITY_ROLE");
bytes32 public constant BRIDGE_OPERATOR_ROLE = keccak256("BRIDGE_OPERATOR_ROLE");
}

View File

@@ -0,0 +1,168 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "forge-std/Script.sol";
import "../../contracts/reserve/ReserveSystem.sol";
import "../../contracts/reserve/IReserveSystem.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/**
* @title ConfigureInitialReserves
* @notice Script to configure initial reserve assets for Reserve System
* @dev Sets up supported assets, liquidity flags, and initial deposits
*/
contract ConfigureInitialReserves is Script {
function run() external {
uint256 chainId = block.chainid;
require(chainId == 138, "This script is for ChainID 138 only");
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);
address deployer = vm.addr(deployerPrivateKey);
console.log("=== Configure Initial Reserves (ChainID 138) ===");
console.log("Deployer:", deployer);
console.log("");
// Load addresses from environment
address reserveSystem = vm.envAddress("RESERVE_SYSTEM");
address admin = vm.envOr("RESERVE_ADMIN", deployer);
address reserveManager = vm.envOr("RESERVE_MANAGER", deployer);
ReserveSystem reserve = ReserveSystem(reserveSystem);
console.log("=== Adding Supported Assets ===");
// Asset 1: Gold (XAU) - Liquid asset
address xauAsset = vm.envOr("XAU_ASSET", address(0));
if (xauAsset != address(0)) {
console.log("Adding XAU asset:", xauAsset);
vm.prank(admin);
reserve.addSupportedAsset(xauAsset, true); // Liquid
console.log("XAU added as liquid asset");
}
// Asset 2: USDC - Liquid asset
address usdcAsset = vm.envOr("USDC_ASSET", address(0));
if (usdcAsset != address(0)) {
console.log("Adding USDC asset:", usdcAsset);
vm.prank(admin);
reserve.addSupportedAsset(usdcAsset, true); // Liquid
console.log("USDC added as liquid asset");
}
// Asset 3: ETH - Liquid asset
address ethAsset = vm.envOr("ETH_ASSET", address(0));
if (ethAsset != address(0)) {
console.log("Adding ETH asset:", ethAsset);
vm.prank(admin);
reserve.addSupportedAsset(ethAsset, true); // Liquid
console.log("ETH added as liquid asset");
}
// Asset 4: WETH (if different from ETH)
address wethAsset = vm.envOr("WETH_ASSET", address(0));
if (wethAsset != address(0) && wethAsset != ethAsset) {
console.log("Adding WETH asset:", wethAsset);
vm.prank(admin);
reserve.addSupportedAsset(wethAsset, true); // Liquid
console.log("WETH added as liquid asset");
}
// Asset 5: Sovereign Instruments (example - less liquid)
address sovereignAsset = vm.envOr("SOVEREIGN_ASSET", address(0));
if (sovereignAsset != address(0)) {
console.log("Adding Sovereign Instrument asset:", sovereignAsset);
vm.prank(admin);
reserve.addSupportedAsset(sovereignAsset, false); // Less liquid
console.log("Sovereign Instrument added as less liquid asset");
}
console.log("");
console.log("=== Initial Reserve Deposits ===");
console.log("Note: Ensure assets are approved and sufficient balance exists");
// Deposit XAU reserves (if configured)
if (xauAsset != address(0)) {
uint256 xauAmount = vm.envOr("XAU_INITIAL_DEPOSIT", uint256(0));
if (xauAmount > 0) {
console.log("Depositing XAU reserves:", xauAmount);
IERC20(xauAsset).approve(reserveSystem, xauAmount);
vm.prank(reserveManager);
reserve.depositReserve(xauAsset, xauAmount);
console.log("XAU deposit complete");
}
}
// Deposit USDC reserves (if configured)
if (usdcAsset != address(0)) {
uint256 usdcAmount = vm.envOr("USDC_INITIAL_DEPOSIT", uint256(0));
if (usdcAmount > 0) {
console.log("Depositing USDC reserves:", usdcAmount);
IERC20(usdcAsset).approve(reserveSystem, usdcAmount);
vm.prank(reserveManager);
reserve.depositReserve(usdcAsset, usdcAmount);
console.log("USDC deposit complete");
}
}
// Deposit ETH reserves (if configured)
if (ethAsset != address(0)) {
uint256 ethAmount = vm.envOr("ETH_INITIAL_DEPOSIT", uint256(0));
if (ethAmount > 0) {
console.log("Depositing ETH reserves:", ethAmount);
IERC20(ethAsset).approve(reserveSystem, ethAmount);
vm.prank(reserveManager);
reserve.depositReserve(ethAsset, ethAmount);
console.log("ETH deposit complete");
}
}
// Deposit WETH reserves (if configured)
if (wethAsset != address(0)) {
uint256 wethAmount = vm.envOr("WETH_INITIAL_DEPOSIT", uint256(0));
if (wethAmount > 0) {
console.log("Depositing WETH reserves:", wethAmount);
IERC20(wethAsset).approve(reserveSystem, wethAmount);
vm.prank(reserveManager);
reserve.depositReserve(wethAsset, wethAmount);
console.log("WETH deposit complete");
}
}
console.log("");
console.log("=== Reserve Balances ===");
if (xauAsset != address(0)) {
uint256 xauBalance = reserve.getReserveBalance(xauAsset);
console.log("XAU Reserve Balance:", xauBalance);
}
if (usdcAsset != address(0)) {
uint256 usdcBalance = reserve.getReserveBalance(usdcAsset);
console.log("USDC Reserve Balance:", usdcBalance);
}
if (ethAsset != address(0)) {
uint256 ethBalance = reserve.getReserveBalance(ethAsset);
console.log("ETH Reserve Balance:", ethBalance);
}
if (wethAsset != address(0)) {
uint256 wethBalance = reserve.getReserveBalance(wethAsset);
console.log("WETH Reserve Balance:", wethBalance);
}
console.log("");
console.log("=== Configuration Complete ===");
console.log("");
console.log("=== Supported Assets ===");
address[] memory supportedAssets = reserve.getSupportedAssets();
for (uint256 i = 0; i < supportedAssets.length; i++) {
console.log("Asset", i, ":", supportedAssets[i]);
}
vm.stopBroadcast();
}
}

View File

@@ -0,0 +1,54 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "forge-std/Script.sol";
import "../../contracts/reserve/ReserveSystem.sol";
import "../../contracts/reserve/ReserveTokenIntegration.sol";
import "@emoney/interfaces/ITokenFactory138.sol";
/**
* @title DeployReserveSystem
* @notice Deployment script for Reserve System on ChainID 138
*/
contract DeployReserveSystem is Script {
function run() external {
uint256 chainId = block.chainid;
require(chainId == 138, "This script is for ChainID 138 only");
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);
address deployer = vm.addr(deployerPrivateKey);
console.log("=== Reserve System Deployment (ChainID 138) ===");
console.log("Deployer:", deployer);
console.log("");
// Load addresses from environment
address admin = vm.envOr("RESERVE_ADMIN", deployer);
address tokenFactory = vm.envAddress("TOKEN_FACTORY");
console.log("Deploying ReserveSystem...");
ReserveSystem reserveSystem = new ReserveSystem(admin);
console.log("ReserveSystem deployed at:", address(reserveSystem));
console.log("Deploying ReserveTokenIntegration...");
ReserveTokenIntegration integration = new ReserveTokenIntegration(
admin,
address(reserveSystem),
tokenFactory
);
console.log("ReserveTokenIntegration deployed at:", address(integration));
console.log("");
console.log("=== Deployment Summary ===");
console.log("ReserveSystem:", address(reserveSystem));
console.log("ReserveTokenIntegration:", address(integration));
console.log("");
console.log("=== Export to .env ===");
console.log("export RESERVE_SYSTEM=", vm.toString(address(reserveSystem)));
console.log("export RESERVE_INTEGRATION=", vm.toString(address(integration)));
vm.stopBroadcast();
}
}

View File

@@ -0,0 +1,34 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "forge-std/Script.sol";
/**
* @title SetupComplete
* @notice Complete setup script for Reserve System
* @dev This script should be run as a bash script that calls the individual scripts
*
* Usage:
* # Step 1: Setup Price Feeds
* forge script script/reserve/SetupPriceFeeds.s.sol:SetupPriceFeeds --rpc-url chain138 --broadcast
*
* # Step 2: Configure Initial Reserves
* forge script script/reserve/ConfigureInitialReserves.s.sol:ConfigureInitialReserves --rpc-url chain138 --broadcast
*/
contract SetupComplete is Script {
function run() external {
console.log("=== Complete Reserve System Setup ===");
console.log("");
console.log("This script is a placeholder.");
console.log("Please run the setup scripts individually:");
console.log("");
console.log("Step 1: Setup Price Feeds");
console.log(" forge script script/reserve/SetupPriceFeeds.s.sol:SetupPriceFeeds --rpc-url chain138 --broadcast");
console.log("");
console.log("Step 2: Configure Initial Reserves");
console.log(" forge script script/reserve/ConfigureInitialReserves.s.sol:ConfigureInitialReserves --rpc-url chain138 --broadcast");
console.log("");
console.log("Or use the bash script: scripts/reserve/setup-complete.sh");
}
}

View File

@@ -0,0 +1,144 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "forge-std/Script.sol";
import "../../contracts/reserve/OraclePriceFeed.sol";
import "../../contracts/reserve/MockPriceFeed.sol";
import "../../contracts/reserve/IReserveSystem.sol";
/**
* @title SetupPriceFeeds
* @notice Script to set up price feeds for Reserve System
* @dev Configures aggregators and initial prices for supported assets
*/
contract SetupPriceFeeds is Script {
function run() external {
uint256 chainId = block.chainid;
require(chainId == 138, "This script is for ChainID 138 only");
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);
address deployer = vm.addr(deployerPrivateKey);
console.log("=== Setup Price Feeds (ChainID 138) ===");
console.log("Deployer:", deployer);
console.log("");
// Load addresses from environment
address reserveSystem = vm.envAddress("RESERVE_SYSTEM");
address oraclePriceFeed = vm.envOr("ORACLE_PRICE_FEED", address(0));
address admin = vm.envOr("RESERVE_ADMIN", deployer);
// Deploy OraclePriceFeed if not provided
if (oraclePriceFeed == address(0)) {
console.log("Deploying OraclePriceFeed...");
OraclePriceFeed priceFeed = new OraclePriceFeed(admin, reserveSystem);
oraclePriceFeed = address(priceFeed);
console.log("OraclePriceFeed deployed at:", oraclePriceFeed);
} else {
console.log("Using existing OraclePriceFeed:", oraclePriceFeed);
}
OraclePriceFeed priceFeedContract = OraclePriceFeed(oraclePriceFeed);
// Configuration: Asset addresses and initial prices
// For production, use real Chainlink aggregators
// For testing, deploy MockPriceFeed contracts
bool useMockFeeds = vm.envOr("USE_MOCK_FEEDS", true);
if (useMockFeeds) {
console.log("");
console.log("=== Deploying Mock Price Feeds ===");
// Example assets with mock prices (in 8 decimals for Chainlink compatibility)
// Gold (XAU) - $2000 per ounce
address xauAsset = vm.envOr("XAU_ASSET", address(0x1111111111111111111111111111111111111111));
if (xauAsset != address(0)) {
console.log("Setting up XAU price feed...");
MockPriceFeed xauFeed = new MockPriceFeed(2000 * 1e8, 8);
// Multiplier: 1e10 to convert 8 decimals to 18 decimals
vm.prank(admin);
priceFeedContract.setAggregator(xauAsset, address(xauFeed), 1e10);
console.log("XAU MockPriceFeed:", address(xauFeed));
}
// Example: USDC - $1 per token
address usdcAsset = vm.envOr("USDC_ASSET", address(0x2222222222222222222222222222222222222222));
if (usdcAsset != address(0)) {
console.log("Setting up USDC price feed...");
MockPriceFeed usdcFeed = new MockPriceFeed(1 * 1e8, 8);
vm.prank(admin);
priceFeedContract.setAggregator(usdcAsset, address(usdcFeed), 1e10);
console.log("USDC MockPriceFeed:", address(usdcFeed));
}
// Example: ETH - $3000 per token
address ethAsset = vm.envOr("ETH_ASSET", address(0x3333333333333333333333333333333333333333));
if (ethAsset != address(0)) {
console.log("Setting up ETH price feed...");
MockPriceFeed ethFeed = new MockPriceFeed(3000 * 1e8, 8);
vm.prank(admin);
priceFeedContract.setAggregator(ethAsset, address(ethFeed), 1e10);
console.log("ETH MockPriceFeed:", address(ethFeed));
}
console.log("");
console.log("=== Updating Price Feeds ===");
// Update all price feeds
address[] memory assets = new address[](3);
if (xauAsset != address(0)) assets[0] = xauAsset;
if (usdcAsset != address(0)) assets[1] = usdcAsset;
if (ethAsset != address(0)) assets[2] = ethAsset;
vm.prank(admin);
priceFeedContract.updateMultiplePriceFeeds(assets);
console.log("Price feeds updated successfully");
} else {
console.log("");
console.log("=== Configuring Real Chainlink Aggregators ===");
console.log("Set USE_MOCK_FEEDS=false and provide aggregator addresses");
console.log("Example environment variables:");
console.log(" XAU_AGGREGATOR=<chainlink_xau_usd_aggregator>");
console.log(" USDC_AGGREGATOR=<chainlink_usdc_usd_aggregator>");
console.log(" ETH_AGGREGATOR=<chainlink_eth_usd_aggregator>");
// Configure real aggregators if provided
address xauAggregator = vm.envOr("XAU_AGGREGATOR", address(0));
address xauAsset = vm.envOr("XAU_ASSET", address(0));
if (xauAggregator != address(0) && xauAsset != address(0)) {
vm.prank(admin);
priceFeedContract.setAggregator(xauAsset, xauAggregator, 1e10);
console.log("XAU aggregator configured:", xauAggregator);
}
address usdcAggregator = vm.envOr("USDC_AGGREGATOR", address(0));
address usdcAsset = vm.envOr("USDC_ASSET", address(0));
if (usdcAggregator != address(0) && usdcAsset != address(0)) {
vm.prank(admin);
priceFeedContract.setAggregator(usdcAsset, usdcAggregator, 1e10);
console.log("USDC aggregator configured:", usdcAggregator);
}
address ethAggregator = vm.envOr("ETH_AGGREGATOR", address(0));
address ethAsset = vm.envOr("ETH_ASSET", address(0));
if (ethAggregator != address(0) && ethAsset != address(0)) {
vm.prank(admin);
priceFeedContract.setAggregator(ethAsset, ethAggregator, 1e10);
console.log("ETH aggregator configured:", ethAggregator);
}
}
console.log("");
console.log("=== Setup Complete ===");
console.log("OraclePriceFeed:", oraclePriceFeed);
console.log("");
console.log("=== Export to .env ===");
console.log("export ORACLE_PRICE_FEED=", vm.toString(oraclePriceFeed));
vm.stopBroadcast();
}
}