ETHDistributor

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/**
 * @title ETHDistributor
 * @dev Accumulates ETH and distributes to two recipients when threshold is reached
 */
contract ETHDistributor {
    address public owner;
    address public recipient1;
    address public recipient2;
    
    uint256 public threshold;
    uint256 public recipient1Share; // In basis points (e.g., 5000 = 50%)
    uint256 public recipient2Share; // In basis points (e.g., 5000 = 50%)
    
    uint256 private constant BASIS_POINTS = 10000; // 100% = 10000 basis points
    
    event ETHReceived(address indexed sender, uint256 amount);
    event ETHDistributed(uint256 amount, uint256 toRecipient1, uint256 toRecipient2);
    event ThresholdUpdated(uint256 newThreshold);
    event RecipientsUpdated(address recipient1, address recipient2);
    event SharesUpdated(uint256 recipient1Share, uint256 recipient2Share);
    event EmergencyWithdraw(address indexed to, uint256 amount);
    event TransferFailed(address indexed recipient, uint256 amount);
    
    modifier onlyOwner() {
        require(msg.sender == owner, "Only owner can call this function");
        _;
    }
    
    /**
     * @dev Constructor sets initial parameters
     * @param _recipient1 First recipient address
     * @param _recipient2 Second recipient address
     * @param _thresholdInCentiETH Threshold in 0.01 ETH units (e.g., 10 = 0.1 ETH, 100 = 1 ETH)
     * @param _recipient1Percentage Share for recipient1 as percentage (e.g., 50 = 50%)
     */
    constructor(
        address _recipient1,
        address _recipient2,
        uint256 _thresholdInCentiETH,
        uint256 _recipient1Percentage
    ) {
        require(_recipient1 != address(0), "Recipient1 cannot be zero address");
        require(_recipient2 != address(0), "Recipient2 cannot be zero address");
        require(_recipient1Percentage <= 100, "Percentage cannot exceed 100");
        
        owner = msg.sender;
        recipient1 = _recipient1;
        recipient2 = _recipient2;
        
        // Convert 0.01 ETH units to wei (multiply by 10^16)
        // Example: 10 → 0.1 ETH, 100 → 1 ETH, 250 → 2.5 ETH
        threshold = _thresholdInCentiETH > 0 ? _thresholdInCentiETH * 10**16 : 1 ether;
        
        // Convert percentage to basis points
        // Example: 50% → 5000 basis points, 70% → 7000 basis points
        recipient1Share = _recipient1Percentage * 100;
        recipient2Share = BASIS_POINTS - recipient1Share;
    }
    
    /**
     * @dev Receive function to accept ETH
     */
    receive() external payable {
        emit ETHReceived(msg.sender, msg.value);
        _checkAndDistribute();
    }
    
    /**
     * @dev Fallback function to accept ETH
     */
    fallback() external payable {
        emit ETHReceived(msg.sender, msg.value);
        _checkAndDistribute();
    }
    
    /**
     * @dev Internal function to check threshold and distribute if reached
     * Wrapped to prevent reverting the receive function
     */
    function _checkAndDistribute() private {
        if (address(this).balance >= threshold) {
            // Try to distribute, but don't revert receive() if it fails
            try this.attemptDistribute() {
                // Distribution succeeded
            } catch {
                // Distribution failed (likely recipient1 transfer failed)
                // ETH stays in contract for retry, but receive() doesn't revert
            }
        }
    }
    
    /**
     * @dev External function for safe distribution attempts
     * Must be external to be called via try-catch
     */
    function attemptDistribute() external {
        require(msg.sender == address(this), "Only internal calls");
        _distribute();
    }
    
    /**
     * @dev Internal function to distribute ETH to recipients
     * Requires recipient1 transfer to succeed, allows recipient2 to fail gracefully
     */
    function _distribute() private {
        uint256 balance = address(this).balance;
        require(balance > 0, "No ETH to distribute");
        
        uint256 amountToRecipient1 = (balance * recipient1Share) / BASIS_POINTS;
        uint256 amountToRecipient2 = balance - amountToRecipient1; // Remaining amount
        
        // Transfer to recipient1 - MUST succeed or entire distribution fails
        (bool success1, ) = recipient1.call{value: amountToRecipient1, gas: 50000}("");
        require(success1, "Transfer to recipient1 failed");
        
        // Transfer to recipient2 - if fails, just log it (acceptable rare edge case)
        (bool success2, ) = recipient2.call{value: amountToRecipient2, gas: 50000}("");
        if (!success2) {
            emit TransferFailed(recipient2, amountToRecipient2);
        }
        
        emit ETHDistributed(balance, amountToRecipient1, amountToRecipient2);
    }
    
    /**
     * @dev Manual distribution trigger (only owner)
     */
    function manualDistribute() external onlyOwner {
        require(address(this).balance > 0, "No ETH to distribute");
        _distribute();
    }
    
    /**
     * @dev Update threshold value (only owner)
     * @param _newThresholdInCentiETH New threshold in 0.01 ETH units (e.g., 10 = 0.1 ETH, 200 = 2 ETH)
     */
    function updateThreshold(uint256 _newThresholdInCentiETH) external onlyOwner {
        require(_newThresholdInCentiETH > 0, "Threshold must be greater than 0");
        threshold = _newThresholdInCentiETH * 10**16;
        emit ThresholdUpdated(threshold);
    }
    
    /**
     * @dev Update recipient addresses (only owner)
     * @param _recipient1 New first recipient address
     * @param _recipient2 New second recipient address
     */
    function updateRecipients(address _recipient1, address _recipient2) external onlyOwner {
        require(_recipient1 != address(0), "Recipient1 cannot be zero address");
        require(_recipient2 != address(0), "Recipient2 cannot be zero address");
        
        recipient1 = _recipient1;
        recipient2 = _recipient2;
        emit RecipientsUpdated(_recipient1, _recipient2);
    }
    
    /**
     * @dev Update distribution shares (only owner)
     * @param _recipient1Percentage Share for recipient1 as percentage (e.g., 50 = 50%, 70 = 70%)
     */
    function updateShares(uint256 _recipient1Percentage) external onlyOwner {
        require(_recipient1Percentage <= 100, "Percentage cannot exceed 100");
        
        recipient1Share = _recipient1Percentage * 100;
        recipient2Share = BASIS_POINTS - recipient1Share;
        emit SharesUpdated(recipient1Share, recipient2Share);
    }
    
    /**
     * @dev Emergency withdraw function (only owner)
     * Withdraws all ETH to the owner
     */
    function emergencyWithdraw() external onlyOwner {
        uint256 balance = address(this).balance;
        require(balance > 0, "No ETH to withdraw");
        
        (bool success, ) = owner.call{value: balance}("");
        require(success, "Emergency withdraw failed");
        
        emit EmergencyWithdraw(owner, balance);
    }
    
    /**
     * @dev Transfer ownership (only owner)
     * @param newOwner Address of the new owner
     */
    function transferOwnership(address newOwner) external onlyOwner {
        require(newOwner != address(0), "New owner cannot be zero address");
        owner = newOwner;
    }
    
    /**
     * @dev Get current contract balance
     * @return Current balance in wei
     */
    function getBalance() external view returns (uint256) {
        return address(this).balance;
    }
    
    /**
     * @dev Get current distribution shares
     * @return Share percentages for both recipients in basis points
     */
    function getShares() external view returns (uint256, uint256) {
        return (recipient1Share, recipient2Share);
    }
    
    /**
     * @dev Get current distribution shares as percentages
     * @return percentage1 Percentage for recipient1 (0-100)
     * @return percentage2 Percentage for recipient2 (0-100)
     */
    function getSharesAsPercentages() external view returns (uint256 percentage1, uint256 percentage2) {
        return (recipient1Share / 100, recipient2Share / 100);
    }
    
    /**
     * @dev Get threshold in user-friendly format
     * @return thresholdInCentiETH Threshold in 0.01 ETH units
     * @return thresholdInWei Threshold in wei (actual value)
     */
    function getThreshold() external view returns (uint256 thresholdInCentiETH, uint256 thresholdInWei) {
        return (threshold / 10**16, threshold);
    }
}

Tags:
Multisig, Multi-Signature|addr:0xe7041f158b61ba63c8b3420eee4e0e55024bc24e|verified:true|block:23540300|tx:0xd19403822ea8ed49fe1d4a4ae20cd476b94bb33233aefa54e3634c55b487e856|first_check:1760018000

Submitted on: 2025-10-09 15:53:20

Comments

Log in to comment.

No comments yet.