// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import "./interfaces/ILabRegistry.sol"; import "./LabHelper.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; contract LabContract is Ownable, ReentrancyGuard { ILabRegistry public labRegistry; LabHelper public labHelper; struct ExperimentConfig { address creator; uint256 startTimestamp; uint256 fundingAmount; bool isActive; bytes32 configHash; } mapping(bytes32 => ExperimentConfig) public experiments; mapping(address => bytes32[]) public creatorExperiments; event ExperimentCreated( bytes32 indexed experimentId, address indexed creator, uint256 fundingAmount ); event ExperimentUpdated( bytes32 indexed experimentId, bool isActive ); constructor(address _registryAddress, address _helperAddress) { require(_registryAddress != address(0), "Invalid registry address"); require(_helperAddress != address(0), "Invalid helper address"); labRegistry = ILabRegistry(_registryAddress); labHelper = LabHelper(_helperAddress); } function createExperiment( uint256 fundingAmount, bytes32 configHash ) external nonReentrant { require(fundingAmount > 0, "Funding must be greater than zero"); require(configHash != bytes32(0), "Invalid config hash"); bytes32 experimentId = keccak256( abi.encodePacked(msg.sender, block.timestamp, configHash) ); ExperimentConfig memory newExperiment = ExperimentConfig({ creator: msg.sender, startTimestamp: block.timestamp, fundingAmount: fundingAmount, isActive: true, configHash: configHash }); experiments[experimentId] = newExperiment; creatorExperiments[msg.sender].push(experimentId); require( labRegistry.registerExperiment(experimentId, msg.sender), "Experiment registration failed" ); emit ExperimentCreated(experimentId, msg.sender, fundingAmount); } function updateExperimentStatus( bytes32 experimentId, bool newStatus ) external nonReentrant { ExperimentConfig storage experiment = experiments[experimentId]; require( experiment.creator == msg.sender, "Only experiment creator can update status" ); experiment.isActive = newStatus; emit ExperimentUpdated(experimentId, newStatus); } function getExperimentDetails( bytes32 experimentId ) external view returns (ExperimentConfig memory) { ExperimentConfig memory experiment = experiments[experimentId]; require( experiment.creator != address(0), "Experiment does not exist" ); return experiment; } function withdrawFunds( bytes32 experimentId, uint256 amount ) external nonReentrant { ExperimentConfig storage experiment = experiments[experimentId]; require( experiment.creator == msg.sender, "Only experiment creator can withdraw" ); require(experiment.fundingAmount >= amount, "Insufficient funds"); require(experiment.isActive, "Experiment is not active"); experiment.fundingAmount -= amount; (bool success, ) = payable(msg.sender).call{value: amount}(""); require(success, "Transfer failed"); } receive() external payable {} }