// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "@openzeppelin/contracts/security/Pausable.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "./AIPowerRental.sol"; import "./AITBCPaymentProcessor.sol"; /** * @title Escrow Service * @dev Advanced escrow service with multi-signature, time-locks, and conditional releases * @notice Secure payment holding with automated release conditions and dispute protection */ contract EscrowService is Ownable, ReentrancyGuard, Pausable { // State variables IERC20 public aitbcToken; AIPowerRental public aiPowerRental; AITBCPaymentProcessor public paymentProcessor; uint256 public escrowCounter; uint256 public minEscrowAmount = 1e15; // 0.001 AITBC minimum uint256 public maxEscrowAmount = 1e22; // 10,000 AITBC maximum uint256 public minTimeLock = 300; // 5 minutes minimum uint256 public maxTimeLock = 86400 * 30; // 30 days maximum uint256 public defaultReleaseDelay = 3600; // 1 hour default uint256 public emergencyReleaseDelay = 86400; // 24 hours for emergency uint256 public platformFeePercentage = 50; // 0.5% in basis points // Structs struct EscrowAccount { uint256 escrowId; address depositor; address beneficiary; address arbiter; uint256 amount; uint256 platformFee; uint256 releaseTime; uint256 creationTime; bool isReleased; bool isRefunded; bool isFrozen; EscrowType escrowType; ReleaseCondition releaseCondition; bytes32 conditionHash; string conditionDescription; uint256 releaseAttempts; uint256 lastReleaseAttempt; address[] signatories; mapping(address => bool) hasSigned; uint256 requiredSignatures; uint256 currentSignatures; } struct ConditionalRelease { uint256 escrowId; bytes32 condition; bool conditionMet; address oracle; uint256 verificationTime; string conditionData; uint256 confidence; } struct MultiSigRelease { uint256 escrowId; address[] requiredSigners; uint256 signaturesRequired; mapping(address => bool) hasSigned; uint256 currentSignatures; uint256 deadline; bool isExecuted; } struct TimeLockRelease { uint256 escrowId; uint256 lockStartTime; uint256 lockDuration; uint256 releaseWindow; bool canEarlyRelease; uint256 earlyReleaseFee; bool isReleased; } struct EmergencyRelease { uint256 escrowId; address initiator; string reason; uint256 requestTime; uint256 votingDeadline; mapping(address => bool) hasVoted; uint256 votesFor; uint256 votesAgainst; uint256 totalVotes; bool isApproved; bool isExecuted; } // Enums enum EscrowType { Standard, MultiSignature, TimeLocked, Conditional, PerformanceBased, MilestoneBased, Emergency } enum ReleaseCondition { Manual, Automatic, OracleVerified, PerformanceMet, TimeBased, MultiSignature, Emergency } enum EscrowStatus { Created, Funded, Locked, ConditionPending, Approved, Released, Refunded, Disputed, Frozen, Expired } // Mappings mapping(uint256 => EscrowAccount) public escrowAccounts; mapping(uint256 => ConditionalRelease) public conditionalReleases; mapping(uint256 => MultiSigRelease) public multiSigReleases; mapping(uint256 => TimeLockRelease) public timeLockReleases; mapping(uint256 => EmergencyRelease) public emergencyReleases; mapping(address => uint256[]) public depositorEscrows; mapping(address => uint256[]) public beneficiaryEscrows; mapping(bytes32 => uint256) public conditionEscrows; mapping(address => bool) public authorizedOracles; mapping(address => bool) public authorizedArbiters; // Arrays for tracking uint256[] public activeEscrows; uint256[] public pendingReleases; // Events event EscrowCreated( uint256 indexed escrowId, address indexed depositor, address indexed beneficiary, uint256 amount, EscrowType escrowType, ReleaseCondition releaseCondition ); event EscrowFunded( uint256 indexed escrowId, uint256 amount, uint256 platformFee ); event EscrowReleased( uint256 indexed escrowId, address indexed beneficiary, uint256 amount, string reason ); event EscrowRefunded( uint256 indexed escrowId, address indexed depositor, uint256 amount, string reason ); event ConditionSet( uint256 indexed escrowId, bytes32 indexed condition, address indexed oracle, string conditionDescription ); event ConditionMet( uint256 indexed escrowId, bytes32 indexed condition, bool conditionMet, uint256 verificationTime ); event MultiSignatureRequired( uint256 indexed escrowId, address[] requiredSigners, uint256 signaturesRequired ); event SignatureSubmitted( uint256 indexed escrowId, address indexed signer, uint256 currentSignatures, uint256 requiredSignatures ); event TimeLockSet( uint256 indexed escrowId, uint256 lockDuration, uint256 releaseWindow, bool canEarlyRelease ); event EmergencyReleaseRequested( uint256 indexed escrowId, address indexed initiator, string reason, uint256 votingDeadline ); event EmergencyReleaseApproved( uint256 indexed escrowId, uint256 votesFor, uint256 votesAgainst, bool approved ); event EscrowFrozen( uint256 indexed escrowId, address indexed freezer, string reason ); event EscrowUnfrozen( uint256 indexed escrowId, address indexed unfreezer, string reason ); event PlatformFeeCollected( uint256 indexed escrowId, uint256 feeAmount, address indexed collector ); // Modifiers modifier onlyAuthorizedOracle() { require(authorizedOracles[msg.sender], "Not authorized oracle"); _; } modifier onlyAuthorizedArbiter() { require(authorizedArbiters[msg.sender], "Not authorized arbiter"); _; } modifier escrowExists(uint256 _escrowId) { require(_escrowId < escrowCounter, "Escrow does not exist"); _; } modifier onlyParticipant(uint256 _escrowId) { require( msg.sender == escrowAccounts[_escrowId].depositor || msg.sender == escrowAccounts[_escrowId].beneficiary || msg.sender == escrowAccounts[_escrowId].arbiter, "Not escrow participant" ); _; } modifier sufficientBalance(address _user, uint256 _amount) { require(aitbcToken.balanceOf(_user) >= _amount, "Insufficient balance"); _; } modifier sufficientAllowance(address _user, uint256 _amount) { require(aitbcToken.allowance(_user, address(this)) >= _amount, "Insufficient allowance"); _; } modifier escrowNotFrozen(uint256 _escrowId) { require(!escrowAccounts[_escrowId].isFrozen, "Escrow is frozen"); _; } modifier escrowNotReleased(uint256 _escrowId) { require(!escrowAccounts[_escrowId].isReleased, "Escrow already released"); _; } modifier escrowNotRefunded(uint256 _escrowId) { require(!escrowAccounts[_escrowId].isRefunded, "Escrow already refunded"); _; } // Constructor constructor( address _aitbcToken, address _aiPowerRental, address _paymentProcessor ) { aitbcToken = IERC20(_aitbcToken); aiPowerRental = AIPowerRental(_aiPowerRental); paymentProcessor = AITBCPaymentProcessor(_paymentProcessor); escrowCounter = 0; } /** * @dev Creates a new escrow account * @param _beneficiary Beneficiary address * @param _arbiter Arbiter address (can be zero address for no arbiter) * @param _amount Amount to lock in escrow * @param _escrowType Type of escrow * @param _releaseCondition Release condition * @param _releaseTime Release time (0 for no time limit) * @param _conditionDescription Description of release conditions */ function createEscrow( address _beneficiary, address _arbiter, uint256 _amount, EscrowType _escrowType, ReleaseCondition _releaseCondition, uint256 _releaseTime, string memory _conditionDescription ) external sufficientBalance(msg.sender, _amount) sufficientAllowance(msg.sender, _amount) nonReentrant whenNotPaused returns (uint256) { require(_beneficiary != address(0), "Invalid beneficiary"); require(_beneficiary != msg.sender, "Cannot be own beneficiary"); require(_amount >= minEscrowAmount && _amount <= maxEscrowAmount, "Invalid amount"); require(_releaseTime == 0 || _releaseTime > block.timestamp, "Invalid release time"); uint256 escrowId = escrowCounter++; uint256 platformFee = (_amount * platformFeePercentage) / 10000; uint256 totalAmount = _amount + platformFee; escrowAccounts[escrowId] = EscrowAccount({ escrowId: escrowId, depositor: msg.sender, beneficiary: _beneficiary, arbiter: _arbiter, amount: _amount, platformFee: platformFee, releaseTime: _releaseTime, creationTime: block.timestamp, isReleased: false, isRefunded: false, isFrozen: false, escrowType: _escrowType, releaseCondition: _releaseCondition, conditionHash: bytes32(0), conditionDescription: _conditionDescription, releaseAttempts: 0, lastReleaseAttempt: 0, signatories: new address[](0), requiredSignatures: 0, currentSignatures: 0 }); depositorEscrows[msg.sender].push(escrowId); beneficiaryEscrows[_beneficiary].push(escrowId); activeEscrows.push(escrowId); // Transfer tokens to contract require( aitbcToken.transferFrom(msg.sender, address(this), totalAmount), "Escrow funding failed" ); emit EscrowCreated(escrowId, msg.sender, _beneficiary, _amount, _escrowType, _releaseCondition); emit EscrowFunded(escrowId, _amount, platformFee); // Setup specific escrow type configurations if (_escrowType == EscrowType.TimeLocked) { _setupTimeLock(escrowId, _releaseTime - block.timestamp); } else if (_escrowType == EscrowType.MultiSignature) { _setupMultiSignature(escrowId); } return escrowId; } /** * @dev Sets release condition for escrow * @param _escrowId ID of the escrow * @param _condition Condition hash * @param _oracle Oracle address for verification * @param _conditionData Condition data */ function setReleaseCondition( uint256 _escrowId, bytes32 _condition, address _oracle, string memory _conditionData ) external escrowExists(_escrowId) onlyParticipant(_escrowId) escrowNotFrozen(_escrowId) escrowNotReleased(_escrowId) { require(authorizedOracles[_oracle] || _oracle == address(0), "Invalid oracle"); EscrowAccount storage escrow = escrowAccounts[_escrowId]; escrow.conditionHash = _condition; conditionalReleases[_escrowId] = ConditionalRelease({ escrowId: _escrowId, condition: _condition, conditionMet: false, oracle: _oracle, verificationTime: 0, conditionData: _conditionData, confidence: 0 }); conditionEscrows[_condition] = _escrowId; emit ConditionSet(_escrowId, _condition, _oracle, _conditionData); } /** * @dev Verifies and meets release condition * @param _escrowId ID of the escrow * @param _conditionMet Whether condition is met * @param _confidence Confidence level (0-100) */ function verifyCondition( uint256 _escrowId, bool _conditionMet, uint256 _confidence ) external onlyAuthorizedOracle escrowExists(_escrowId) escrowNotFrozen(_escrowId) escrowNotReleased(_escrowId) { ConditionalRelease storage condRelease = conditionalReleases[_escrowId]; require(condRelease.oracle == msg.sender, "Not assigned oracle"); condRelease.conditionMet = _conditionMet; condRelease.verificationTime = block.timestamp; condRelease.confidence = _confidence; emit ConditionMet(_escrowId, condRelease.condition, _conditionMet, block.timestamp); if (_conditionMet) { _releaseEscrow(_escrowId, "Condition verified and met"); } } /** * @dev Submits signature for multi-signature release * @param _escrowId ID of the escrow */ function submitSignature(uint256 _escrowId) external escrowExists(_escrowId) onlyParticipant(_escrowId) escrowNotFrozen(_escrowId) escrowNotReleased(_escrowId) { EscrowAccount storage escrow = escrowAccounts[_escrowId]; MultiSigRelease storage multiSig = multiSigReleases[_escrowId]; require(!escrow.hasSigned[msg.sender], "Already signed"); require(multiSig.requiredSigners > 0, "Multi-signature not setup"); escrow.hasSigned[msg.sender] = true; escrow.currentSignatures++; multiSig.currentSignatures++; emit SignatureSubmitted(_escrowId, msg.sender, escrow.currentSignatures, multiSig.requiredSigners); if (escrow.currentSignatures >= multiSig.requiredSigners) { _releaseEscrow(_escrowId, "Multi-signature requirement met"); } } /** * @dev Releases escrow to beneficiary * @param _escrowId ID of the escrow * @param _reason Reason for release */ function releaseEscrow(uint256 _escrowId, string memory _reason) external escrowExists(_escrowId) escrowNotFrozen(_escrowId) escrowNotReleased(_escrowId) escrowNotRefunded(_escrowId) nonReentrant { EscrowAccount storage escrow = escrowAccounts[_escrowId]; require( msg.sender == escrow.depositor || msg.sender == escrow.beneficiary || msg.sender == escrow.arbiter || msg.sender == owner(), "Not authorized to release" ); // Check release conditions if (escrow.releaseCondition == ReleaseCondition.Manual) { require(msg.sender == escrow.depositor || msg.sender == escrow.arbiter, "Manual release requires depositor or arbiter"); } else if (escrow.releaseCondition == ReleaseCondition.TimeBased) { require(block.timestamp >= escrow.releaseTime, "Release time not reached"); } else if (escrow.releaseCondition == ReleaseCondition.OracleVerified) { require(conditionalReleases[_escrowId].conditionMet, "Condition not met"); } else if (escrow.releaseCondition == ReleaseCondition.MultiSignature) { require(escrow.currentSignatures >= escrow.requiredSignatures, "Insufficient signatures"); } _releaseEscrow(_escrowId, _reason); } /** * @dev Refunds escrow to depositor * @param _escrowId ID of the escrow * @param _reason Reason for refund */ function refundEscrow(uint256 _escrowId, string memory _reason) external escrowExists(_escrowId) escrowNotFrozen(_escrowId) escrowNotReleased(_escrowId) escrowNotRefunded(_escrowId) nonReentrant { EscrowAccount storage escrow = escrowAccounts[_escrowId]; require( msg.sender == escrow.depositor || msg.sender == escrow.arbiter || msg.sender == owner(), "Not authorized to refund" ); // Check refund conditions if (escrow.releaseCondition == ReleaseCondition.TimeBased) { require(block.timestamp < escrow.releaseTime, "Release time passed, cannot refund"); } else if (escrow.releaseCondition == ReleaseCondition.OracleVerified) { require(!conditionalReleases[_escrowId].conditionMet, "Condition met, cannot refund"); } escrow.isRefunded = true; require( aitbcToken.transfer(escrow.depositor, escrow.amount), "Refund transfer failed" ); emit EscrowRefunded(_escrowId, escrow.depositor, escrow.amount, _reason); } /** * @dev Requests emergency release * @param _escrowId ID of the escrow * @param _reason Reason for emergency release */ function requestEmergencyRelease(uint256 _escrowId, string memory _reason) external escrowExists(_escrowId) onlyParticipant(_escrowId) escrowNotFrozen(_escrowId) escrowNotReleased(_escrowId) escrowNotRefunded(_escrowId) { EscrowAccount storage escrow = escrowAccounts[_escrowId]; EmergencyRelease storage emergency = emergencyReleases[_escrowId]; require(emergency.requestTime == 0, "Emergency release already requested"); emergencyReleases[_escrowId] = EmergencyRelease({ escrowId: _escrowId, initiator: msg.sender, reason: _reason, requestTime: block.timestamp, votingDeadline: block.timestamp + emergencyReleaseDelay, votesFor: 0, votesAgainst: 0, totalVotes: 0, isApproved: false, isExecuted: false }); emit EmergencyReleaseRequested(_escrowId, msg.sender, _reason, block.timestamp + emergencyReleaseDelay); } /** * @dev Votes on emergency release * @param _escrowId ID of the escrow * @param _vote True to approve, false to reject */ function voteEmergencyRelease(uint256 _escrowId, bool _vote) external escrowExists(_escrowId) onlyAuthorizedArbiter { EmergencyRelease storage emergency = emergencyReleases[_escrowId]; require(emergency.requestTime > 0, "No emergency release requested"); require(block.timestamp <= emergency.votingDeadline, "Voting deadline passed"); require(!emergency.hasVoted[msg.sender], "Already voted"); emergency.hasVoted[msg.sender] = true; emergency.totalVotes++; if (_vote) { emergency.votesFor++; } else { emergency.votesAgainst++; } // Check if voting is complete and approved if (emergency.totalVotes >= 3 && emergency.votesFor > emergency.votesAgainst) { emergency.isApproved = true; emit EmergencyReleaseApproved(_escrowId, emergency.votesFor, emergency.votesAgainst, true); _releaseEscrow(_escrowId, "Emergency release approved"); } } /** * @dev Freezes an escrow account * @param _escrowId ID of the escrow * @param _reason Reason for freezing */ function freezeEscrow(uint256 _escrowId, string memory _reason) external escrowExists(_escrowId) escrowNotFrozen(_escrowId) { require( msg.sender == escrowAccounts[_escrowId].arbiter || msg.sender == owner(), "Not authorized to freeze" ); escrowAccounts[_escrowId].isFrozen = true; emit EscrowFrozen(_escrowId, msg.sender, _reason); } /** * @dev Unfreezes an escrow account * @param _escrowId ID of the escrow * @param _reason Reason for unfreezing */ function unfreezeEscrow(uint256 _escrowId, string memory _reason) external escrowExists(_escrowId) { require( msg.sender == escrowAccounts[_escrowId].arbiter || msg.sender == owner(), "Not authorized to unfreeze" ); escrowAccounts[_escrowId].isFrozen = false; emit EscrowUnfrozen(_escrowId, msg.sender, _reason); } /** * @dev Authorizes an oracle * @param _oracle Address of the oracle */ function authorizeOracle(address _oracle) external onlyOwner { require(_oracle != address(0), "Invalid oracle address"); authorizedOracles[_oracle] = true; } /** * @dev Revokes oracle authorization * @param _oracle Address of the oracle */ function revokeOracle(address _oracle) external onlyOwner { authorizedOracles[_oracle] = false; } /** * @dev Authorizes an arbiter * @param _arbiter Address of the arbiter */ function authorizeArbiter(address _arbiter) external onlyOwner { require(_arbiter != address(0), "Invalid arbiter address"); authorizedArbiters[_arbiter] = true; } /** * @dev Revokes arbiter authorization * @param _arbiter Address of the arbiter */ function revokeArbiter(address _arbiter) external onlyOwner { authorizedArbiters[_arbiter] = false; } // Internal functions function _setupTimeLock(uint256 _escrowId, uint256 _duration) internal { require(_duration >= minTimeLock && _duration <= maxTimeLock, "Invalid duration"); timeLockReleases[_escrowId] = TimeLockRelease({ escrowId: _escrowId, lockStartTime: block.timestamp, lockDuration: _duration, releaseWindow: _duration / 10, // 10% of lock duration as release window canEarlyRelease: false, earlyReleaseFee: 1000, // 10% fee for early release isReleased: false }); emit TimeLockSet(_escrowId, _duration, _duration / 10, false); } function _setupMultiSignature(uint256 _escrowId) internal { EscrowAccount storage escrow = escrowAccounts[_escrowId]; // Default to requiring depositor + beneficiary signatures address[] memory requiredSigners = new address[](2); requiredSigners[0] = escrow.depositor; requiredSigners[1] = escrow.beneficiary; multiSigReleases[_escrowId] = MultiSigRelease({ escrowId: _escrowId, requiredSigners: requiredSigners, signaturesRequired: 2, currentSignatures: 0, deadline: block.timestamp + 7 days, isExecuted: false }); escrow.requiredSignatures = 2; emit MultiSignatureRequired(_escrowId, requiredSigners, 2); } function _releaseEscrow(uint256 _escrowId, string memory _reason) internal { EscrowAccount storage escrow = escrowAccounts[_escrowId]; escrow.isReleased = true; escrow.lastReleaseAttempt = block.timestamp; // Transfer amount to beneficiary require( aitbcToken.transfer(escrow.beneficiary, escrow.amount), "Escrow release failed" ); // Transfer platform fee to owner if (escrow.platformFee > 0) { require( aitbcToken.transfer(owner(), escrow.platformFee), "Platform fee transfer failed" ); emit PlatformFeeCollected(_escrowId, escrow.platformFee, owner()); } emit EscrowReleased(_escrowId, escrow.beneficiary, escrow.amount, _reason); } // View functions /** * @dev Gets escrow account details * @param _escrowId ID of the escrow */ function getEscrowAccount(uint256 _escrowId) external view escrowExists(_escrowId) returns (EscrowAccount memory) { return escrowAccounts[_escrowId]; } /** * @dev Gets conditional release details * @param _escrowId ID of the escrow */ function getConditionalRelease(uint256 _escrowId) external view returns (ConditionalRelease memory) { return conditionalReleases[_escrowId]; } /** * @dev Gets multi-signature release details * @param _escrowId ID of the escrow */ function getMultiSigRelease(uint256 _escrowId) external view returns (MultiSigRelease memory) { return multiSigReleases[_escrowId]; } /** * @dev Gets time-lock release details * @param _escrowId ID of the escrow */ function getTimeLockRelease(uint256 _escrowId) external view returns (TimeLockRelease memory) { return timeLockReleases[_escrowId]; } /** * @dev Gets emergency release details * @param _escrowId ID of the escrow */ function getEmergencyRelease(uint256 _escrowId) external view returns (EmergencyRelease memory) { return emergencyReleases[_escrowId]; } /** * @dev Gets all escrows for a depositor * @param _depositor Address of the depositor */ function getDepositorEscrows(address _depositor) external view returns (uint256[] memory) { return depositorEscrows[_depositor]; } /** * @dev Gets all escrows for a beneficiary * @param _beneficiary Address of the beneficiary */ function getBeneficiaryEscrows(address _beneficiary) external view returns (uint256[] memory) { return beneficiaryEscrows[_beneficiary]; } /** * @dev Gets active escrows */ function getActiveEscrows() external view returns (uint256[] memory) { uint256[] memory active = new uint256[](activeEscrows.length); uint256 activeCount = 0; for (uint256 i = 0; i < activeEscrows.length; i++) { EscrowAccount storage escrow = escrowAccounts[activeEscrows[i]]; if (!escrow.isReleased && !escrow.isRefunded) { active[activeCount] = activeEscrows[i]; activeCount++; } } // Resize array to active count assembly { mstore(active, activeCount) } return active; } /** * @dev Emergency pause function */ function pause() external onlyOwner { _pause(); } /** * @dev Unpause function */ function unpause() external onlyOwner { _unpause(); } }