pilotdemo.gno
3.43 Kb · 102 lines
1// Package pilotdemo is a power for a realm-driven account: a payout module
2// installed into gno.land/p/moul/pilot/v0 after the account was deployed.
3//
4// It shows both delegation modes side by side:
5//
6// - Pay goes through the account's revocable [pilot.Purse]. Revoking the
7// module stops it on the next call, even though this realm still holds
8// the purse object.
9// - Act is only reachable under an identity grant: the account lends its
10// sub-identity, this realm acts as "<account>#<subpath>" toward another
11// realm, and spends the sub-treasury through a banker it mints. That
12// banker is KEPT on purpose, which is what makes an identity grant
13// permanent: revoking stops Exec, not this.
14//
15// Demo of gno.land/p/moul/pilot/v0. Account instance: r/moul/pilot.
16package pilotdemo
17
18import (
19 "chain"
20 "chain/banker"
21
22 "gno.land/p/moul/pilot/v0"
23 "gno.land/p/nt/ufmt/v0"
24
25 account "gno.land/r/moul/pilot/v0"
26)
27
28type payout struct {
29 purse *pilot.Purse
30 kept banker.Banker // minted from a lent identity, and retained
31 sub address
32 dest address
33 paid int64
34}
35
36func (m *payout) Name() string { return "payout" }
37
38// Run is what the account calls. rlm is the account's sub-identity token
39// under an identity grant, and its plain cur otherwise.
40func (m *payout) Run(_ int, rlm realm, args string) string {
41 if !rlm.IsCurrent() {
42 panic("pilotdemo: stale realm value")
43 }
44 if rlm.Subpath() == "" {
45 // purse-only grant: no identity to act under
46 m.purse.Pay(m.dest, 100)
47 m.paid += 100
48 return ufmt.Sprintf("paid 100ugnot from the main treasury, %d left", m.purse.Left())
49 }
50
51 // identity grant: act as the account toward another realm...
52 seen := Echo(cross(rlm))
53
54 // ...and spend the sub-treasury it owns.
55 m.sub = rlm.Address()
56 m.kept = banker.NewBanker(banker.BankerTypeRealmSend, rlm)
57 m.kept.SendCoins(m.sub, m.dest, chain.NewCoins(chain.NewCoin("ugnot", 100)))
58 m.paid += 100
59 return "acted as " + seen
60}
61
62var self = &payout{}
63
64// Install wires this module into moul's account.
65func Install(cur realm, dest address) {
66 InstallInto(cur, account.Handle(), dest)
67}
68
69// InstallInto wires it into ANY account: a module is not bound to one
70// instance. gno has no dynamic call, so the handle has to be passed as a
71// value, which a `gnokey maketx run` script can do and a MsgCall cannot.
72func InstallInto(cur realm, acct *pilot.Account, dest address) {
73 self.dest = dest
74 acct.Register(0, cur, self)
75 self.purse = acct.PurseFor(0, cur)
76}
77
78// Echo reports who called it. A crossing call shifts the previous-realm
79// stack even into the same realm, so this is what the module's borrowed
80// identity looks like from the outside.
81func Echo(cur realm) string { return cur.Previous().PkgPath() }
82
83// Payout spends the sub-treasury with the retained banker, reaching the bank
84// without re-entering the account. This is the demonstration that an
85// identity grant cannot be revoked: it keeps working after Revoke.
86func Payout(cur realm, to address, amount int64) {
87 if self.kept == nil {
88 panic("pilotdemo: no identity was ever granted")
89 }
90 self.kept.SendCoins(self.sub, to, chain.NewCoins(chain.NewCoin("ugnot", amount)))
91}
92
93// Paid is what this module has moved in total.
94func Paid() int64 { return self.paid }
95
96func Render(path string) string {
97 if self.purse == nil {
98 return "# pilotdemo\n\nNot installed.\n"
99 }
100 return ufmt.Sprintf("# pilotdemo\n\n| | |\n|---|---|\n| destination | %s |\n| purse left | %d ugnot |\n",
101 self.dest.String(), self.purse.Left())
102}