Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

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}