github.com/diadata-org/diadata@v1.4.593/pkg/dia/scraper/liquidity-scrapers/BalancerV3Scraper.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 vault "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/balancerv3/vault" 16 vaultextension "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/balancerv3/vaultextension" 17 models "github.com/diadata-org/diadata/pkg/model" 18 "github.com/diadata-org/diadata/pkg/utils" 19 20 "github.com/diadata-org/diadata/pkg/dia" 21 ) 22 23 const ( 24 balancerV3FilterPageSize = 5000 25 balancerV3RestDial = "" 26 ) 27 28 type BalancerV3Scraper struct { 29 RestClient *ethclient.Client 30 relDB *models.RelDB 31 datastore *models.DB 32 poolChannel chan dia.Pool 33 doneChannel chan bool 34 blockchain string 35 exchangeName string 36 vaultContract string 37 startblockPoolRegister uint64 38 cachedAssets map[string]dia.Asset 39 } 40 41 // NewBalancerV2Scraper returns a Balancer V2 scraper 42 func NewBalancerV3Scraper(exchange dia.Exchange, relDB *models.RelDB, datastore *models.DB) *BalancerV3Scraper { 43 var ( 44 restClient *ethclient.Client 45 err error 46 poolChannel = make(chan dia.Pool) 47 doneChannel = make(chan bool) 48 scraper *BalancerV3Scraper 49 ) 50 51 log.Infof("Init rest client for %s.", exchange.BlockChain.Name) 52 restClient, err = ethclient.Dial(utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_URI_REST", balancerV3RestDial)) 53 if err != nil { 54 log.Fatal("init rest client: ", err) 55 } 56 57 scraper = &BalancerV3Scraper{ 58 RestClient: restClient, 59 relDB: relDB, 60 datastore: datastore, 61 poolChannel: poolChannel, 62 doneChannel: doneChannel, 63 blockchain: exchange.BlockChain.Name, 64 exchangeName: exchange.Name, 65 vaultContract: exchange.Contract, 66 cachedAssets: make(map[string]dia.Asset), 67 } 68 69 switch exchange.Name { 70 case dia.BalancerV3Exchange: 71 scraper.startblockPoolRegister = 21332121 72 73 } 74 75 go func() { 76 scraper.fetchPools() 77 }() 78 79 return scraper 80 } 81 82 // fetchPools collects all available pools and sends them into the pool channel. 83 func (scraper *BalancerV3Scraper) fetchPools() { 84 events, err := scraper.allRegisteredPools() 85 if err != nil { 86 log.Fatal("fetch all registered pools: ", err) 87 } 88 89 vaultExtensionCaller, err := vaultextension.NewVaultextensionCaller(common.HexToAddress(scraper.vaultContract), scraper.RestClient) 90 if err != nil { 91 log.Fatal("vault extension caller: ", err) 92 } 93 94 for _, evt := range events { 95 poolTokens, err := vaultExtensionCaller.GetPoolData(&bind.CallOpts{}, evt.Pool) 96 if err != nil { 97 log.Warn("get pool tokens: ", err) 98 } 99 assetvolumes := scraper.extractPoolInfo(poolTokens) 100 pool := dia.Pool{ 101 Exchange: dia.Exchange{Name: scraper.exchangeName}, 102 Blockchain: dia.BlockChain{Name: scraper.blockchain}, 103 Address: evt.Pool.String(), 104 Assetvolumes: assetvolumes, 105 Time: time.Now(), 106 } 107 108 // Determine USD liquidity. 109 if pool.SufficientNativeBalance(GLOBAL_NATIVE_LIQUIDITY_THRESHOLD) { 110 scraper.datastore.GetPoolLiquiditiesUSD(&pool, priceCache) 111 } 112 113 scraper.poolChannel <- pool 114 } 115 scraper.doneChannel <- true 116 } 117 118 // allRegisteredPools returns a slice of all pool creation events. 119 func (scraper *BalancerV3Scraper) allRegisteredPools() ([]*vault.VaultPoolRegistered, error) { 120 var ( 121 offset uint64 = balancerV3FilterPageSize 122 startBlock uint64 = scraper.startblockPoolRegister 123 endBlock = startBlock + offset 124 events []*vault.VaultPoolRegistered 125 ) 126 127 filterer, err := vault.NewVaultFilterer(common.HexToAddress(scraper.vaultContract), scraper.RestClient) 128 if err != nil { 129 return nil, err 130 } 131 132 currBlock, err := scraper.RestClient.BlockNumber(context.Background()) 133 if err != nil { 134 return nil, err 135 } 136 137 for { 138 if endBlock > currBlock { 139 endBlock = currBlock 140 } 141 log.Infof("startblock - endblock: %v --- %v ", startBlock, endBlock) 142 143 it, err := filterer.FilterPoolRegistered(&bind.FilterOpts{ 144 Start: startBlock, 145 End: &endBlock, 146 }, nil, nil) 147 if err != nil { 148 log.Warn("filterpoolregistered: ", err) 149 continue 150 } 151 152 for it.Next() { 153 events = append(events, it.Event) 154 } 155 if err := it.Close(); err != nil { 156 log.Warn("closing iterator: ", it) 157 } 158 159 if endBlock == currBlock { 160 break 161 } 162 163 startBlock = endBlock + 1 164 endBlock = endBlock + offset 165 } 166 167 return events, nil 168 } 169 170 // extractPoolInfo returns assetvolumes in the correct format for a dia.Pool. 171 func (scraper *BalancerV3Scraper) extractPoolInfo(poolData vaultextension.PoolData) (assetvolumes []dia.AssetVolume) { 172 for i := range poolData.Tokens { 173 174 asset, err := scraper.relDB.GetAsset(poolData.Tokens[i].Hex(), scraper.blockchain) 175 if err != nil { 176 asset, err = ethhelper.ETHAddressToAsset(poolData.Tokens[i], scraper.RestClient, scraper.blockchain) 177 if err != nil { 178 log.Warn("cannot fetch asset from address ", poolData.Tokens[i].Hex()) 179 continue 180 } 181 } 182 183 volume, _ := new(big.Float).Quo(big.NewFloat(0).SetInt(poolData.BalancesRaw[i]), new(big.Float).SetFloat64(math.Pow10(int(asset.Decimals)))).Float64() 184 185 assetvolumes = append(assetvolumes, dia.AssetVolume{Asset: asset, Volume: volume, Index: uint8(i)}) 186 } 187 return 188 } 189 190 func (scraper *BalancerV3Scraper) Pool() chan dia.Pool { 191 return scraper.poolChannel 192 } 193 194 func (scraper *BalancerV3Scraper) Done() chan bool { 195 return scraper.doneChannel 196 }