forked from regen-network/regen-ledger
/
helpers.go
147 lines (119 loc) · 3.66 KB
/
helpers.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package server
import (
"fmt"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/regen-network/regen-ledger/types/math"
"github.com/regen-network/regen-ledger/x/ecocredit"
)
func getDecimal(store sdk.KVStore, key []byte) (math.Dec, error) {
bz := store.Get(key)
if bz == nil {
return math.NewDecFromInt64(0), nil
}
value, err := math.NewDecFromString(string(bz))
if err != nil {
return math.Dec{}, sdkerrors.Wrap(err, fmt.Sprintf("can't unmarshal %s as decimal", bz))
}
return value, nil
}
func setDecimal(store sdk.KVStore, key []byte, value math.Dec) {
// always remove all trailing zeros for canonical representation
value, _ = value.Reduce()
if value.IsZero() {
store.Delete(key)
} else {
// use floating notation here always for canonical representation
store.Set(key, []byte(value.String()))
}
}
func addAndSetDecimal(store sdk.KVStore, key []byte, x math.Dec) error {
value, err := getDecimal(store, key)
if err != nil {
return err
}
value, err = value.Add(x)
if err != nil {
return err
}
setDecimal(store, key, value)
return nil
}
func subAndSetDecimal(store sdk.KVStore, key []byte, x math.Dec) error {
value, err := getDecimal(store, key)
if err != nil {
return err
}
if value.Cmp(x) == -1 {
return ecocredit.ErrInsufficientFunds
}
value, err = math.SafeSubBalance(value, x)
if err != nil {
return err
}
setDecimal(store, key, value)
return nil
}
func iterateSupplies(store sdk.KVStore, storeKey byte, cb func(denom, supply string) (bool, error)) error {
iter := sdk.KVStorePrefixIterator(store, []byte{storeKey})
defer iter.Close()
for ; iter.Valid(); iter.Next() {
stop, err := cb(string(ParseSupplyKey(iter.Key())), string(iter.Value()))
if err != nil {
return err
}
if stop {
break
}
}
return nil
}
func iterateBalances(store sdk.KVStore, storeKey byte, cb func(address, denom, balance string) bool) {
iter := sdk.KVStorePrefixIterator(store, []byte{storeKey})
defer iter.Close()
for ; iter.Valid(); iter.Next() {
addr, denom := ParseBalanceKey(iter.Key())
if cb(addr.String(), string(denom), string(iter.Value())) {
break
}
}
}
func verifyCreditBalance(store storetypes.KVStore, ownerAddr sdk.AccAddress, batchDenom string, quantity string) error {
bd := batchDenomT(batchDenom)
balance, err := getDecimal(store, TradableBalanceKey(ownerAddr, bd))
if err != nil {
return err
}
q, err := math.NewPositiveDecFromString(quantity)
if err != nil {
return err
}
if balance.Cmp(q) == -1 {
return ecocredit.ErrInsufficientFunds
}
return nil
}
// getCoinNeeded calculates the amount of coin needed for purchasing [quantity] ecocredits at [bidPrice] price
func getCoinNeeded(quantity math.Dec, bidPrice *sdk.Coin) (coinNeeded sdk.Coin, err error) {
// amount is the amount (in bid denom) of coins necessary for purchasing
// the exact quantity requested
amountDec, err := quantity.Mul(math.NewDecFromInt64(bidPrice.Amount.Int64()))
if err != nil {
return coinNeeded, err
}
// amountNeeded should always be rounded down to the nearest integer number
// as a buyer cannot spend fractional sdk.Coin (e.g. fractional uregen).
// The ecocredit quantity sent to the buyer should always be calculated by
// multiplying the integral amountNeeded by bidPrice as opposed to the quantity sent
// in the Buy() request.
amountNeeded, err := amountDec.QuoInteger(math.NewDecFromInt64(1))
if err != nil {
return coinNeeded, err
}
amountNeededInt64, err := amountNeeded.Int64()
if err != nil {
return coinNeeded, err
}
return sdk.NewCoin(bidPrice.Denom, sdk.NewInt(amountNeededInt64)), nil
}