Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}