Cycle

The Scriptorium

Smart Assembly code templates and tools for on-chain development in Eve Frontier.

← Back to Example Code

Tribe Treasury

Tribe Management

A simple multi-sig-style tribal treasury. Officers can propose withdrawals, and a configurable number of approvals are required before funds are released. Tracks deposit and withdrawal history.

tribetreasurymulti-siggovernance
tribe-treasury.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

/**
 * @title TribeTreasury
 * @notice Simple multi-sig treasury for tribal fund management.
 */
contract TribeTreasury {
    address public chief;
    mapping(address => bool) public officers;
    uint256 public requiredApprovals;

    struct Proposal {
        address recipient;
        uint256 amount;
        string reason;
        uint256 approvalCount;
        bool executed;
        mapping(address => bool) approvals;
    }

    uint256 public proposalCount;
    mapping(uint256 => Proposal) public proposals;

    event Deposited(address indexed from, uint256 amount);
    event ProposalCreated(uint256 indexed id, address recipient, uint256 amount);
    event ProposalApproved(uint256 indexed id, address approver);
    event ProposalExecuted(uint256 indexed id, address recipient, uint256 amount);

    constructor(uint256 _requiredApprovals) {
        chief = msg.sender;
        officers[msg.sender] = true;
        requiredApprovals = _requiredApprovals;
    }

    modifier onlyChief() {
        require(msg.sender == chief, "Not chief");
        _;
    }

    modifier onlyOfficer() {
        require(officers[msg.sender], "Not an officer");
        _;
    }

    function addOfficer(address _officer) external onlyChief {
        officers[_officer] = true;
    }

    function removeOfficer(address _officer) external onlyChief {
        require(_officer != chief, "Cannot remove chief");
        officers[_officer] = false;
    }

    function deposit() external payable {
        require(msg.value > 0, "Must send value");
        emit Deposited(msg.sender, msg.value);
    }

    function propose(
        address _recipient,
        uint256 _amount,
        string calldata _reason
    ) external onlyOfficer returns (uint256) {
        require(_amount <= address(this).balance, "Insufficient balance");
        uint256 id = proposalCount++;
        Proposal storage p = proposals[id];
        p.recipient = _recipient;
        p.amount = _amount;
        p.reason = _reason;
        emit ProposalCreated(id, _recipient, _amount);
        return id;
    }

    function approve(uint256 _id) external onlyOfficer {
        Proposal storage p = proposals[_id];
        require(!p.executed, "Already executed");
        require(!p.approvals[msg.sender], "Already approved");
        p.approvals[msg.sender] = true;
        p.approvalCount++;
        emit ProposalApproved(_id, msg.sender);

        if (p.approvalCount >= requiredApprovals) {
            p.executed = true;
            (bool sent, ) = p.recipient.call{value: p.amount}("");
            require(sent, "Transfer failed");
            emit ProposalExecuted(_id, p.recipient, p.amount);
        }
    }

    receive() external payable {
        emit Deposited(msg.sender, msg.value);
    }
}