github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/gnovm/stdlibs/std/banker.go (about)

     1  package std
     2  
     3  import (
     4  	"fmt"
     5  	"regexp"
     6  
     7  	gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
     8  	"github.com/gnolang/gno/tm2/pkg/crypto"
     9  	"github.com/gnolang/gno/tm2/pkg/std"
    10  )
    11  
    12  // BankerInterface is the interface through which Gno is capable of accessing
    13  // the blockchain's banker.
    14  //
    15  // The name is what it is to avoid a collision with Gno's Banker, when
    16  // transpiling.
    17  type BankerInterface interface {
    18  	GetCoins(addr crypto.Bech32Address) (dst std.Coins)
    19  	SendCoins(from, to crypto.Bech32Address, amt std.Coins)
    20  	TotalCoin(denom string) int64
    21  	IssueCoin(addr crypto.Bech32Address, denom string, amount int64)
    22  	RemoveCoin(addr crypto.Bech32Address, denom string, amount int64)
    23  }
    24  
    25  const (
    26  	// Can only read state.
    27  	btReadonly uint8 = iota //nolint
    28  	// Can only send from tx send.
    29  	btOrigSend
    30  	// Can send from all realm coins.
    31  	btRealmSend
    32  	// Can issue and remove realm coins.
    33  	btRealmIssue
    34  )
    35  
    36  // regexp for denom format
    37  var reDenom = regexp.MustCompile("[a-z][a-z0-9]{2,15}")
    38  
    39  func X_bankerGetCoins(m *gno.Machine, bt uint8, addr string) (denoms []string, amounts []int64) {
    40  	coins := m.Context.(ExecContext).Banker.GetCoins(crypto.Bech32Address(addr))
    41  	return ExpandCoins(coins)
    42  }
    43  
    44  func X_bankerSendCoins(m *gno.Machine, bt uint8, fromS, toS string, denoms []string, amounts []int64) {
    45  	// bt != BankerTypeReadonly (checked in gno)
    46  
    47  	ctx := m.Context.(ExecContext)
    48  	amt := CompactCoins(denoms, amounts)
    49  	from, to := crypto.Bech32Address(fromS), crypto.Bech32Address(toS)
    50  
    51  	pkgAddr := ctx.OrigPkgAddr
    52  	if m.Realm != nil {
    53  		pkgPath := m.Realm.Path
    54  		pkgAddr = gno.DerivePkgAddr(pkgPath).Bech32()
    55  	}
    56  
    57  	if bt == btOrigSend || bt == btRealmSend {
    58  		if from != pkgAddr {
    59  			m.Panic(typedString(
    60  				fmt.Sprintf(
    61  					"can only send from the realm package address %q, but got %q",
    62  					pkgAddr, from),
    63  			))
    64  			return
    65  		}
    66  	}
    67  
    68  	switch bt {
    69  	case btOrigSend:
    70  		// indirection allows us to "commit" in a second phase
    71  		spent := (*ctx.OrigSendSpent).Add(amt)
    72  		if !ctx.OrigSend.IsAllGTE(spent) {
    73  			m.Panic(typedString(
    74  				fmt.Sprintf(
    75  					`cannot send "%v", limit "%v" exceeded with "%v" already spent`,
    76  					amt, ctx.OrigSend, *ctx.OrigSendSpent),
    77  			))
    78  			return
    79  		}
    80  		ctx.Banker.SendCoins(from, to, amt)
    81  		*ctx.OrigSendSpent = spent
    82  	case btRealmSend, btRealmIssue:
    83  		ctx.Banker.SendCoins(from, to, amt)
    84  	default:
    85  		panic(fmt.Sprintf("invalid banker type %d in bankerSendCoins", bt))
    86  	}
    87  }
    88  
    89  func X_bankerTotalCoin(m *gno.Machine, bt uint8, denom string) int64 {
    90  	return m.Context.(ExecContext).Banker.TotalCoin(denom)
    91  }
    92  
    93  func X_bankerIssueCoin(m *gno.Machine, bt uint8, addr string, denom string, amount int64) {
    94  	// gno checks for bt == RealmIssue
    95  
    96  	// check origin denom format
    97  	matched := reDenom.MatchString(denom)
    98  	if !matched {
    99  		m.Panic(typedString("invalid denom format to issue coin, must be " + reDenom.String()))
   100  		return
   101  	}
   102  
   103  	// Similar to ibc spec
   104  	// ibc_denom := 'ibc/' + hash('path' + 'base_denom')
   105  	// gno_realm_denom := '/' + 'pkg_path' + ':' + 'base_denom'
   106  	newDenom := "/" + m.Realm.Path + ":" + denom
   107  	m.Context.(ExecContext).Banker.IssueCoin(crypto.Bech32Address(addr), newDenom, amount)
   108  }
   109  
   110  func X_bankerRemoveCoin(m *gno.Machine, bt uint8, addr string, denom string, amount int64) {
   111  	// gno checks for bt == RealmIssue
   112  
   113  	matched := reDenom.MatchString(denom)
   114  	if !matched {
   115  		m.Panic(typedString("invalid denom format to remove coin, must be " + reDenom.String()))
   116  		return
   117  	}
   118  
   119  	newDenom := "/" + m.Realm.Path + ":" + denom
   120  	m.Context.(ExecContext).Banker.RemoveCoin(crypto.Bech32Address(addr), newDenom, amount)
   121  }