github.com/diadata-org/diadata@v1.4.593/pkg/dia/scraper/liquidity-scrapers/BalancerV2Scraper.go (about) 1 package liquidityscrapers 2 3 import ( 4 "context" 5 "math" 6 "math/big" 7 "strings" 8 "time" 9 10 "github.com/ethereum/go-ethereum/accounts/abi/bind" 11 "github.com/ethereum/go-ethereum/common" 12 "github.com/ethereum/go-ethereum/ethclient" 13 14 "github.com/diadata-org/diadata/pkg/dia/helpers/ethhelper" 15 balancervault "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/balancerv2/vault" 16 models "github.com/diadata-org/diadata/pkg/model" 17 "github.com/diadata-org/diadata/pkg/utils" 18 19 "github.com/diadata-org/diadata/pkg/dia" 20 ) 21 22 const ( 23 balancerV2FilterPageSize = 5000 24 balancerV2RestDial = "" 25 ) 26 27 type BalancerV2Scraper struct { 28 RestClient *ethclient.Client 29 relDB *models.RelDB 30 datastore *models.DB 31 poolChannel chan dia.Pool 32 doneChannel chan bool 33 blockchain string 34 exchangeName string 35 vaultContract string 36 startblockPoolRegister uint64 37 cachedAssets map[string]dia.Asset 38 } 39 40 // NewBalancerV2Scraper returns a Balancer V2 scraper 41 func NewBalancerV2Scraper(exchange dia.Exchange, relDB *models.RelDB, datastore *models.DB) *BalancerV2Scraper { 42 var ( 43 restClient *ethclient.Client 44 err error 45 poolChannel = make(chan dia.Pool) 46 doneChannel = make(chan bool) 47 scraper *BalancerV2Scraper 48 ) 49 50 log.Infof("Init rest client for %s.", exchange.BlockChain.Name) 51 restClient, err = ethclient.Dial(utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_URI_REST", balancerV2RestDial)) 52 if err != nil { 53 log.Fatal("init rest client: ", err) 54 } 55 56 scraper = &BalancerV2Scraper{ 57 RestClient: restClient, 58 relDB: relDB, 59 datastore: datastore, 60 poolChannel: poolChannel, 61 doneChannel: doneChannel, 62 blockchain: exchange.BlockChain.Name, 63 exchangeName: exchange.Name, 64 vaultContract: exchange.Contract, 65 cachedAssets: make(map[string]dia.Asset), 66 } 67 68 switch exchange.Name { 69 case dia.BalancerV2Exchange: 70 scraper.startblockPoolRegister = 12272146 71 case dia.BalancerV2ExchangeArbitrum: 72 scraper.startblockPoolRegister = 222832 73 case dia.BalancerV2ExchangePolygon: 74 scraper.startblockPoolRegister = 15832990 75 case dia.BeetsExchange: 76 scraper.startblockPoolRegister = 16896080 77 } 78 79 go func() { 80 scraper.fetchPools() 81 }() 82 83 return scraper 84 } 85 86 // fetchPools collects all available pools and sends them into the pool channel. 87 func (scraper *BalancerV2Scraper) fetchPools() { 88 events, err := scraper.allRegisteredPools() 89 if err != nil { 90 log.Fatal("fetch all registered pools: ", err) 91 } 92 93 caller, err := balancervault.NewBalancerVaultCaller(common.HexToAddress(scraper.vaultContract), scraper.RestClient) 94 if err != nil { 95 log.Fatal("make balancer vault caller: ", err) 96 } 97 98 for _, evt := range events { 99 poolTokens, err := caller.GetPoolTokens(&bind.CallOpts{}, evt.PoolId) 100 if err != nil { 101 log.Warn("get pool tokens: ", err) 102 } 103 assetvolumes := scraper.extractPoolInfo(poolTokens) 104 pool := dia.Pool{ 105 Exchange: dia.Exchange{Name: scraper.exchangeName}, 106 Blockchain: dia.BlockChain{Name: scraper.blockchain}, 107 Address: evt.PoolAddress.String(), 108 Assetvolumes: assetvolumes, 109 Time: time.Now(), 110 } 111 112 // Determine USD liquidity. 113 if pool.SufficientNativeBalance(GLOBAL_NATIVE_LIQUIDITY_THRESHOLD) { 114 scraper.datastore.GetPoolLiquiditiesUSD(&pool, priceCache) 115 } 116 117 scraper.poolChannel <- pool 118 } 119 scraper.doneChannel <- true 120 } 121 122 // allRegisteredPools returns a slice of all pool creation events. 123 func (scraper *BalancerV2Scraper) allRegisteredPools() ([]*balancervault.BalancerVaultPoolRegistered, error) { 124 var ( 125 offset uint64 = balancerV2FilterPageSize 126 startBlock uint64 = scraper.startblockPoolRegister 127 endBlock = startBlock + offset 128 events []*balancervault.BalancerVaultPoolRegistered 129 ) 130 131 filterer, err := balancervault.NewBalancerVaultFilterer(common.HexToAddress(scraper.vaultContract), scraper.RestClient) 132 if err != nil { 133 return nil, err 134 } 135 136 currBlock, err := scraper.RestClient.BlockNumber(context.Background()) 137 if err != nil { 138 return nil, err 139 } 140 141 for { 142 if endBlock > currBlock { 143 endBlock = currBlock 144 } 145 log.Infof("startblock - endblock: %v --- %v ", startBlock, endBlock) 146 147 it, err := filterer.FilterPoolRegistered(&bind.FilterOpts{ 148 Start: startBlock, 149 End: &endBlock, 150 }, nil, nil) 151 if err != nil { 152 log.Warn("filterpoolregistered: ", err) 153 continue 154 } 155 156 for it.Next() { 157 events = append(events, it.Event) 158 log.Info("pool address: ", it.Event.PoolAddress) 159 } 160 if err := it.Close(); err != nil { 161 log.Warn("closing iterator: ", it) 162 } 163 164 if endBlock == currBlock { 165 break 166 } 167 168 startBlock = endBlock + 1 169 endBlock = endBlock + offset 170 } 171 172 return events, nil 173 } 174 175 // extractPoolInfo returns assetvolumes in the correct format for a dia.Pool. 176 func (scraper *BalancerV2Scraper) extractPoolInfo(poolTokens struct { 177 Tokens []common.Address 178 Balances []*big.Int 179 LastChangeBlock *big.Int 180 }) (assetvolumes []dia.AssetVolume) { 181 for i := range poolTokens.Tokens { 182 183 asset, err := scraper.relDB.GetAsset(poolTokens.Tokens[i].Hex(), scraper.blockchain) 184 if err != nil { 185 asset, err = ethhelper.ETHAddressToAsset(poolTokens.Tokens[i], scraper.RestClient, scraper.blockchain) 186 if err != nil { 187 log.Warn("cannot fetch asset from address ", poolTokens.Tokens[i].Hex()) 188 continue 189 } 190 } 191 192 volume, _ := new(big.Float).Quo(big.NewFloat(0).SetInt(poolTokens.Balances[i]), new(big.Float).SetFloat64(math.Pow10(int(asset.Decimals)))).Float64() 193 194 assetvolumes = append(assetvolumes, dia.AssetVolume{Asset: asset, Volume: volume, Index: uint8(i)}) 195 } 196 return 197 } 198 199 // assetFromToken returns the dia.Asset corresponding to an address on the underlying scraper's blockchain. 200 func (scraper *BalancerV2Scraper) assetFromToken(token common.Address) (dia.Asset, error) { 201 cached, ok := scraper.cachedAssets[token.Hex()] 202 if !ok { 203 asset, err := ethhelper.ETHAddressToAsset(token, scraper.RestClient, scraper.blockchain) 204 if err != nil { 205 return dia.Asset{}, err 206 } 207 scraper.cachedAssets[token.Hex()] = asset 208 return asset, nil 209 } else { 210 return cached, nil 211 } 212 } 213 214 func (scraper *BalancerV2Scraper) Pool() chan dia.Pool { 215 return scraper.poolChannel 216 } 217 218 func (scraper *BalancerV2Scraper) Done() chan bool { 219 return scraper.doneChannel 220 }