/
routine.go
178 lines (143 loc) · 5.06 KB
/
routine.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
176
177
178
package oracle
import (
"errors"
"fmt"
"time"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/client/utils"
sdk "github.com/cosmos/cosmos-sdk/types"
authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
"github.com/tendermint/tendermint/rpc/client"
"github.com/spf13/viper"
"github.com/everett-protocol/terra-oracle/types"
)
const (
FlagValidator = "validator"
FlagPassword = "password"
)
func (os *OracleService) init() error {
os.txBldr = authtxb.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(os.cdc))
os.cliCtx = context.NewCLIContext().
WithCodec(os.cdc).
WithAccountDecoder(os.cdc)
if os.cliCtx.BroadcastMode != "block" {
return errors.New("I recommend to use block broadcast mode")
}
fromName := os.cliCtx.GetFromName()
passphrase := viper.GetString(FlagPassword)
if passphrase == "" {
var err error
passphrase, err = keys.GetPassphrase(fromName)
if err != nil {
return err
}
}
os.passphrase = passphrase
return nil
}
func (os *OracleService) startRoutine() {
httpClient := client.NewHTTP(os.cliCtx.NodeURI, "/websocket")
var latestVoteHeight int64 = 0
for {
func() {
defer func() {
if r := recover(); r != nil {
os.Logger.Error("Unknown error", r)
}
time.Sleep(5 * time.Second)
}()
status, err := httpClient.Status()
if err != nil {
os.Logger.Error("Fail to fetch status", err.Error())
return
}
latestHeignt := status.SyncInfo.LatestBlockHeight
var tick int64 = latestHeignt / VotePeriod
if tick <= latestVoteHeight/VotePeriod {
return
}
latestVoteHeight = latestHeignt
os.Logger.Info(fmt.Sprintf("Tick: %d", tick))
err = os.calculatePrice()
if err != nil {
os.Logger.Error(fmt.Sprintf("Error when calculate price: %s", err.Error()))
}
denoms := []string{types.KRW, types.USD, types.SDR}
if os.prevoteInited {
os.Logger.Info(fmt.Sprintf("Try to send vote msg (including prevote for next vote msg)"))
voteMsgs, err := os.makeVoteMsgs(denoms)
if err != nil {
os.Logger.Error("Fail to make vote msgs", err.Error())
}
// Because vote tx includes prevote for next price,
// use twice as much gas.
res, err := os.broadcast(voteMsgs)
if err != nil {
os.Logger.Error("Fail to send vote msgs", err.Error())
os.prevoteInited = false // Retry initialization (prevote msg).
return
}
if tick > res.Height/VotePeriod {
os.Logger.Error("Tx couldn't be sent within vote period")
os.prevoteInited = false // Retry initialization (prevote msg).
}
} else {
os.Logger.Info(fmt.Sprintf("Try to send prevote msg"))
prevoteMsgs, err := os.makePrevoteMsgs(denoms)
if err != nil {
os.Logger.Error("Fail to make prevote msgs", err.Error())
}
_, err = os.broadcast(prevoteMsgs)
if err != nil {
os.Logger.Error("Fail to send prevote msgs", err.Error())
os.prevoteInited = false // Retry initialization (prevote msg).
return
}
os.prevoteInited = true
}
}()
}
}
func (os *OracleService) calculatePriceSynthetic(base string, quote string, intermediation string) (sdk.Dec, error) {
baseToInter, baseToInterWeight, baseToInterErr := os.ps.GetPrice(base, intermediation)
interToQuote, _, interToQuoteErr := os.ps.GetPrice(intermediation, quote)
sumBaseToQuote := sdk.ZeroDec()
sumBaseToQuoteWeight := uint64(0)
baseToQuote, baseToQuoteWeight, baseToQuoteErr := os.ps.GetPrice(base, quote)
if baseToQuoteErr == nil {
sumBaseToQuote = sumBaseToQuote.Add(baseToQuote.MulInt64(int64(baseToQuoteWeight)))
sumBaseToQuoteWeight += baseToQuoteWeight
}
if baseToQuoteErr != nil && (baseToInterErr != nil || interToQuoteErr != nil) {
return sdk.Dec{}, fmt.Errorf("can't calculate pair %s", types.PairStr(base, quote))
}
sumBaseToQuote = sumBaseToQuote.Add(baseToInter.Mul(interToQuote).MulInt64(int64(baseToInterWeight)))
sumBaseToQuoteWeight += baseToInterWeight
if sumBaseToQuoteWeight == 0 {
return sdk.Dec{}, fmt.Errorf("can't calculate weighted mean pair %s", types.PairStr(base, quote))
}
return sumBaseToQuote.QuoInt64(int64(sumBaseToQuoteWeight)), nil
}
func (os *OracleService) calculatePrice() error {
lunaToKrw, err := os.calculatePriceSynthetic(types.LUNA, types.KRW, types.BTC)
if err != nil {
return err
}
os.lunaPrices[types.PairStr(types.LUNA, types.KRW)] = lunaToKrw
os.Logger.Info(fmt.Sprintf("Calculated %s: %s", types.PairStr(types.LUNA, types.KRW), lunaToKrw))
lunaToUsd, err := os.calculatePriceSynthetic(types.LUNA, types.USD, types.BTC)
if err != nil {
return err
}
os.lunaPrices[types.PairStr(types.LUNA, types.USD)] = lunaToUsd
os.Logger.Info(fmt.Sprintf("Calculated %s: %s", types.PairStr(types.LUNA, types.USD), lunaToUsd))
// TODO: Use usd as intermediation instead of krw
lunaToSdr, err := os.calculatePriceSynthetic(types.LUNA, types.SDR, types.KRW)
if err != nil {
return err
}
os.lunaPrices[types.PairStr(types.LUNA, types.SDR)] = lunaToSdr
os.Logger.Info(fmt.Sprintf("Calculated %s: %s", types.PairStr(types.LUNA, types.SDR), lunaToSdr))
return nil
}