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 }