Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // SPDX-License-Identifier: MIT
- pragma solidity ^0.8.19;
- import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
- import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
- import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
- import "@openzeppelin/contracts/security/Pausable.sol";
- import "@openzeppelin/contracts/access/Ownable.sol";
- import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
- /**
- * @title SecureUSDCEscrow
- * @dev Ultra-secure escrow contract for USDC transactions with comprehensive security features
- * @author AI Assistant
- */
- contract SecureUSDCEscrow is ReentrancyGuard, Pausable, Ownable {
- using SafeERC20 for IERC20;
- using ECDSA for bytes32;
- // USDC contract address - configurable for different networks
- IERC20 public immutable USDC;
- // Network-specific USDC addresses
- address public constant ETHEREUM_USDC = 0xA0b86a33E6441f8952F865F7cD0b06de36B24B61;
- address public constant BASE_USDC = 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913;
- address public constant POLYGON_USDC = 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174;
- address public constant ARBITRUM_USDC = 0xaf88d065e77c8cC2239327C5EDb3A432268e5831;
- // Maximum escrow duration (90 days)
- uint256 public constant MAX_ESCROW_DURATION = 90 days;
- // Minimum escrow amount (1 USDC)
- uint256 public constant MIN_ESCROW_AMOUNT = 1e6; // USDC has 6 decimals
- // Maximum escrow amount (1M USDC)
- uint256 public constant MAX_ESCROW_AMOUNT = 1e12; // 1M USDC with 6 decimals
- // Fee percentage (0.5% = 50 basis points)
- uint256 public feePercentage = 50;
- uint256 public constant MAX_FEE_PERCENTAGE = 500; // 5% max fee
- // Emergency timeout (7 days after escrow expiry)
- uint256 public constant EMERGENCY_TIMEOUT = 7 days;
- enum EscrowStatus {
- ACTIVE,
- COMPLETED,
- REFUNDED,
- DISPUTED,
- EMERGENCY_WITHDRAWN
- }
- struct EscrowData {
- address depositor;
- address beneficiary;
- address arbiter;
- uint256 amount;
- uint256 fee;
- uint256 createdAt;
- uint256 expiresAt;
- EscrowStatus status;
- string description;
- bytes32 conditionsHash; // Hash of conditions for verification
- bool depositorApproved;
- bool beneficiaryApproved;
- bool arbiterApproved;
- }
- // Mappings
- mapping(uint256 => EscrowData) public escrows;
- mapping(address => bool) public authorizedArbiters;
- mapping(address => uint256[]) public userEscrows;
- mapping(bytes32 => bool) public usedSignatures;
- // State variables
- uint256 public nextEscrowId = 1;
- uint256 public totalEscrowValue;
- address public feeRecipient;
- bool public emergencyMode;
- // Events
- event EscrowCreated(
- uint256 indexed escrowId,
- address indexed depositor,
- address indexed beneficiary,
- address arbiter,
- uint256 amount,
- uint256 expiresAt,
- string description
- );
- event EscrowCompleted(uint256 indexed escrowId, address indexed beneficiary, uint256 amount);
- event EscrowRefunded(uint256 indexed escrowId, address indexed depositor, uint256 amount);
- event EscrowDisputed(uint256 indexed escrowId, address indexed disputer);
- event ApprovalGiven(uint256 indexed escrowId, address indexed approver, string role);
- event ArbiterDecision(uint256 indexed escrowId, address indexed arbiter, bool releaseTobeneficiary);
- event EmergencyWithdrawal(uint256 indexed escrowId, address indexed recipient, uint256 amount);
- event FeeUpdated(uint256 oldFee, uint256 newFee);
- event ArbiterAuthorized(address indexed arbiter, bool authorized);
- event NetworkSupported(string network, address usdcAddress);
- // Modifiers
- modifier validEscrow(uint256 _escrowId) {
- require(_escrowId > 0 && _escrowId < nextEscrowId, "Invalid escrow ID");
- _;
- }
- modifier onlyParticipant(uint256 _escrowId) {
- EscrowData storage escrow = escrows[_escrowId];
- require(
- msg.sender == escrow.depositor ||
- msg.sender == escrow.beneficiary ||
- msg.sender == escrow.arbiter,
- "Not authorized"
- );
- _;
- }
- modifier onlyActiveEscrow(uint256 _escrowId) {
- require(escrows[_escrowId].status == EscrowStatus.ACTIVE, "Escrow not active");
- _;
- }
- modifier notExpired(uint256 _escrowId) {
- require(block.timestamp <= escrows[_escrowId].expiresAt, "Escrow expired");
- _;
- }
- constructor(address _feeRecipient, address _usdcAddress) {
- require(_feeRecipient != address(0), "Invalid fee recipient");
- require(_usdcAddress != address(0), "Invalid USDC address");
- // Validate USDC address is one of the supported networks
- require(
- _usdcAddress == ETHEREUM_USDC ||
- _usdcAddress == BASE_USDC ||
- _usdcAddress == POLYGON_USDC ||
- _usdcAddress == ARBITRUM_USDC,
- "Unsupported USDC address"
- );
- feeRecipient = _feeRecipient;
- USDC = IERC20(_usdcAddress);
- }
- /**
- * @dev Create a new escrow with comprehensive validation
- */
- function createEscrow(
- address _beneficiary,
- address _arbiter,
- uint256 _amount,
- uint256 _duration,
- string calldata _description,
- bytes32 _conditionsHash
- ) external nonReentrant whenNotPaused returns (uint256) {
- // Input validation
- require(_beneficiary != address(0), "Invalid beneficiary");
- require(_arbiter != address(0), "Invalid arbiter");
- require(authorizedArbiters[_arbiter], "Arbiter not authorized");
- require(_amount >= MIN_ESCROW_AMOUNT && _amount <= MAX_ESCROW_AMOUNT, "Invalid amount");
- require(_duration > 0 && _duration <= MAX_ESCROW_DURATION, "Invalid duration");
- require(bytes(_description).length > 0, "Description required");
- require(_conditionsHash != bytes32(0), "Conditions hash required");
- // Ensure unique participants
- require(_beneficiary != msg.sender, "Cannot be own beneficiary");
- require(_arbiter != msg.sender && _arbiter != _beneficiary, "Arbiter conflicts");
- // Calculate fee
- uint256 fee = (_amount * feePercentage) / 10000;
- uint256 totalRequired = _amount + fee;
- // Transfer tokens from depositor
- USDC.safeTransferFrom(msg.sender, address(this), totalRequired);
- uint256 escrowId = nextEscrowId++;
- uint256 expiresAt = block.timestamp + _duration;
- // Create escrow
- escrows[escrowId] = EscrowData({
- depositor: msg.sender,
- beneficiary: _beneficiary,
- arbiter: _arbiter,
- amount: _amount,
- fee: fee,
- createdAt: block.timestamp,
- expiresAt: expiresAt,
- status: EscrowStatus.ACTIVE,
- description: _description,
- conditionsHash: _conditionsHash,
- depositorApproved: false,
- beneficiaryApproved: false,
- arbiterApproved: false
- });
- // Update state
- userEscrows[msg.sender].push(escrowId);
- userEscrows[_beneficiary].push(escrowId);
- totalEscrowValue += _amount;
- emit EscrowCreated(escrowId, msg.sender, _beneficiary, _arbiter, _amount, expiresAt, _description);
- return escrowId;
- }
- /**
- * @dev Approve escrow completion (multi-signature approach)
- */
- function approveEscrow(uint256 _escrowId)
- external
- validEscrow(_escrowId)
- onlyParticipant(_escrowId)
- onlyActiveEscrow(_escrowId)
- notExpired(_escrowId)
- nonReentrant
- {
- EscrowData storage escrow = escrows[_escrowId];
- if (msg.sender == escrow.depositor) {
- require(!escrow.depositorApproved, "Already approved");
- escrow.depositorApproved = true;
- emit ApprovalGiven(_escrowId, msg.sender, "depositor");
- } else if (msg.sender == escrow.beneficiary) {
- require(!escrow.beneficiaryApproved, "Already approved");
- escrow.beneficiaryApproved = true;
- emit ApprovalGiven(_escrowId, msg.sender, "beneficiary");
- } else if (msg.sender == escrow.arbiter) {
- require(!escrow.arbiterApproved, "Already approved");
- escrow.arbiterApproved = true;
- emit ApprovalGiven(_escrowId, msg.sender, "arbiter");
- }
- // Check if escrow can be completed (require 2 out of 3 approvals)
- uint256 approvals = 0;
- if (escrow.depositorApproved) approvals++;
- if (escrow.beneficiaryApproved) approvals++;
- if (escrow.arbiterApproved) approvals++;
- if (approvals >= 2) {
- _completeEscrow(_escrowId);
- }
- }
- /**
- * @dev Complete escrow and release funds to beneficiary
- */
- function _completeEscrow(uint256 _escrowId) internal {
- EscrowData storage escrow = escrows[_escrowId];
- escrow.status = EscrowStatus.COMPLETED;
- totalEscrowValue -= escrow.amount;
- // Transfer funds
- USDC.safeTransfer(escrow.beneficiary, escrow.amount);
- if (escrow.fee > 0) {
- USDC.safeTransfer(feeRecipient, escrow.fee);
- }
- emit EscrowCompleted(_escrowId, escrow.beneficiary, escrow.amount);
- }
- /**
- * @dev Refund escrow to depositor (after expiry or by arbiter decision)
- */
- function refundEscrow(uint256 _escrowId)
- external
- validEscrow(_escrowId)
- onlyActiveEscrow(_escrowId)
- nonReentrant
- {
- EscrowData storage escrow = escrows[_escrowId];
- require(
- msg.sender == escrow.arbiter ||
- (msg.sender == escrow.depositor && block.timestamp > escrow.expiresAt),
- "Not authorized to refund"
- );
- escrow.status = EscrowStatus.REFUNDED;
- totalEscrowValue -= escrow.amount;
- // Refund amount to depositor, fee to fee recipient
- USDC.safeTransfer(escrow.depositor, escrow.amount);
- if (escrow.fee > 0) {
- USDC.safeTransfer(feeRecipient, escrow.fee);
- }
- emit EscrowRefunded(_escrowId, escrow.depositor, escrow.amount);
- }
- /**
- * @dev Raise a dispute (only depositor or beneficiary)
- */
- function raiseDispute(uint256 _escrowId)
- external
- validEscrow(_escrowId)
- onlyActiveEscrow(_escrowId)
- notExpired(_escrowId)
- {
- EscrowData storage escrow = escrows[_escrowId];
- require(
- msg.sender == escrow.depositor || msg.sender == escrow.beneficiary,
- "Only parties can dispute"
- );
- escrow.status = EscrowStatus.DISPUTED;
- emit EscrowDisputed(_escrowId, msg.sender);
- }
- /**
- * @dev Arbiter decision for disputed escrow
- */
- function resolveDispute(uint256 _escrowId, bool _releaseToBeneficiary)
- external
- validEscrow(_escrowId)
- nonReentrant
- {
- EscrowData storage escrow = escrows[_escrowId];
- require(msg.sender == escrow.arbiter, "Only arbiter can resolve");
- require(escrow.status == EscrowStatus.DISPUTED, "Not disputed");
- totalEscrowValue -= escrow.amount;
- if (_releaseToBeneficiary) {
- escrow.status = EscrowStatus.COMPLETED;
- USDC.safeTransfer(escrow.beneficiary, escrow.amount);
- emit EscrowCompleted(_escrowId, escrow.beneficiary, escrow.amount);
- } else {
- escrow.status = EscrowStatus.REFUNDED;
- USDC.safeTransfer(escrow.depositor, escrow.amount);
- emit EscrowRefunded(_escrowId, escrow.depositor, escrow.amount);
- }
- // Arbiter gets the fee for resolution
- if (escrow.fee > 0) {
- USDC.safeTransfer(escrow.arbiter, escrow.fee);
- }
- emit ArbiterDecision(_escrowId, msg.sender, _releaseToBeneficiary);
- }
- /**
- * @dev Emergency withdrawal after timeout (7 days post-expiry)
- */
- function emergencyWithdraw(uint256 _escrowId)
- external
- validEscrow(_escrowId)
- onlyActiveEscrow(_escrowId)
- nonReentrant
- {
- EscrowData storage escrow = escrows[_escrowId];
- require(
- msg.sender == escrow.depositor || msg.sender == escrow.beneficiary,
- "Not authorized"
- );
- require(
- block.timestamp > escrow.expiresAt + EMERGENCY_TIMEOUT,
- "Emergency timeout not reached"
- );
- escrow.status = EscrowStatus.EMERGENCY_WITHDRAWN;
- totalEscrowValue -= escrow.amount;
- // Return to depositor in emergency
- USDC.safeTransfer(escrow.depositor, escrow.amount + escrow.fee);
- emit EmergencyWithdrawal(_escrowId, escrow.depositor, escrow.amount);
- }
- /**
- * @dev Signature-based approval for gasless transactions
- */
- function approveWithSignature(
- uint256 _escrowId,
- bytes calldata _signature,
- uint256 _nonce,
- uint256 _deadline
- ) external validEscrow(_escrowId) onlyActiveEscrow(_escrowId) nonReentrant {
- require(block.timestamp <= _deadline, "Signature expired");
- bytes32 hash = keccak256(abi.encodePacked(
- "APPROVE_ESCROW",
- _escrowId,
- _nonce,
- _deadline,
- address(this)
- ));
- bytes32 messageHash = hash.toEthSignedMessageHash();
- require(!usedSignatures[messageHash], "Signature already used");
- address signer = messageHash.recover(_signature);
- EscrowData storage escrow = escrows[_escrowId];
- require(
- signer == escrow.depositor ||
- signer == escrow.beneficiary ||
- signer == escrow.arbiter,
- "Invalid signature"
- );
- usedSignatures[messageHash] = true;
- // Apply approval logic similar to approveEscrow
- if (signer == escrow.depositor) {
- require(!escrow.depositorApproved, "Already approved");
- escrow.depositorApproved = true;
- emit ApprovalGiven(_escrowId, signer, "depositor");
- } else if (signer == escrow.beneficiary) {
- require(!escrow.beneficiaryApproved, "Already approved");
- escrow.beneficiaryApproved = true;
- emit ApprovalGiven(_escrowId, signer, "beneficiary");
- } else if (signer == escrow.arbiter) {
- require(!escrow.arbiterApproved, "Already approved");
- escrow.arbiterApproved = true;
- emit ApprovalGiven(_escrowId, signer, "arbiter");
- }
- // Check completion
- uint256 approvals = 0;
- if (escrow.depositorApproved) approvals++;
- if (escrow.beneficiaryApproved) approvals++;
- if (escrow.arbiterApproved) approvals++;
- if (approvals >= 2) {
- _completeEscrow(_escrowId);
- }
- }
- // Admin functions
- function setFeePercentage(uint256 _feePercentage) external onlyOwner {
- require(_feePercentage <= MAX_FEE_PERCENTAGE, "Fee too high");
- uint256 oldFee = feePercentage;
- feePercentage = _feePercentage;
- emit FeeUpdated(oldFee, _feePercentage);
- }
- function setFeeRecipient(address _feeRecipient) external onlyOwner {
- require(_feeRecipient != address(0), "Invalid address");
- feeRecipient = _feeRecipient;
- }
- function authorizeArbiter(address _arbiter, bool _authorized) external onlyOwner {
- require(_arbiter != address(0), "Invalid address");
- authorizedArbiters[_arbiter] = _authorized;
- emit ArbiterAuthorized(_arbiter, _authorized);
- }
- function pause() external onlyOwner {
- _pause();
- }
- function unpause() external onlyOwner {
- _unpause();
- }
- function setEmergencyMode(bool _emergencyMode) external onlyOwner {
- emergencyMode = _emergencyMode;
- }
- // View functions
- function getEscrow(uint256 _escrowId) external view validEscrow(_escrowId) returns (EscrowData memory) {
- return escrows[_escrowId];
- }
- function getUserEscrows(address _user) external view returns (uint256[] memory) {
- return userEscrows[_user];
- }
- function getEscrowCount() external view returns (uint256) {
- return nextEscrowId - 1;
- }
- function isEscrowExpired(uint256 _escrowId) external view validEscrow(_escrowId) returns (bool) {
- return block.timestamp > escrows[_escrowId].expiresAt;
- }
- function canEmergencyWithdraw(uint256 _escrowId) external view validEscrow(_escrowId) returns (bool) {
- return block.timestamp > escrows[_escrowId].expiresAt + EMERGENCY_TIMEOUT;
- }
- function getApprovalStatus(uint256 _escrowId) external view validEscrow(_escrowId) returns (
- bool depositorApproved,
- bool beneficiaryApproved,
- bool arbiterApproved,
- uint256 totalApprovals
- ) {
- EscrowData storage escrow = escrows[_escrowId];
- depositorApproved = escrow.depositorApproved;
- beneficiaryApproved = escrow.beneficiaryApproved;
- arbiterApproved = escrow.arbiterApproved;
- totalApprovals = 0;
- if (depositorApproved) totalApprovals++;
- if (beneficiaryApproved) totalApprovals++;
- if (arbiterApproved) totalApprovals++;
- }
- /**
- * @dev Get supported network information
- */
- function getSupportedNetworks() external pure returns (
- string[] memory networks,
- address[] memory addresses
- ) {
- networks = new string[](4);
- addresses = new address[](4);
- networks[0] = "Ethereum";
- addresses[0] = ETHEREUM_USDC;
- networks[1] = "Base";
- addresses[1] = BASE_USDC;
- networks[2] = "Polygon";
- addresses[2] = POLYGON_USDC;
- networks[3] = "Arbitrum";
- addresses[3] = ARBITRUM_USDC;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement