🚀 [멤버십]
상위 1% 수익률 도전! 코인 텐베거 클럽 가입하기 →
다중서명 기업 지갑 샘플 코드입니다.
교육용이니 참고하시기 바랍니다.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
// 4-Eyes Principle 기반 다중 서명(Multi-Sig) 기업 지갑
contract CorporateMultiSig {
// 기업 자금 관리 권한을 가진 임원(Owner)들의 지갑 주소 목록
address[] public owners;
mapping(address => bool) public isOwner;
// 자금 이체를 위해 필요한 최소 서명(결재) 개수 (예: 3명 중 2명)
uint256 public requiredApprovals;
// 결재를 위해 상신된 자금 이체(트랜잭션) 구조체
struct Transaction {
address to; // 돈을 받을 대상
uint256 amount; // 보낼 금액(ETH)
bool executed; // 실제 집행(송금) 완료 여부
uint256 approvalCount;// 현재까지 받은 결재(서명) 개수
}
// 상신된 전체 트랜잭션 목록 (인덱스 번호가 결재 문서 번호가 됨)
Transaction[] public transactions;
// 특정 결재 문서(트랜잭션 인덱스)에 특정 임원이 서명했는지 기록하는 장부
mapping(uint256 => mapping(address => bool)) public approved;
// 이벤트 로그 (감사 기록용)
event Deposit(address indexed sender, uint256 amount, uint256 balance);
event SubmitTransaction(address indexed owner, uint256 indexed txIndex, address indexed to, uint256 amount);
event ApproveTransaction(address indexed owner, uint256 indexed txIndex);
event ExecuteTransaction(address indexed owner, uint256 indexed txIndex);
// 오직 임원(Owner)만 실행할 수 있도록 통제하는 접근 제어자
modifier onlyOwner() {
require(isOwner[msg.sender], "Not an authorized owner");
_;
}
// 배포 시 임원 주소 목록과 최소 결재 필요 인원수를 설정합니다.
constructor(address[] memory _owners, uint256 _requiredApprovals) {
require(_owners.length > 0, "Owners required");
require(_requiredApprovals > 0 && _requiredApprovals <= _owners.length, "Invalid required number of approvals");
for (uint256 i = 0; i < _owners.length; i++) {
address owner = _owners[i];
require(owner != address(0), "Invalid owner address");
require(!isOwner[owner], "Owner not unique"); // 중복 등록 방지
isOwner[owner] = true;
owners.push(owner);
}
requiredApprovals = _requiredApprovals;
}
// [자금 수신] 법인 지갑으로 이더리움을 입금받는 함수 (누구나 입금 가능)
receive() external payable {
emit Deposit(msg.sender, msg.value, address(this).balance);
}
// 1. [Maker] 자금 이체 기안 올리기 (트랜잭션 상신)
function submitTransaction(address _to, uint256 _amount) public onlyOwner {
uint256 txIndex = transactions.length;
transactions.push(Transaction({
to: _to,
amount: _amount,
executed: false,
approvalCount: 0
}));
emit SubmitTransaction(msg.sender, txIndex, _to, _amount);
}
// 2. [Checker] 상신된 결재 문서(트랜잭션)에 서명하기 (승인)
function approveTransaction(uint256 _txIndex) public onlyOwner {
require(_txIndex < transactions.length, "Transaction does not exist");
require(!transactions[_txIndex].executed, "Transaction already executed");
require(!approved[_txIndex][msg.sender], "Transaction already approved by you");
approved[_txIndex][msg.sender] = true;
transactions[_txIndex].approvalCount += 1;
emit ApproveTransaction(msg.sender, _txIndex);
}
// 3. [Execution] 서명 요건이 충족되면 자금 이체 실행 (집행)
function executeTransaction(uint256 _txIndex) public onlyOwner {
require(_txIndex < transactions.length, "Transaction does not exist");
require(!transactions[_txIndex].executed, "Transaction already executed");
Transaction storage txn = transactions[_txIndex];
// 최소 서명(결재) 개수를 채웠는지 확인
require(txn.approvalCount >= requiredApprovals, "Not enough approvals");
txn.executed = true; // 중복 집행을 막기 위해 상태 먼저 변경 (Reentrancy 방지)
// 실제 이더리움 송금
(bool success, ) = txn.to.call{value: txn.amount}("");
require(success, "Transaction execution failed");
emit ExecuteTransaction(msg.sender, _txIndex);
}
// 법인 지갑 잔액 확인
function getBalance() public view returns (uint256) {
return address(this).balance;
}
}
댓글