coindemo.gno
3.54 Kb · 118 lines
1// Realm coindemo is a deliberately minimal third consumer proving the
2// extracted primitives compose: coinio handles every coin movement,
3// feeledger handles every balance. It is an echo-vault: deposit GNOT,
4// claim it back in full (no fees), owner sweeps out-of-band surplus.
5//
6// Conservation: H == U + S (no fee pot, no app escrow), where U is the
7// ledger's users total and S >= 0 is out-of-band surplus, recoverable
8// only via the owner's Sweep which reserves U.
9package coindemo
10
11import (
12 "chain"
13 "chain/runtime/unsafe"
14 "strconv"
15
16 "gno.land/p/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/coinio"
17 "gno.land/p/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/feeledger"
18)
19
20// Denom is the only asset this realm accepts.
21const Denom = "ugnot"
22
23var (
24 owner address // may sweep surplus
25 self address // this realm's address, captured at deploy
26 ledger = feeledger.MustNew(0) // zero fee cap: pure echo accounting
27)
28
29func init() {
30 owner = unsafe.OriginCaller()
31 self = unsafe.CurrentRealm().Address()
32}
33
34// Deposit credits the caller with the attached GNOT, in full.
35func Deposit(cur realm) {
36 from, amount := coinio.Receive(0, cur, Denom)
37 if _, _, err := ledger.Deposit(from.String(), amount, 0); err != nil {
38 panic(err)
39 }
40 chain.Emit("Deposit", "from", from.String(), "amount", fmtInt(amount))
41}
42
43// ClaimAll sends the caller's entire balance back. Fails if empty.
44func ClaimAll(cur realm) {
45 caller := cur.Previous().Address()
46 amount, err := ledger.WithdrawAll(caller.String())
47 if err != nil {
48 panic(err)
49 }
50 if amount == 0 {
51 panic("nothing to claim")
52 }
53 coinio.Payout(0, cur, caller, Denom, amount)
54 chain.Emit("Claim", "to", caller.String(), "amount", fmtInt(amount))
55}
56
57// Sweep sends ugnot held above user liabilities to the owner. Owner
58// only; user balances are reserved by construction.
59func Sweep(cur realm) {
60 if cur.Previous().Address() != owner {
61 panic("owner only")
62 }
63 swept := coinio.Sweep(0, cur, owner, Denom, ledger.Liabilities())
64 chain.Emit("SurplusSwept", "to", owner.String(), "amount", fmtInt(swept))
65}
66
67// TransferOwner hands the sweep role to next. Owner only; zero
68// address rejected (audit Y3: without rotation, key loss permanently
69// orphans surplus sweeping).
70func TransferOwner(cur realm, next address) {
71 if cur.Previous().Address() != owner {
72 panic("owner only")
73 }
74 var zero address
75 if next == zero {
76 panic("empty owner address")
77 }
78 owner = next
79 chain.Emit("OwnerTransferred", "newOwner", next.String())
80}
81
82// --- read-only views ---
83
84// Owner returns who may sweep surplus.
85func Owner() address { return owner }
86
87// BalanceOf returns addr's claimable balance.
88func BalanceOf(addr address) int64 { return ledger.BalanceOf(addr.String()) }
89
90// UsersTotal returns the sum of all claimable balances.
91func UsersTotal() int64 { return ledger.UsersTotal() }
92
93// Held returns the ugnot actually held at the realm address.
94func Held() int64 { return coinio.HeldAt(self, Denom) }
95
96// Surplus returns Held() - UsersTotal().
97func Surplus() int64 { return Held() - ledger.Liabilities() }
98
99// Address returns this realm's address (the deposit target).
100func Address() address { return self }
101
102// Render shows totals and the conservation check.
103func Render(_ string) string {
104 held := Held()
105 status := "OK"
106 if held < ledger.Liabilities() {
107 status = "VIOLATED"
108 }
109 return "# Coindemo\n\n" +
110 "Echo-vault composing coinio + feeledger.\n\n" +
111 "- claimable (U): " + fmtInt(ledger.UsersTotal()) + Denom + "\n" +
112 "- held (H): " + fmtInt(held) + Denom + "\n" +
113 "- conservation (H >= U): " + status + "\n"
114}
115
116func fmtInt(n int64) string {
117 return strconv.FormatInt(n, 10)
118}