const IndexCrews
IndexCrews is how many crews the index page lists.
Package crews is small-group coordination you can create in one transaction: three to fifteen people with a shared po...
A crew is three to fifteen people with a shared pot, a way to decide, and a way to leave with their share. One transaction to start one.
The chain wiring for p/moul/x/social/crew, which
holds the share math and the proposal state. This realm reads the caller, reads
the coins attached to the call, asks the engine, and renders the result.
| call | |
|---|---|
Create(name) |
payable. You become the crew's first member, your shares bought out of what you sent at InitialPricePerShare. |
Join(crewID) |
payable. Mints shares at what a share is worth now: amount * totalShares / treasury, rounded down. |
Fund(crewID) |
payable. Adds to the treasury and mints nothing, so every existing share is worth more. |
Propose(crewID, text) |
any member. Open for VoteBlocks blocks. |
Vote(proposalID, yes) |
weighted by the shares you hold right now, changeable while it is open. |
Close(proposalID) |
anyone, after the deadline. Records passed or failed by share weight. |
Ragequit(crewID) |
burn every share you hold, be credited your pro-rata of the treasury, leave. |
Withdraw() |
collect what you were credited. |
Reads: Get, Count, SharesOf, MemberCount, Treasury, ValuePerShare,
ProposalOf, Tally, CreditOf, TotalOwed.
Render serves three views: the index, :crew/<id>, and :proposal/<id>.
The shares are the token. They are the vote weight and the claim on the treasury at once, minted by joining and burned by leaving, so nothing about them is decorative and nobody has to be persuaded they are worth something.
v0 keeps them as an internal ledger rather than one GRC20 per crew, because a
GRC20 per crew means a realm per crew. A transferable share belongs in
p/moul/x/social/coin, the sibling that refuses to
exist until its mint rule, sink and buyer are declared. This realm does not
import it.
One realm address holds every crew's treasury, with per-crew accounting inside it. The chain sees one balance; which crew owns which part of it is a number in this realm's state.
So a bug in that accounting is a bug across crews, not inside one. An arithmetic error that over-credits one crew's ragequit pays it out of another crew's money, and nothing at the bank layer would refuse that transfer: as far as the chain is concerned it is all the same address paying itself out.
The fix is the instance-per-realm pattern
(EFFECTIVE_GNO.md section 1.4, in this repository): one realm
per crew, each holding its own coins at its own address, with
p/moul/x/social/crew as the shared engine. That is v1. It is not v0 because
deploying a realm per crew is a publish per crew, which is the opposite of
"create it in one transaction", and the one transaction is the whole product
claim.
A ragequit credits an internal ledger and the leaver calls Withdraw
themselves. One address that cannot be paid cannot wedge anybody else, and the
credit is zeroed before the coins move, so a reentrant call finds nothing left
to take.
Nothing. A v0 proposal is advisory text: passing one records that the crew agreed by share weight at a point in time, and moves no coins, changes no membership and binds no code. Executing a payout is the next step, and it is what would turn this into a treasury contract rather than a notice board.
private = true, so it can be redeployed at this path by its creator, and no
other realm may import it. The trade is the usual one: a redeploy wipes every
package-level variable, so every crew, every share and every credit would go
with it while the coins stayed at the address. Nothing imports this realm and
nothing is meant to, so the redeploy door is worth more than the import one
while the design is still moving. The v1 above is the move that closes it.
Part of moul/gno-contracts — moul's versioned gno.land contracts. See the repository for the full catalog, build/test tooling, and usage.
Dependency graph:

🧪 Highly experimental — potentially vibe-coded. Not audited; may break, change, or be removed at any time. Do not use with anything of value. Full disclaimer: DISCLAIMER.
Package crews is small-group coordination you can create in one transaction: three to fifteen people with a shared pot, a way to decide, and a way to leave with their share.
It is the chain wiring for gno.land/p/moul/x/social/crew, which holds the share math and the proposal state. This realm reads the caller, reads the coins attached to the call, asks the engine, and renders the result.
1Create pay in, and you are the crew's first member
2Join pay in at what a share is worth NOW, not at what it cost them
3Fund pay in and mint nothing, so every existing share is worth more
4Propose any member opens an advisory question
5Vote weighted by the shares you hold, changeable while it is open
6Close anyone, once the deadline has gone by
7Ragequit burn your shares, be credited your slice, leave
8Withdraw collect what you were credited
There is no second asset to issue. A share is the vote weight and the claim on the treasury at the same moment, minted by paying in and burned by leaving, so nothing about it is decorative and nobody has to be persuaded it is worth something. v0 keeps the shares as an internal ledger rather than one GRC20 per crew, because a GRC20 per crew means a realm per crew. A transferable share belongs in gno.land/p/moul/x/social/coin, the sibling that refuses to exist until its mint rule, sink and buyer are declared; this realm does not import it.
ONE realm address holds EVERY crew's treasury, with per-crew accounting inside it. The chain sees one balance; which crew owns which part of it is a number in this realm's state. So a bug in the accounting is a bug across crews, not inside one: an arithmetic error that over-credits one crew's ragequit pays it out of another crew's money, and nothing at the bank layer would refuse that transfer.
The fix is the instance-per-realm pattern (EFFECTIVE_GNO.md section 1.4): one realm per crew, each holding its own coins at its own address, with this package as the shared engine. That is v1. It is not v0 because deploying a realm per crew is a publish per crew, which is the opposite of "create it in one transaction", and the whole product claim here is the one transaction.
Nothing is ever pushed. A ragequit credits an internal ledger and the leaver calls Withdraw themselves, so one address that cannot be paid cannot wedge anybody else, and the credit is zeroed before the coins move.
IndexCrews is how many crews the index page lists.
Close records a proposal as passed or failed once its deadline has gone by, and returns which. Anyone may call it: closing is bookkeeping, not authority.
Count is how many crews exist.
Create opens a crew and makes the caller its first member.
Send ugnot with the call: the founder's shares are bought out of it at crew.InitialPricePerShare, and the whole amount, remainder included, becomes the treasury. Returns the new crew's id.
CreditOf is what an address can withdraw right now, in ugnot.
Fund adds what the caller sent to a crew's treasury and mints nothing, so every existing share is worth more afterwards.
It is how revenue, a grant or a member topping the pot up arrives. Anyone may fund any crew, member or not: there is no way to refuse a transfer on this chain anyway, and pretending otherwise would only move the donation to a bank send the realm cannot account for.
Get returns a crew's name, the height it was created at, how many members it has, its total shares and its treasury in ugnot.
Flat values rather than the crew itself: handing out a pointer to live state is a mutation handle nothing checks, and a struct is not something a wallet can decode anyway.
Join mints the caller shares at what a share is worth right now and adds what they sent to the treasury. Returns the shares minted.
The price is amount * totalShares / treasury, rounded down, so somebody joining a crew that has tripled its money pays three times what the founders did. Send at least ValuePerShare ugnot, or there is no whole share to mint.
MemberCount is how many people are in a crew.
Propose opens an advisory question on a crew. Any member may propose, and it stays open for crew.VoteBlocks blocks. Returns the proposal's id.
A proposal EXECUTES NOTHING. Passing one records that the crew agreed by share weight, and moves no coins, changes no membership and binds no code. Executing a payout is the next step and it is not in v0.
Ragequit burns every share the caller holds, credits them their pro-rata slice of the treasury and removes them from the crew. Returns the amount credited, which Withdraw collects.
Nobody has to agree. That is what makes a share mean something: the exit is priced by the same number that votes, and a member outvoted on everything can still leave with what they put in plus their part of what the crew built.
Render routes three views: the index, one crew, and one proposal.
SharesOf is how many shares who holds in a crew.
Tally is a proposal's share-weighted yes and no.
TotalOwed is every outstanding credit, which is the part of this realm's balance that belongs to people who have already left.
Treasury is what a crew holds, in ugnot. The coins themselves sit at this realm's single address: see the package doc on custody.
ValuePerShare is what one of a crew's shares is worth in ugnot, rounded down.
Vote records the caller's ballot, weighted by the shares they hold at this moment. One ballot per member, changeable while the proposal is open.
Withdraw sends the caller everything they have been credited, and returns it.
The credit is zeroed before the coins move, so a reentrant call finds nothing left to take. Nothing in this realm ever pushes value: one address that cannot be paid must not be able to wedge everybody else.