banker_coins.gno
3.25 Kb · 123 lines
1package treasury
2
3import (
4 "chain"
5 "chain/banker"
6 "errors"
7
8 "gno.land/p/aeddi/panictoerr"
9)
10
11var ErrNonCanonicalBanker = errors.New("inner banker is not the canonical chain/banker.Banker")
12
13// CoinsBanker is a Banker that sends banker.Coins.
14type CoinsBanker struct {
15 owner address // The address of this coins banker owner.
16 banker banker.Banker // The underlying std banker, must be a BankerTypeRealmSend.
17}
18
19var _ Banker = (*CoinsBanker)(nil)
20
21// ID implements Banker.
22func (CoinsBanker) ID() string {
23 return "Coins"
24}
25
26// Send implements Banker.
27//
28// rlm must be the caller's own captured cur (i.e. the cur of the
29// immediate crossing-function caller). Sending with rlm = cur.Previous()
30// or any other realm value is rejected: rlm.IsCurrent() asserts pointer
31// identity against the topmost crossing frame. Combined with the
32// rlm.Address() == cb.owner check, this restricts Send to the owning
33// realm acting in its own frame.
34func (cb *CoinsBanker) Send(_ int, rlm realm, p Payment) error {
35 if !rlm.IsCurrent() {
36 return ErrSpoofedRealm
37 }
38 if rlm.Address() != cb.owner {
39 return ErrCurrentRealmIsNotOwner
40 }
41 // Check if payment is of type coinsPayment.
42 payment, ok := p.(coinsPayment)
43 if !ok {
44 return ErrInvalidPaymentType
45 }
46
47 // Send the coins.
48 return panictoerr.PanicToError(func() {
49 cb.banker.SendCoins(cb.owner, payment.toAddress, payment.coins)
50 })
51}
52
53// Balances implements Banker.
54func (cb *CoinsBanker) Balances() []Balance {
55 // Get the coins from the banker.
56 coins := cb.banker.GetCoins(cb.owner)
57
58 // Convert banker.Coins to []Balance.
59 balances := make([]Balance, len(coins))
60 for i := range coins {
61 balances[i] = Balance{
62 Denom: coins[i].Denom,
63 Amount: coins[i].Amount,
64 }
65 }
66
67 return balances
68}
69
70// Address implements Banker.
71func (cb *CoinsBanker) Address() string {
72 return cb.owner.String()
73}
74
75// NewCoinsBankerWithOwner creates a new CoinsBanker with the given address.
76//
77// banker_ must be the canonical Banker produced by banker.NewBanker;
78// hand-rolled Banker implementations (no-op fakes, decorators) are
79// rejected via banker.IsCanonical. Without this check, a callee
80// receiving a *CoinsBanker constructed from a fake banker would not
81// be able to tell that Send is a no-op (no real coins move). The
82// pkgAddr-vs-owner mismatch is still surfaced lazily by the inner
83// banker's own SendCoins check.
84func NewCoinsBankerWithOwner(owner address, banker_ banker.Banker) (*CoinsBanker, error) {
85 if owner == "" {
86 return nil, ErrNoOwnerProvided
87 }
88
89 if !banker.IsCanonical(banker_) {
90 return nil, ErrNonCanonicalBanker
91 }
92
93 return &CoinsBanker{
94 owner: owner,
95 banker: banker_,
96 }, nil
97}
98
99// coinsPayment represents a payment that is issued by a CoinsBanker.
100type coinsPayment struct {
101 coins chain.Coins // The coins being sent.
102 toAddress address // The recipient of the payment.
103}
104
105var _ Payment = (*coinsPayment)(nil)
106
107// BankerID implements Payment.
108func (coinsPayment) BankerID() string {
109 return CoinsBanker{}.ID()
110}
111
112// String implements Payment.
113func (cp coinsPayment) String() string {
114 return cp.coins.String() + " to " + cp.toAddress.String()
115}
116
117// NewCoinsPayment creates a new coinsPayment.
118func NewCoinsPayment(coins chain.Coins, toAddress address) Payment {
119 return coinsPayment{
120 coins: coins,
121 toAddress: toAddress,
122 }
123}