-
Notifications
You must be signed in to change notification settings - Fork 168
/
oracle.go
131 lines (106 loc) · 3.82 KB
/
oracle.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
package keeper
import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
oracletypes "github.com/umee-network/umee/v2/x/oracle/types"
"github.com/umee-network/umee/v2/x/leverage/types"
)
// TokenPrice returns the USD value of a base token. Note, the token's denomination
// must be the base denomination, e.g. uumee. The x/oracle module must know of
// the base and display/symbol denominations for each exchange pair. E.g. it must
// know about the UMEE/USD exchange rate along with the uumee base denomination
// and the exponent.
// This function will not return non-positive exchange rates, preferring
// to error instead.
func (k Keeper) TokenPrice(ctx sdk.Context, denom string) (sdk.Dec, error) {
if !k.IsAcceptedToken(ctx, denom) {
return sdk.ZeroDec(), sdkerrors.Wrap(types.ErrInvalidAsset, denom)
}
price, err := k.oracleKeeper.GetExchangeRateBase(ctx, denom)
if err != nil {
return sdk.ZeroDec(), sdkerrors.Wrap(err, "oracle")
}
if price.IsNil() || !price.IsPositive() {
return sdk.ZeroDec(), sdkerrors.Wrap(types.ErrInvalidOraclePrice, denom)
}
return price, nil
}
// TokenValue returns the total token value given a Coin. An error is
// returned if we cannot get the token's price or if it's not an accepted token.
func (k Keeper) TokenValue(ctx sdk.Context, coin sdk.Coin) (sdk.Dec, error) {
p, err := k.TokenPrice(ctx, coin.Denom)
if err != nil {
return sdk.ZeroDec(), err
}
return p.Mul(coin.Amount.ToDec()), nil
}
// TotalTokenValue returns the total value of all supplied tokens. It is
// equivalent to calling GetTokenValue on each coin individually.
func (k Keeper) TotalTokenValue(ctx sdk.Context, coins sdk.Coins) (sdk.Dec, error) {
total := sdk.ZeroDec()
for _, c := range coins {
v, err := k.TokenValue(ctx, c)
if err != nil {
return sdk.ZeroDec(), err
}
total = total.Add(v)
}
return total, nil
}
// EquivalentValue returns the amount of a selected denom which would have equal
// USD value to a provided sdk.Coin
func (k Keeper) EquivalentTokenValue(ctx sdk.Context, fromCoin sdk.Coin, toDenom string) (sdk.Coin, error) {
// get USD price of input (fromCoin) denomination
p1, err := k.TokenPrice(ctx, fromCoin.Denom)
if err != nil {
return sdk.Coin{}, err
}
// return immediately on zero input value
if p1.IsZero() {
return sdk.NewCoin(toDenom, sdk.ZeroInt()), nil
}
// get USD price of output denomination
p2, err := k.TokenPrice(ctx, toDenom)
if err != nil {
return sdk.Coin{}, err
}
if !p2.IsPositive() {
return sdk.Coin{}, sdkerrors.Wrap(types.ErrBadValue, p2.String())
}
// then return the amount corrected by the price ratio
return sdk.NewCoin(
toDenom,
fromCoin.Amount.ToDec().Mul(p1).Quo(p2).TruncateInt(),
), nil
}
// FundOracle transfers requested coins to the oracle module account, as
// long as the leverage module account has sufficient unreserved assets.
func (k Keeper) FundOracle(ctx sdk.Context, requested sdk.Coins) error {
rewards := sdk.Coins{}
// reduce rewards if they exceed unreserved module balance
for _, coin := range requested {
reserved := k.GetReserveAmount(ctx, coin.Denom)
balance := k.ModuleBalance(ctx, coin.Denom)
amountToTransfer := sdk.MinInt(coin.Amount, balance.Sub(reserved))
if amountToTransfer.IsPositive() {
rewards = rewards.Add(sdk.NewCoin(coin.Denom, amountToTransfer))
}
}
// Because this action is not caused by a message, logging and
// events are here instead of msg_server.go
k.Logger(ctx).Debug(
"funded oracle",
"amount", rewards.String(),
)
ctx.EventManager().EmitEvents(sdk.Events{
sdk.NewEvent(
types.EventTypeFundOracle,
sdk.NewAttribute(sdk.AttributeKeyAmount, rewards.String()),
),
})
// Send rewards
if !rewards.IsZero() {
return k.bankKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, oracletypes.ModuleName, rewards)
}
return nil
}