Contracts

BuzzerTreasury

Git Source

Title: BuzzerTreasury — two-founder, ten-wallet fee sink

Author: Buzzer

Receives everything Buzzer sends — ETH, any ERC-20, and NFTs (ERC-721 / ERC-1155). Ownership is split between two founders, A and B, each holding five wallets (ten slots total, fixed for the life of the contract). There is no owner, no guardian and no admin.

Fungibles (ETH & ERC-20)

withdraw(token) is callable by any one of the ten current wallets, at most once per 7 days per token. It pays a fixed 10% of the contract's current balance of that token, split equally — 1% to each of the ten current slot addresses. A slot that cannot receive is skipped (its share stays), so one bad address never blocks the others. This bounds a single compromised wallet to its own 1%/week until it is rotated out — it can neither drain more nor change the split.

NFTs

NFTs can't be split ten ways, so by the founders' choice any one of the ten wallets may transfer a held NFT out to any address, with no cooldown and no quorum. The contract implements the receiver hooks so NFT transfers into it never fail.

Recovery (per group, 3-of-4, 48h)

To change the address in a slot, three distinct members of that slot's own group — excluding the slot being changed — approve the same new address; after a 48h timelock (cancellable by any group member) it takes effect. Group A can only rotate A-slots and group B only B-slots: A cannot recover B and B cannot recover A.

State Variables

GROUP_SIZE

Wallets per founder group.

uint256 public constant GROUP_SIZE = 5

WALLETS

Total wallet slots. Slots 0-4 are group A; 5-9 are group B.

uint256 public constant WALLETS = 10

WITHDRAW_BPS

Share of the current balance paid per withdraw, in basis points (10%).

uint256 public constant WITHDRAW_BPS = 1000

WITHDRAW_INTERVAL

Minimum time between two withdrawals of the same token.

uint256 public constant WITHDRAW_INTERVAL = 7 days

ROTATION_APPROVALS

Distinct in-group approvals (excluding the target slot) required to rotate a wallet.

uint256 public constant ROTATION_APPROVALS = 3

ROTATION_TIMELOCK

Delay between the 3rd approval and a rotation taking effect.

uint256 public constant ROTATION_TIMELOCK = 48 hours

wallets

Current address in each slot. 0-4 = group A, 5-9 = group B.

address[WALLETS] public wallets

slotPlus1

Reverse index: slot + 1 for a current wallet, 0 for any other address.

mapping(address => uint256) internal slotPlus1

lastWithdrawAt

Last withdrawal time per token (address(0) = ETH). ETH and each ERC-20 have independent clocks.

mapping(address token => uint64) public lastWithdrawAt

rotations

Rotation proposal per slot.

mapping(uint256 slot => Rotation) public rotations

Functions

constructor

constructor(address[WALLETS] memory initial) ;

Parameters

Name Type Description
initial address[WALLETS] The ten founder wallets: indices 0-4 = group A, 5-9 = group B. All non-zero and distinct.

receive

Accepts ETH from anyone (Buzzer fee pushes, sweeps, donations).

receive() external payable;

allWallets

All ten current wallet addresses (0-4 = group A, 5-9 = group B).

function allWallets() external view returns (address[WALLETS] memory);

slotOf

Slot of a (and whether it is currently a wallet at all).

function slotOf(address a) external view returns (uint256 slot, bool isWallet);

nextWithdrawal

Amount the next withdraw(token) would pay each wallet, and when it becomes possible.

function nextWithdrawal(address token) external view returns (uint256 perWallet, uint256 availableAt);

withdraw

Pay 10% of the current balance of token, split equally (1% each) across the ten wallets. Callable by any current wallet, at most once per WITHDRAW_INTERVAL per token.

The clock is stamped before any transfer, so a re-entrant withdraw hits TooSoon — a malicious recipient cannot draw more than its share. A wallet that cannot receive (reverting receive, token blocklist, …) is skipped with PayoutSkipped; its 1% stays and rolls into the next round's base. Integer dust (from the /10) also stays. ETH sends carry a 50k stipend.

function withdraw(address token) external;

Parameters

Name Type Description
token address ERC-20 to withdraw, or address(0) for ETH.

withdrawMany

Withdraw several tokens in one transaction. Callable by any current wallet.

Convenience batch over withdraw: each token is distributed on its own 7-day clock. A token that is still on cooldown, or has nothing to pay, is silently skipped — so a mix of ready and not-ready tokens still pays out the ready ones instead of reverting the whole batch. Duplicate entries are deduped naturally (the first stamps the clock; the rest hit the cooldown).

function withdrawMany(address[] calldata tokens) external;

Parameters

Name Type Description
tokens address[] ERC-20 addresses to withdraw; use address(0) for ETH.

_due

True once token's 7-day cooldown has elapsed (also true on first use, since real timestamps far exceed WITHDRAW_INTERVAL).

function _due(address token) internal view returns (bool);

_distribute

Pay 10% of token's current balance, split equally (1% each) across the ten wallets. Returns the per-wallet amount, or 0 if there was nothing to pay (in which case the clock is NOT stamped, so an empty token isn't put on cooldown). Callers must check _due first. The clock is stamped before any transfer, so a re-entrant withdraw of the same token hits the cooldown and can't double-pay. A wallet that cannot receive (reverting receive, token blocklist, …) is skipped with PayoutSkipped; its 1% stays and rolls into the next round's base. Integer dust also stays.

function _distribute(address token) internal returns (uint256 per);

withdrawERC721

Transfer a held ERC-721 out to to. Callable by any current wallet; no cooldown, no quorum.

function withdrawERC721(address collection, uint256 tokenId, address to) external;

Parameters

Name Type Description
collection address ERC-721 contract.
tokenId uint256 Token to send.
to address Recipient.

withdrawERC1155

Transfer a held ERC-1155 balance out to to. Callable by any current wallet; no cooldown, no quorum.

function withdrawERC1155(address collection, uint256 id, uint256 amount, address to) external;

approveRotation

Approve rotating slot to newAddr. Caller must be in the same group as slot and must not be the target slot. The 3rd distinct approval queues the change behind a 48h timelock.

A proposal is identified by its newAddr; approving a different address for the same slot supersedes the current proposal (approvals reset). The target slot is excluded, so a single compromised wallet can never approve its own rotation.

function approveRotation(uint256 slot, address newAddr) external;

executeRotation

Execute a queued rotation once its 48h timelock has passed. Anyone may finalise.

function executeRotation(uint256 slot) external;

cancelRotation

Cancel a pending rotation for slot. Any member of that slot's group EXCEPT the slot being changed may cancel (the target cannot veto its own eviction).

function cancelRotation(uint256 slot) external;

onERC721Received

function onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4);

onERC1155Received

function onERC1155Received(address, address, uint256, uint256, bytes calldata) external pure returns (bytes4);

onERC1155BatchReceived

function onERC1155BatchReceived(address, address, uint256[] calldata, uint256[] calldata, bytes calldata)
    external
    pure
    returns (bytes4);

supportsInterface

ERC-165: advertises ERC-165 + the ERC-721 and ERC-1155 receiver interfaces.

function supportsInterface(bytes4 id) external pure returns (bool);

_slotOf

Slot index of a current wallet; reverts NotWallet otherwise.

function _slotOf(address a) internal view returns (uint256);

_group

Group of a slot: 0 = A (slots 0-4), 1 = B (slots 5-9).

function _group(uint256 slot) internal pure returns (uint256);

_sendETH

ETH send with a fixed 50k stipend; returns success instead of reverting.

function _sendETH(address to, uint256 amount) internal returns (bool ok);

_balanceOf

balanceOf(this) via staticcall; reverts TransferFailed on a non-conforming token.

function _balanceOf(address token) internal view returns (uint256);

_tryTransfer

transfer that returns success instead of reverting; tolerates no-return tokens.

function _tryTransfer(address token, address to, uint256 amount) internal returns (bool);

Events

Withdrawn

A withdrawal round. total is what was actually delivered; perWallet the intended 1% share.

event Withdrawn(address indexed token, uint256 total, uint256 perWallet);

PayoutSkipped

A wallet could not receive its share; the amount stays in the treasury.

event PayoutSkipped(address indexed token, address indexed to, uint256 amount);

NFTWithdrawn

An NFT was transferred out by one of the wallets.

event NFTWithdrawn(address indexed collection, address indexed to, uint256 id, uint256 amount, address by);

RotationApproved

A group member approved a rotation of slot to newAddr.

event RotationApproved(uint256 indexed slot, address indexed newAddr, address indexed by, uint8 approvals);

RotationQueued

The 3rd approval queued a rotation; it executes at executeAfter.

event RotationQueued(uint256 indexed slot, address indexed newAddr, uint64 executeAfter);

RotationExecuted

A rotation took effect: slot now holds newAddr.

event RotationExecuted(uint256 indexed slot, address indexed oldAddr, address indexed newAddr);

RotationCancelled

A pending rotation was cancelled by a group member.

event RotationCancelled(uint256 indexed slot, address indexed by);

Errors

NotWallet

Caller is not one of the ten current wallets.

error NotWallet();

NotGroupMember

Caller is not in the target slot's group, or is the target slot itself.

error NotGroupMember();

TooSoon

Withdrawal interval or rotation timelock has not elapsed.

error TooSoon();

Zero

Zero address not allowed.

error Zero();

DuplicateWallet

Address is already one of the ten wallets.

error DuplicateWallet();

BadSlot

Slot index out of range (must be 0-9).

error BadSlot();

AlreadyApproved

This group member has already approved the current proposal.

error AlreadyApproved();

NoProposal

No active rotation proposal for this slot.

error NoProposal();

NothingToWithdraw

Per-wallet share would be zero.

error NothingToWithdraw();

TransferFailed

ERC-20 balance query or NFT transfer failed.

error TransferFailed();

Structs

Rotation

An in-flight wallet rotation for one slot.

struct Rotation {
    address newAddr;
    uint64 executeAfter;
    uint16 approverMask;
    uint8 approvals;
}

Properties

Name Type Description
newAddr address Proposed replacement address (0 = no proposal).
executeAfter uint64 Earliest execution time; 0 until the 3rd approval queues it.
approverMask uint16 Bitmask of approver slot indices (bit i set = slot i approved).
approvals uint8 Distinct approvals so far.