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);
}
}
Submitted on: 2025-10-09 15:53:20
Comments
Log in to comment.
No comments yet.