diff --git a/contracts/LabContract.sol b/contracts/LabContract.sol new file mode 100644 index 000000000..1696f5a75 --- /dev/null +++ b/contracts/LabContract.sol @@ -0,0 +1,122 @@ +// 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 {} +} \ No newline at end of file