-
Notifications
You must be signed in to change notification settings - Fork 558
/
pool_asset.go
145 lines (127 loc) · 4.63 KB
/
pool_asset.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
package types
import (
"fmt"
"sort"
"strings"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)
func ValidateUserSpecifiedWeight(weight sdk.Int) error {
if !weight.IsPositive() {
return sdkerrors.Wrap(ErrNotPositiveWeight, weight.String())
}
if weight.GTE(MaxUserSpecifiedWeight) {
return sdkerrors.Wrap(ErrWeightTooLarge, weight.String())
}
return nil
}
func ValidateUserSpecifiedPoolAssets(assets []PoolAsset) error {
// The pool must be swapping between at least two assets
if len(assets) < 2 {
return ErrTooFewPoolAssets
}
// TODO: Add the limit of binding token to the pool params?
if len(assets) > 8 {
return sdkerrors.Wrapf(ErrTooManyPoolAssets, "%d", len(assets))
}
for _, asset := range assets {
err := ValidateUserSpecifiedWeight(asset.Weight)
if err != nil {
return err
}
if !asset.Token.IsValid() || !asset.Token.IsPositive() {
return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, asset.Token.String())
}
}
return nil
}
// SortPoolAssetsOutOfPlaceByDenom sorts pool assets in place, by weight
// Doesn't deep copy the underlying weights, but it does place the assets
// into a new slice.
func SortPoolAssetsOutOfPlaceByDenom(assets []PoolAsset) []PoolAsset {
assets_copy := make([]PoolAsset, len(assets))
copy(assets_copy, assets)
SortPoolAssetsByDenom(assets_copy)
return assets_copy
}
// SortPoolAssetsByDenom sorts pool assets in place, by weight
func SortPoolAssetsByDenom(assets []PoolAsset) {
sort.Slice(assets, func(i, j int) bool {
PoolAssetA := assets[i]
PoolAssetB := assets[j]
return strings.Compare(PoolAssetA.Token.Denom, PoolAssetB.Token.Denom) == -1
})
}
// Validates a pool asset, to check if it has a valid weight.
func (asset PoolAsset) ValidateWeight() error {
if asset.Weight.LTE(sdk.ZeroInt()) {
return fmt.Errorf("a token's weight in the pool must be greater than 0")
}
// TODO: Choose a value that is too large for weights
// if asset.Weight >= (1 << 32) {
// return fmt.Errorf("a token's weight in the pool must be less than 2^32")
// }
return nil
}
// subPoolAssetWeights subtracts the weights of two different pool asset slices.
// It assumes that both pool assets have the same token denominations,
// with the denominations in the same order.
// Returned weights can (and probably will have some) be negative.
func subPoolAssetWeights(base []PoolAsset, other []PoolAsset) []PoolAsset {
weightDifference := make([]PoolAsset, len(base))
// TODO: Consider deleting these panics for performance
if len(base) != len(other) {
panic("subPoolAssetWeights called with invalid input, len(base) != len(other)")
}
for i, asset := range base {
if asset.Token.Denom != other[i].Token.Denom {
panic(fmt.Sprintf("subPoolAssetWeights called with invalid input, "+
"expected other's %vth asset to be %v, got %v",
i, asset.Token.Denom, other[i].Token.Denom))
}
curWeightDiff := asset.Weight.Sub(other[i].Weight)
weightDifference[i] = PoolAsset{Token: asset.Token, Weight: curWeightDiff}
}
return weightDifference
}
// addPoolAssetWeights adds the weights of two different pool asset slices.
// It assumes that both pool assets have the same token denominations,
// with the denominations in the same order.
// Returned weights can be negative.
func addPoolAssetWeights(base []PoolAsset, other []PoolAsset) []PoolAsset {
weightSum := make([]PoolAsset, len(base))
// TODO: Consider deleting these panics for performance
if len(base) != len(other) {
panic("addPoolAssetWeights called with invalid input, len(base) != len(other)")
}
for i, asset := range base {
if asset.Token.Denom != other[i].Token.Denom {
panic(fmt.Sprintf("addPoolAssetWeights called with invalid input, "+
"expected other's %vth asset to be %v, got %v",
i, asset.Token.Denom, other[i].Token.Denom))
}
curWeightSum := asset.Weight.Add(other[i].Weight)
weightSum[i] = PoolAsset{Token: asset.Token, Weight: curWeightSum}
}
return weightSum
}
// assumes 0 < d < 1
func poolAssetsMulDec(base []PoolAsset, d sdk.Dec) []PoolAsset {
newWeights := make([]PoolAsset, len(base))
for i, asset := range base {
// TODO: This can adversarially panic at the moment! (as can Pool.TotalWeight)
// Ensure this won't be able to panic in the future PR where we bound
// each assets weight, and add precision
newWeight := d.MulInt(asset.Weight).RoundInt()
newWeights[i] = PoolAsset{Token: asset.Token, Weight: newWeight}
}
return newWeights
}
// PoolAssetsCoins returns all the coins corresponding to a slice of pool assets
func PoolAssetsCoins(assets []PoolAsset) sdk.Coins {
coins := sdk.Coins{}
for _, asset := range assets {
coins = coins.Add(asset.Token)
}
return coins
}