Governor should handle duplicate coin gecko ids

This commit is contained in:
Bruce Riley 2022-08-10 15:53:48 +00:00 committed by Evan Gray
parent 09b33552df
commit 5ca2faa9a3
3 changed files with 61 additions and 40 deletions

View File

@ -105,7 +105,7 @@ type ChainGovernor struct {
logger *zap.Logger
mutex sync.Mutex
tokens map[tokenKey]*tokenEntry
tokensByCoinGeckoId map[string]*tokenEntry
tokensByCoinGeckoId map[string][]*tokenEntry
chains map[vaa.ChainID]*chainEntry
msgsToPublish []*common.MessagePublication
dayLengthInMinutes int
@ -122,7 +122,7 @@ func NewChainGovernor(
db: db,
logger: logger,
tokens: make(map[tokenKey]*tokenEntry),
tokensByCoinGeckoId: make(map[string]*tokenEntry),
tokensByCoinGeckoId: make(map[string][]*tokenEntry),
chains: make(map[vaa.ChainID]*chainEntry),
env: env,
}
@ -185,6 +185,17 @@ func (gov *ChainGovernor) initConfig() error {
te := &tokenEntry{cfgPrice: cfgPrice, price: initialPrice, decimals: decimals, symbol: ct.symbol, coinGeckoId: ct.coinGeckoId, token: key}
te.updatePrice()
gov.tokens[key] = te
// Multiple tokens can share a CoinGecko price, so we keep an array of tokens per CoinGecko ID.
cge, cgExists := gov.tokensByCoinGeckoId[te.coinGeckoId]
if !cgExists {
gov.tokensByCoinGeckoId[te.coinGeckoId] = []*tokenEntry{te}
} else {
cge = append(cge, te)
gov.tokensByCoinGeckoId[te.coinGeckoId] = cge
}
gov.logger.Info("cgov: will monitor token:", zap.Stringer("chain", key.chain),
zap.Stringer("addr", key.addr),
zap.String("symbol", te.symbol),
@ -193,9 +204,6 @@ func (gov *ChainGovernor) initConfig() error {
zap.Int64("decimals", dec),
zap.Int64("origDecimals", ct.decimals),
)
gov.tokens[key] = te
gov.tokensByCoinGeckoId[te.coinGeckoId] = te
}
if len(gov.tokens) == 0 {

View File

@ -109,13 +109,13 @@ func (gov *ChainGovernor) queryCoinGecko() {
gov.mutex.Lock()
defer gov.mutex.Unlock()
localTokenMap := make(map[string]*tokenEntry)
for coinGeckoId, te := range gov.tokensByCoinGeckoId {
localTokenMap[coinGeckoId] = te
localTokenMap := make(map[string][]*tokenEntry)
for coinGeckoId, cge := range gov.tokensByCoinGeckoId {
localTokenMap[coinGeckoId] = cge
}
for coinGeckoId, data := range result {
te, exists := gov.tokensByCoinGeckoId[coinGeckoId]
cge, exists := gov.tokensByCoinGeckoId[coinGeckoId]
if exists {
price, ok := data.(map[string]interface{})["usd"].(float64)
if !ok {
@ -123,6 +123,8 @@ func (gov *ChainGovernor) queryCoinGecko() {
// By continuing, we leave this one in the local map so the price will get reverted below.
continue
}
for _, te := range cge {
te.coinGeckoPrice = big.NewFloat(price)
te.updatePrice()
te.priceTime = now
@ -135,6 +137,7 @@ func (gov *ChainGovernor) queryCoinGecko() {
zap.Stringer("cfgPrice", te.cfgPrice),
zap.Stringer("coinGeckoPrice", te.coinGeckoPrice),
)
}
delete(localTokenMap, coinGeckoId)
} else {
@ -143,7 +146,8 @@ func (gov *ChainGovernor) queryCoinGecko() {
}
if len(localTokenMap) != 0 {
for _, te := range localTokenMap {
for _, lcge := range localTokenMap {
for _, te := range lcge {
gov.logger.Error("cgov: did not receive a CoinGecko response for symbol, reverting to configured price",
zap.String("symbol", te.symbol),
zap.String("coinGeckoId",
@ -155,13 +159,15 @@ func (gov *ChainGovernor) queryCoinGecko() {
// Don't update the timestamp so we'll know when we last received an update from CoinGecko.
}
}
}
}
func (gov *ChainGovernor) revertAllPrices() {
gov.mutex.Lock()
defer gov.mutex.Unlock()
for _, te := range gov.tokensByCoinGeckoId {
for _, cge := range gov.tokensByCoinGeckoId {
for _, te := range cge {
gov.logger.Error("cgov: reverting to configured price",
zap.String("symbol", te.symbol),
zap.String("coinGeckoId",
@ -172,6 +178,7 @@ func (gov *ChainGovernor) revertAllPrices() {
te.price = te.cfgPrice
// Don't update the timestamp so we'll know when we last received an update from CoinGecko.
}
}
}
// We should use the max(coinGeckoPrice, configuredPrice) as our price for computing notional value.

View File

@ -78,7 +78,13 @@ func (gov *ChainGovernor) setTokenForTesting(tokenChainID vaa.ChainID, tokenAddr
key := tokenKey{chain: vaa.ChainID(tokenChainID), addr: tokenAddr}
te := &tokenEntry{cfgPrice: bigPrice, price: bigPrice, decimals: decimals, symbol: symbol, coinGeckoId: symbol, token: key}
gov.tokens[key] = te
gov.tokensByCoinGeckoId[symbol] = te
cge, cgExists := gov.tokensByCoinGeckoId[te.coinGeckoId]
if !cgExists {
gov.tokensByCoinGeckoId[te.coinGeckoId] = []*tokenEntry{te}
} else {
cge = append(cge, te)
gov.tokensByCoinGeckoId[te.coinGeckoId] = cge
}
return nil
}