// ========================================================================================= // Multisig — M of N owners must approve before native STR leaves this contract. // // WHAT IT IS FOR // Shared custody of STR. The contract holds a balance; any owner can propose a payment; // the payment only happens once `threshold` distinct owners have approved that exact // proposal. // // WHAT IT CAN DO, STATED PRECISELY — READ THIS BEFORE YOU CHOOSE IT // AresLang has NO cross-contract calls. This multisig therefore CANNOT execute an // arbitrary call on another contract, cannot move a ZK13 token held elsewhere, and cannot // act as a general-purpose "wallet that can call anything". Its entire authority is: pay // native STR out of its own balance, to an address named in a proposal. If you need more // than that, this template is not a smaller version of what you need — it is a different // thing, and the thing you need does not exist yet. // // WHAT IT GUARANTEES // * A non-owner cannot propose, approve or execute. Every entry point checks `isOwner`. // * One owner is one approval. A second `approve` from the same owner on the same proposal // reverts rather than incrementing the count, so M-of-N cannot be cleared by one key // calling M times. // * An approval binds the exact action. `to` and `amount` are written once, at `propose`, // and there is no function anywhere in this contract that edits them — so approving one // payment and executing another is not a state this contract can reach. // * A proposal executes at most once. `executed` is set before the transfer, and only an // OWNER may trigger it — the opposite of the DAO treasury template, and deliberate: there // the roll is public and execution carries no authority, whereas here the owner set is the // whole of the contract's identity and nothing outside it should touch the balance at all. // * The owner set and the threshold are frozen at `seal`. There is no function that adds an // owner, removes one, or changes M afterwards, so "M-of-N can lower M to 1" — which makes // an M-of-N really a 1-of-N over time — cannot happen here. // // THE PRICE OF THAT LAST GUARANTEE, AND IT IS A REAL ONE // A LOST KEY IS PERMANENT. There is no rotation. If you seal 3-of-5 and two owners lose // their keys, you still have 3 and you are fine; lose a third and the balance is // unreachable forever, because the code is immutable. Choose N with generous margin, or // accept that this contract holds an amount you can afford to lose access to. // // THE IDENTITY RULE — CORRECTED 2026-09-08 // This block described a rule that no longer exists. `ValueJournal.receiveRefusal` used to // require a payment recipient to hold an ALLOCATED STR.domain or be a deployed contract; it // now refuses only Hnitbjorg reserved accounts. The gate became MORE permissive, so nothing // built against the old text is unsafe — but it instructed issuers to pre-flight a check that // no longer applies, and told them a payout would revert when it will not. // // Today: any well-formed, non-reserved address, or a deployed contract, may be paid. What a // recipient with no proved domain still cannot do is SPEND what it receives — a fact about // that holder, not about this contract. // // HOW IT IS FUNDED // By plain STR transfers to its address. A call record carries no value, and the contract // cannot see who funded it, so there is no per-depositor accounting and there is no // withdrawal right — everything here belongs to the owner set collectively. // // HOW IT IS TYPICALLY MISUSED // * Sealing with `threshold` = 1. That is not a multisig; it is N single keys, any one of // which can drain it. The contract permits it because 1-of-N is a legitimate shape for a // hot spending account, but it must be a decision and not an accident. // * Counting approvals by iterating an owner list. This contract keeps a counter instead: // at 55 gas per map read plus 20 per loop iteration, a few thousand owners is a contract // that becomes permanently uncallable with the funds behind the frozen function. That is // also why N is bounded at 20 — edit `maxOwners` before you deploy if you need more, and // understand that you are paying for it in nothing at all, since nothing iterates. // * Forgetting that anyone with a proved STR.domain can read every proposal. Nothing here // is private; a pending payout is public from the moment it is proposed. // * Under-declaring the gas limit. MEASURED: addOwner 919, seal 736, propose 1,400, // approve 938, execute 3,380. Declare at least 34,000. // ========================================================================================= contract Multisig { // ---- edit before deploying ----------------------------------------------------------- // A hard bound on N. Nothing in this contract iterates over owners, so this is not a gas // limit — it is a limit on how far a setup mistake can go before `seal` closes it. state maxOwners: uint = 20; // ------------------------------------------------------------------------------------- // The deployer runs setup and then has no special power at all: after `seal` there is no // function that checks `installer`. state installer: address = caller; state sealed: bool = false; state ownerCount: uint = 0; state threshold: uint = 0; state proposalCount: uint = 0; state isOwner: map
; // One proposal is a row across these five maps, keyed by id. AresLang has no structs; this // is what a struct looks like without them, and the maps must be written together. state payTo: map