github.com/diadata-org/diadata@v1.4.593/pkg/dia/service/assetservice/source/balancerv3.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 vault "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/balancerv3/vault" 11 vaultextension "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/balancerv3/vaultextension" 12 "go.uber.org/ratelimit" 13 14 "github.com/diadata-org/diadata/pkg/dia" 15 "github.com/diadata-org/diadata/pkg/utils" 16 "github.com/ethereum/go-ethereum/accounts/abi/bind" 17 "github.com/ethereum/go-ethereum/common" 18 "github.com/ethereum/go-ethereum/ethclient" 19 ) 20 21 const ( 22 balancerV3RateLimitPerSec = 50 23 balancerV3FilterPageSize = 5000 24 ) 25 26 type BalancerV3AssetSource struct { 27 RestClient *ethclient.Client 28 assetChannel chan dia.Asset 29 doneChannel chan bool 30 waitTime int 31 blockchain string 32 exchangeName string 33 cachedAssets sync.Map // map[string]dia.Asset 34 exchangeFactoryContractAddress string 35 startblockPoolRegister uint64 36 rl ratelimit.Limiter 37 } 38 39 func NewBalancerV3AssetSource(exchange dia.Exchange) (bas *BalancerV3AssetSource) { 40 41 bas = makeBalancerV3AssetSource(exchange, "", uniswapWaitMilliseconds) 42 bas.rl = ratelimit.New(balancerV3RateLimitPerSec) 43 44 switch exchange.Name { 45 case dia.BalancerV3Exchange: 46 bas.startblockPoolRegister = 21332121 47 } 48 49 // TO DO: implement postgres storage of last fetched block. 50 51 go func() { 52 bas.fetchAssets() 53 }() 54 return bas 55 56 } 57 58 func (bas *BalancerV3AssetSource) fetchAssets() { 59 60 pools, err := bas.listPools() 61 if err != nil { 62 log.Fatal("list available pools: ", err) 63 } 64 65 bas.getAssetsFromPools(pools) 66 67 bas.doneChannel <- true 68 69 } 70 71 // makeBalancerV3AssetSource returns an asset source as used in NewBalancerV3AssetSource. 72 func makeBalancerV3AssetSource(exchange dia.Exchange, restDial string, waitMilliseconds string) *BalancerV3AssetSource { 73 var ( 74 restClient *ethclient.Client 75 err error 76 assetChannel = make(chan dia.Asset) 77 doneChannel = make(chan bool) 78 bas *BalancerV3AssetSource 79 ) 80 81 log.Infof("Init rest client for %s.", exchange.BlockChain.Name) 82 restClient, err = ethclient.Dial(utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_URI_REST", restDial)) 83 if err != nil { 84 log.Fatal("init rest client: ", err) 85 } 86 var waitTime int 87 waitTimeString := utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_WAIT_TIME", waitMilliseconds) 88 waitTime, err = strconv.Atoi(waitTimeString) 89 if err != nil { 90 log.Error("could not parse wait time: ", err) 91 waitTime = 500 92 } 93 94 bas = &BalancerV3AssetSource{ 95 RestClient: restClient, 96 assetChannel: assetChannel, 97 doneChannel: doneChannel, 98 blockchain: exchange.BlockChain.Name, 99 waitTime: waitTime, 100 exchangeName: exchange.Name, 101 exchangeFactoryContractAddress: exchange.Contract, 102 } 103 return bas 104 } 105 106 // allRegisteredPools returns an iterator for all pools registered. 107 func (bas *BalancerV3AssetSource) allRegisteredPools() ([]*vault.VaultPoolRegistered, error) { 108 var ( 109 offset uint64 = balancerV3FilterPageSize 110 startBlock uint64 = bas.startblockPoolRegister 111 endBlock = startBlock + offset 112 events []*vault.VaultPoolRegistered 113 ) 114 115 filterer, err := vault.NewVaultFilterer(common.HexToAddress(bas.exchangeFactoryContractAddress), bas.RestClient) 116 if err != nil { 117 return nil, err 118 } 119 120 currBlock, err := bas.RestClient.BlockNumber(context.Background()) 121 if err != nil { 122 return nil, err 123 } 124 125 for { 126 if endBlock > currBlock { 127 endBlock = currBlock 128 } 129 log.Infof("startblock - endblock: %v --- %v ", startBlock, endBlock) 130 131 it, err := filterer.FilterPoolRegistered(&bind.FilterOpts{ 132 Start: startBlock, 133 End: &endBlock, 134 }, nil, nil) 135 if err != nil { 136 log.Warn("filterpoolregistered: ", err) 137 continue 138 } 139 140 for it.Next() { 141 events = append(events, it.Event) 142 } 143 if err := it.Close(); err != nil { 144 log.Warn("closing iterator: ", it) 145 } 146 147 if endBlock == currBlock { 148 break 149 } 150 151 startBlock = endBlock + 1 152 endBlock = endBlock + offset 153 } 154 155 return events, nil 156 } 157 158 // listPools returns a list of pools given by the addresses of the underlying assets. 159 func (bas *BalancerV3AssetSource) listPools() ([][]common.Address, error) { 160 161 events, err := bas.allRegisteredPools() 162 if err != nil { 163 return nil, err 164 } 165 166 vaultExtensionCaller, err := vaultextension.NewVaultextensionCaller(common.HexToAddress(bas.exchangeFactoryContractAddress), bas.RestClient) 167 if err != nil { 168 return [][]common.Address{}, err 169 } 170 171 pools := make([][]common.Address, len(events)) 172 for idx, evt := range events { 173 174 poolAssets, err := vaultExtensionCaller.GetPoolTokens(&bind.CallOpts{}, evt.Pool) 175 if err != nil { 176 log.Error("GetPoolTokens: ", err) 177 } 178 log.Infof("fetch pool %s", evt.Pool.Hex()) 179 180 pools[idx] = poolAssets 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 *BalancerV3AssetSource) 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 *BalancerV3AssetSource) 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 *BalancerV3AssetSource) Asset() chan dia.Asset { 225 return bas.assetChannel 226 } 227 228 func (bas *BalancerV3AssetSource) Done() chan bool { 229 return bas.doneChannel 230 }