Search Apps Documentation Source Content File Folder Download Copy Actions Download 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}