Package coin is a GRC20 that cannot exist until its author has answered the three questions a social app's token usually dodges: what mints it, what burns it, and who has a reason to buy it.
Why a package and not a convention
Every app in the x/social family is asked the same question, "and can it have a token", and the honest answer is only yes when all three of those have an answer. A token with a mint rule and no sink is a scoreboard with a price: supply grows, nothing consumes it, and the number on the leaderboard is the whole product. That is the shape a chain-wide measurement keeps finding, and it is cheap to avoid, so New refuses a Policy with a blank field rather than letting the omission ship.
The three strings are not validated beyond being non-empty, because no package can check that a sink is real. They are a declaration, rendered on the realm's own page by Coin.Render, where being wrong is visible.
The model
Example
1earned minted by the app, for the behaviour the app wants more of
2spent burned by the app, for the thing holders actually want
3traded a plain GRC20 transfer, which is what makes the sink a market
Earning and spending are the app's business and go through Coin.Earn and Coin.Spend, which keep running totals so a reader can see the two halves against each other. Transfers, approvals and balances are the embedded grc20.Token's, unchanged, so wallets and indexers see an ordinary GRC20.
Usage
A realm holds one Coin and never exports the ledger:
Example
1var points = coin.New("Thread Points", "THREAD", 0, 0, coin.Policy{
2 Mint: "1 per distinct address that replies to your thread",
3 Sink: "burned to pin a thread to the top of its page",
4 Buyer: "anyone who wants placement and has not earned it",
5}, 0, cur)
The trailing `_ int, rlm realm` is the shape a pure package has to use to reach the caller's frame: a p/ package may not declare a crossing function, so the realm token is threaded as a later parameter, exactly as grc20's own tellers do.