github.com/diadata-org/diadata@v1.4.593/pkg/dia/service/assetservice/source/balancerv2.go (about) 1 package source 2 3 import ( 4 "context" 5 "strconv" 6 "strings" 7 "sync" 8 9 "github.com/diadata-org/diadata/pkg/dia/helpers/ethhelper" 10 balancervault "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/balancerv2/vault" 11 "go.uber.org/ratelimit" 12 13 "github.com/diadata-org/diadata/pkg/dia" 14 "github.com/diadata-org/diadata/pkg/utils" 15 "github.com/ethereum/go-ethereum/accounts/abi/bind" 16 "github.com/ethereum/go-ethereum/common" 17 "github.com/ethereum/go-ethereum/ethclient" 18 ) 19 20 const ( 21 balancerV2RateLimitPerSec = 50 22 balancerV2FilterPageSize = 5000 23 ) 24 25 type BalancerV2AssetSource struct { 26 RestClient *ethclient.Client 27 assetChannel chan dia.Asset 28 doneChannel chan bool 29 waitTime int 30 blockchain string 31 exchangeName string 32 cachedAssets sync.Map // map[string]dia.Asset 33 exchangeFactoryContractAddress string 34 startblockPoolRegister uint64 35 rl ratelimit.Limiter 36 } 37 38 func NewBalancerV2AssetSource(exchange dia.Exchange) (bas *BalancerV2AssetSource) { 39 40 bas = makeBalancerV2AssetSource(exchange, "", uniswapWaitMilliseconds) 41 bas.rl = ratelimit.New(balancerV2RateLimitPerSec) 42 43 switch exchange.Name { 44 case dia.BalancerV2Exchange: 45 bas.startblockPoolRegister = 12272146 46 case dia.BalancerV2ExchangeArbitrum: 47 bas.startblockPoolRegister = 222832 48 case dia.BeetsExchange: 49 bas.startblockPoolRegister = 16896080 50 case dia.BalancerV2ExchangePolygon: 51 bas.startblockPoolRegister = 15832990 52 } 53 54 go func() { 55 bas.fetchAssets() 56 }() 57 return bas 58 59 } 60 61 func (bas *BalancerV2AssetSource) fetchAssets() { 62 63 pools, err := bas.listPools() 64 if err != nil { 65 log.Fatal("list available pools: ", err) 66 } 67 68 bas.getAssetsFromPools(pools) 69 70 bas.doneChannel <- true 71 72 } 73 74 // makeBalancerV2AssetSource returns an asset source as used in NewBalancerV2AssetSource. 75 func makeBalancerV2AssetSource(exchange dia.Exchange, restDial string, waitMilliseconds string) *BalancerV2AssetSource { 76 var ( 77 restClient *ethclient.Client 78 err error 79 assetChannel = make(chan dia.Asset) 80 doneChannel = make(chan bool) 81 bas *BalancerV2AssetSource 82 ) 83 84 log.Infof("Init rest client for %s.", exchange.BlockChain.Name) 85 restClient, err = ethclient.Dial(utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_URI_REST", restDial)) 86 if err != nil { 87 log.Fatal("init rest client: ", err) 88 } 89 var waitTime int 90 waitTimeString := utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_WAIT_TIME", waitMilliseconds) 91 waitTime, err = strconv.Atoi(waitTimeString) 92 if err != nil { 93 log.Error("could not parse wait time: ", err) 94 waitTime = 500 95 } 96 97 bas = &BalancerV2AssetSource{ 98 RestClient: restClient, 99 assetChannel: assetChannel, 100 doneChannel: doneChannel, 101 blockchain: exchange.BlockChain.Name, 102 waitTime: waitTime, 103 exchangeName: exchange.Name, 104 exchangeFactoryContractAddress: exchange.Contract, 105 } 106 return bas 107 } 108 109 // allRegisteredPools returns an iterator for all pools registered. 110 func (bas *BalancerV2AssetSource) allRegisteredPools() ([]*balancervault.BalancerVaultPoolRegistered, error) { 111 var ( 112 offset uint64 = balancerV2FilterPageSize 113 startBlock uint64 = bas.startblockPoolRegister 114 endBlock = startBlock + offset 115 events []*balancervault.BalancerVaultPoolRegistered 116 ) 117 118 filterer, err := balancervault.NewBalancerVaultFilterer(common.HexToAddress(bas.exchangeFactoryContractAddress), bas.RestClient) 119 if err != nil { 120 return nil, err 121 } 122 123 currBlock, err := bas.RestClient.BlockNumber(context.Background()) 124 if err != nil { 125 return nil, err 126 } 127 128 for { 129 if endBlock > currBlock { 130 endBlock = currBlock 131 } 132 log.Infof("startblock - endblock: %v --- %v ", startBlock, endBlock) 133 134 it, err := filterer.FilterPoolRegistered(&bind.FilterOpts{ 135 Start: startBlock, 136 End: &endBlock, 137 }, nil, nil) 138 if err != nil { 139 log.Warn("filterpoolregistered: ", err) 140 continue 141 } 142 143 for it.Next() { 144 events = append(events, it.Event) 145 } 146 if err := it.Close(); err != nil { 147 log.Warn("closing iterator: ", it) 148 } 149 150 if endBlock == currBlock { 151 break 152 } 153 154 startBlock = endBlock + 1 155 endBlock = endBlock + offset 156 } 157 158 return events, nil 159 } 160 161 // listPools returns a list of pools given by the addresses of the underlying assets. 162 func (bas *BalancerV2AssetSource) listPools() ([][]common.Address, error) { 163 164 events, err := bas.allRegisteredPools() 165 if err != nil { 166 return nil, err 167 } 168 169 caller, err := balancervault.NewBalancerVaultCaller(common.HexToAddress(bas.exchangeFactoryContractAddress), bas.RestClient) 170 if err != nil { 171 return nil, err 172 } 173 174 pools := make([][]common.Address, len(events)) 175 for idx, evt := range events { 176 pool, err := caller.GetPoolTokens(&bind.CallOpts{}, evt.PoolId) 177 if err != nil { 178 log.Errorf("fetch pool %s: %v", evt.PoolAddress.Hex(), err) 179 } 180 pools[idx] = pool.Tokens 181 } 182 183 return pools, nil 184 } 185 186 // getAssetsFromPools fetches all assets from @pools and sends them into the asset channel. 187 func (bas *BalancerV2AssetSource) getAssetsFromPools(pools [][]common.Address) { 188 189 checkMap := make(map[string]struct{}) 190 191 for _, tokens := range pools { 192 for i := 0; i < len(tokens); i++ { 193 if _, ok := checkMap[tokens[i].Hex()]; ok { 194 continue 195 } else { 196 checkMap[tokens[i].Hex()] = struct{}{} 197 } 198 asset, err := bas.assetFromToken(tokens[i]) 199 if err != nil { 200 log.Error("get asset from token: ", err) 201 } 202 bas.assetChannel <- asset 203 } 204 } 205 206 } 207 208 func (bas *BalancerV2AssetSource) assetFromToken(token common.Address) (dia.Asset, error) { 209 cached, ok := bas.cachedAssets.Load(token.Hex()) 210 if !ok { 211 asset, err := ethhelper.ETHAddressToAsset(token, bas.RestClient, bas.blockchain) 212 if err != nil { 213 return dia.Asset{}, err 214 } 215 bas.cachedAssets.Store(token.Hex(), asset) 216 return asset, nil 217 } 218 219 asset := cached.(dia.Asset) 220 221 return asset, nil 222 } 223 224 func (bas *BalancerV2AssetSource) Asset() chan dia.Asset { 225 return bas.assetChannel 226 } 227 228 func (bas *BalancerV2AssetSource) Done() chan bool { 229 return bas.doneChannel 230 }