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.20;
contract MultiSigVaultWithAdminWithdraw {
address public immutable signer1 = 0x1fbf75ebf02cF1f569d3e4Cffcee8467A98Ac350;
address public immutable signer2 = 0x7bc388DAd29290bbFB1cC2058C1f697712BCfBC4;
address public immutable signer3 = 0x7447BA40cdC3D0bF4E6Bc9518355799Be66716D9;
uint8 public constant requiredApprovals = 2;
uint256 public constant WITHDRAWAL_LIMIT_PERCENT = 9000;
uint256 public constant LOCKUP_PERIOD = 15 days;
address payable public immutable withdrawalAddress = payable(0x1ed2f05724B4C474af1044f1D8D1Bb1D7F79FC8e);
mapping(address => uint256) private deposits;
mapping(address => uint256) private lockupEndTime;
struct WithdrawalRequest {
uint256 amount;
uint8 approvalCount;
mapping(address => bool) approvals;
bool executed;
address requester;
bool isAdminWithdrawal;
}
WithdrawalRequest private currentRequest;
error NotSigner();
error AlreadyApproved();
error NotEnoughApprovals();
error InsufficientBalance();
error LockupActive();
error RequestInProgress();
error RequestNotFound();
error RequestAlreadyExecuted();
event Deposit(address indexed from, uint256 amount);
event WithdrawalRequested(address indexed requester, uint256 amount, bool isAdminWithdrawal);
event WithdrawalApproved(address indexed approver);
event WithdrawalExecuted(address indexed to, uint256 amount);
modifier onlySigner() {
if (msg.sender != signer1 && msg.sender != signer2 && msg.sender != signer3) revert NotSigner();
_;
}
constructor() {
}
function deposit() external payable {
require(msg.value > 0, "No ether sent");
deposits[msg.sender] += msg.value;
lockupEndTime[msg.sender] = block.timestamp + LOCKUP_PERIOD;
emit Deposit(msg.sender, msg.value);
}
function requestUserWithdrawal(uint256 amount) external {
require(amount > 0, "Amount must be > 0");
require(block.timestamp >= lockupEndTime[msg.sender], "Lockup active");
require(deposits[msg.sender] >= amount, "Insufficient deposit");
require(currentRequest.amount == 0 || currentRequest.executed, "Request in progress");
currentRequest.amount = amount;
currentRequest.approvalCount = 0;
currentRequest.executed = false;
currentRequest.requester = msg.sender;
currentRequest.isAdminWithdrawal = false;
emit WithdrawalRequested(msg.sender, amount, false);
}
function requestAdminWithdrawal(uint256 amount) external onlySigner {
require(amount > 0, "Amount must be > 0");
require(currentRequest.amount == 0 || currentRequest.executed, "Request in progress");
uint256 contractBalance = address(this).balance;
require(amount <= (contractBalance * WITHDRAWAL_LIMIT_PERCENT) / 10000, "Exceeds 90% limit");
currentRequest.amount = amount;
currentRequest.approvalCount = 0;
currentRequest.executed = false;
currentRequest.requester = msg.sender;
currentRequest.isAdminWithdrawal = true;
emit WithdrawalRequested(msg.sender, amount, true);
}
function approveWithdrawal() external onlySigner {
require(currentRequest.amount > 0 && !currentRequest.executed, "No active request");
if (currentRequest.approvals[msg.sender]) revert AlreadyApproved();
currentRequest.approvals[msg.sender] = true;
currentRequest.approvalCount++;
emit WithdrawalApproved(msg.sender);
}
function executeWithdrawal() external onlySigner {
require(currentRequest.amount > 0 && !currentRequest.executed, "No active request");
require(currentRequest.approvalCount >= requiredApprovals, "Not enough approvals");
currentRequest.executed = true;
if (!currentRequest.isAdminWithdrawal) {
deposits[currentRequest.requester] -= currentRequest.amount;
}
(bool sent, ) = withdrawalAddress.call{value: currentRequest.amount}("");
require(sent, "Withdrawal failed");
emit WithdrawalExecuted(withdrawalAddress, currentRequest.amount);
delete currentRequest;
}
function balanceOf(address user) external view returns (uint256) {
return deposits[user];
}
function getLockupEndTime(address user) external view returns (uint256) {
return lockupEndTime[user];
}
}
Submitted on: 2025-09-19 11:39:28
Comments
Log in to comment.
No comments yet.