/
price.go
175 lines (148 loc) · 5.26 KB
/
price.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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
package keeper
import (
"fmt"
sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/umee-network/umee/v6/util/coin"
"github.com/umee-network/umee/v6/x/metoken"
otypes "github.com/umee-network/umee/v6/x/oracle/types"
)
var usdExponent = uint32(0)
// Prices calculates meToken price as an avg of median prices of all index accepted assets.
func (k Keeper) Prices(index metoken.Index) (metoken.IndexPrices, error) {
indexPrices := metoken.EmptyIndexPrices(index)
supply, err := k.IndexBalances(index.Denom)
if err != nil {
return indexPrices, err
}
allPrices := k.oracleKeeper.AllMedianPrices(*k.ctx)
// calculate the total assets value in the index balances
totalAssetsUSDValue := sdk.ZeroDec()
for _, aa := range index.AcceptedAssets {
// get token settings from leverageKeeper to use the symbol_denom
tokenSettings, err := k.leverageKeeper.GetTokenSettings(*k.ctx, aa.Denom)
if err != nil {
return indexPrices, err
}
assetPrice, err := latestPrice(allPrices, tokenSettings.SymbolDenom)
if err != nil {
return indexPrices, err
}
indexPrices.SetPrice(
metoken.AssetPrice{
BaseDenom: aa.Denom,
SymbolDenom: tokenSettings.SymbolDenom,
Price: assetPrice,
Exponent: tokenSettings.Exponent,
},
)
// if no meTokens were minted, the totalAssetValue is the sum of all the assets prices.
// otherwise is the sum of the value of all the assets in the index.
if supply.MetokenSupply.IsZero() {
totalAssetsUSDValue = totalAssetsUSDValue.Add(assetPrice)
} else {
balance, i := supply.AssetBalance(aa.Denom)
if i < 0 {
return indexPrices, sdkerrors.ErrNotFound.Wrapf("balance for denom %s not found", aa.Denom)
}
assetUSDValue, err := valueInUSD(balance.AvailableSupply(), assetPrice, tokenSettings.Exponent)
if err != nil {
return indexPrices, err
}
totalAssetsUSDValue = totalAssetsUSDValue.Add(assetUSDValue)
}
}
if supply.MetokenSupply.IsZero() {
// if no meTokens were minted, the meTokenPrice is totalAssetsUSDValue divided by accepted assets quantity
indexPrices.Price = totalAssetsUSDValue.QuoInt(sdkmath.NewInt(int64(len(index.AcceptedAssets))))
} else {
// otherwise, the meTokenPrice is totalAssetsUSDValue divided by meTokens minted quantity
meTokenPrice, err := priceInUSD(supply.MetokenSupply.Amount, totalAssetsUSDValue, index.Exponent)
if err != nil {
return indexPrices, err
}
indexPrices.Price = meTokenPrice
}
for i := 0; i < len(indexPrices.Assets); i++ {
asset := indexPrices.Assets[i]
swapRate, err := metoken.Rate(asset.Price, indexPrices.Price, asset.Exponent, indexPrices.Exponent)
if err != nil {
return indexPrices, err
}
swapFee, _, err := k.swapFee(index, indexPrices, coin.One(asset.BaseDenom))
if err != nil {
return indexPrices, err
}
redeemRate, err := metoken.Rate(indexPrices.Price, asset.Price, indexPrices.Exponent, asset.Exponent)
if err != nil {
return indexPrices, err
}
redeemFee, _, err := k.redeemFee(index, indexPrices, coin.One(asset.BaseDenom))
if err != nil {
return indexPrices, err
}
indexPrices.Assets[i].SwapRate = swapRate
indexPrices.Assets[i].SwapFee = swapFee
indexPrices.Assets[i].RedeemRate = redeemRate
indexPrices.Assets[i].RedeemFee = redeemFee
}
return indexPrices, nil
}
// SetPricesToOracle of every registered index.
func (k Keeper) SetPricesToOracle() error {
indexes := k.GetAllRegisteredIndexes()
for _, index := range indexes {
iPrice, err := k.Prices(index)
if err != nil {
k.Logger().Error(
"setting price to oracle: couldn't calculate the price",
"denom", index.Denom,
"error", err.Error(),
"block_time", k.ctx.BlockTime(),
)
continue
}
indexToken, err := k.leverageKeeper.GetTokenSettings(*k.ctx, index.Denom)
if err != nil {
k.Logger().Error(
"setting price to oracle: couldn't get token settings",
"denom", index.Denom,
"error", err.Error(),
"block_time", k.ctx.BlockTime(),
)
continue
}
k.oracleKeeper.SetExchangeRate(*k.ctx, indexToken.SymbolDenom, iPrice.Price)
}
return nil
}
// latestPrice from the list of medians, based on the block number.
func latestPrice(prices otypes.Prices, symbolDenom string) (sdk.Dec, error) {
latestPrice := otypes.Price{}
for _, price := range prices {
if price.ExchangeRateTuple.Denom == symbolDenom && price.BlockNum > latestPrice.BlockNum {
latestPrice = price
}
}
if latestPrice.BlockNum == 0 {
return sdk.Dec{}, fmt.Errorf("price not found in oracle for denom %s", symbolDenom)
}
return latestPrice.ExchangeRateTuple.ExchangeRate, nil
}
// valueInUSD given a specific amount, price and exponent
func valueInUSD(amount sdkmath.Int, assetPrice sdk.Dec, assetExponent uint32) (sdk.Dec, error) {
exponentFactor, err := metoken.ExponentFactor(assetExponent, usdExponent)
if err != nil {
return sdk.Dec{}, err
}
return exponentFactor.MulInt(amount).Mul(assetPrice), nil
}
// priceInUSD given a specific amount, totalValue and exponent
func priceInUSD(amount sdkmath.Int, totalValue sdk.Dec, assetExponent uint32) (sdk.Dec, error) {
exponentFactor, err := metoken.ExponentFactor(assetExponent, usdExponent)
if err != nil {
return sdk.Dec{}, err
}
return totalValue.Quo(exponentFactor.MulInt(amount)), nil
}