github.com/diadata-org/diadata@v1.4.593/pkg/dia/service/assetservice/source/curvefi.go (about) 1 package source 2 3 import ( 4 "math/big" 5 "strconv" 6 "strings" 7 "time" 8 9 "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/curvefi" 10 "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/curvefi/token" 11 "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/curvefifactory" 12 "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/curvefimeta" 13 "github.com/diadata-org/diadata/pkg/utils" 14 15 "github.com/diadata-org/diadata/pkg/dia" 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 curveRestDialEth = "" 23 curveRestDialFantom = "" 24 curveRestDialMoonbeam = "" 25 curveRestDialPolygon = "" 26 curveRestDialArbitrum = "" 27 28 polygonWaitMilliseconds = "500" 29 fantomWaitMilliseconds = "250" 30 arbitrumWaitMilliseconds = "300" 31 ) 32 33 type curveRegistry struct { 34 Address common.Address 35 Type int 36 } 37 38 // CurvefiAssetSource is a curve finance scraper on a specific blockchain. 39 type CurvefiAssetSource struct { 40 RestClient *ethclient.Client 41 assetChannel chan dia.Asset 42 doneChannel chan bool 43 blockchain string 44 registries []curveRegistry 45 waitTime int 46 } 47 48 func NewCurvefiAssetSource(exchange dia.Exchange) *CurvefiAssetSource { 49 50 var cas *CurvefiAssetSource 51 52 switch exchange.Name { 53 case dia.CurveFIExchange: 54 basePools := curveRegistry{Type: 1, Address: common.HexToAddress(exchange.Contract)} 55 cryptoswapPools := curveRegistry{Type: 1, Address: common.HexToAddress("0x8F942C20D02bEfc377D41445793068908E2250D0")} 56 metaPools := curveRegistry{Type: 2, Address: common.HexToAddress("0xB9fC157394Af804a3578134A6585C0dc9cc990d4")} 57 stableSwapRegistry := curveRegistry{Type: 3, Address: common.HexToAddress("0x6A8cbed756804B16E05E741eDaBd5cB544AE21bf")} 58 factoryPools := curveRegistry{Type: 3, Address: common.HexToAddress("0xF18056Bbd320E96A48e3Fbf8bC061322531aac99")} 59 factory2Pools := curveRegistry{Type: 3, Address: common.HexToAddress("0x4F8846Ae9380B90d2E71D5e3D042dff3E7ebb40d")} 60 registries := []curveRegistry{factoryPools, factory2Pools, basePools, cryptoswapPools, metaPools, stableSwapRegistry} 61 cas = makeCurvefiAssetSource(exchange, registries, curveRestDialEth, uniswapWaitMilliseconds) 62 case dia.CurveFIExchangeFantom: 63 exchange.Contract = "" 64 // basePools := curveRegistry{Type: 1, Address: common.HexToAddress(exchange.Contract)} 65 stableSwapFactory := curveRegistry{Type: 2, Address: common.HexToAddress("0x686d67265703D1f124c45E33d47d794c566889Ba")} 66 registries := []curveRegistry{stableSwapFactory} 67 cas = makeCurvefiAssetSource(exchange, registries, curveRestDialFantom, fantomWaitMilliseconds) 68 69 case dia.CurveFIExchangeMoonbeam: 70 exchange.Contract = "" 71 // basePools := curveRegistry{Type: 1, Address: common.HexToAddress(exchange.Contract)} 72 stableSwapFactory := curveRegistry{Type: 2, Address: common.HexToAddress("0x4244eB811D6e0Ef302326675207A95113dB4E1F8")} 73 registries := []curveRegistry{stableSwapFactory} 74 cas = makeCurvefiAssetSource(exchange, registries, curveRestDialMoonbeam, moonbeamWaitMilliseconds) 75 76 case dia.CurveFIExchangePolygon: 77 exchange.Contract = "" 78 // basePools := curveRegistry{Type: 1, Address: common.HexToAddress(exchange.Contract)} 79 stableSwapFactory := curveRegistry{Type: 2, Address: common.HexToAddress("0x722272D36ef0Da72FF51c5A65Db7b870E2e8D4ee")} 80 registries := []curveRegistry{stableSwapFactory} 81 cas = makeCurvefiAssetSource(exchange, registries, curveRestDialPolygon, polygonWaitMilliseconds) 82 83 case dia.CurveFIExchangeArbitrum: 84 exchange.Contract = "" 85 stableSwapFactory := curveRegistry{Type: 2, Address: common.HexToAddress("0xb17b674D9c5CB2e441F8e196a2f048A81355d031")} 86 registries := []curveRegistry{stableSwapFactory} 87 cas = makeCurvefiAssetSource(exchange, registries, curveRestDialArbitrum, arbitrumWaitMilliseconds) 88 } 89 90 go func() { 91 cas.fetchAssets() 92 }() 93 return cas 94 } 95 96 // makeCurvefiAssetSource returns a curve finance scraper as used in NewCurvefiScraper. 97 func makeCurvefiAssetSource(exchange dia.Exchange, registries []curveRegistry, restDial string, waitMilliseconds string) *CurvefiAssetSource { 98 var ( 99 restClient *ethclient.Client 100 err error 101 assetChannel = make(chan dia.Asset) 102 doneChannel = make(chan bool) 103 cas *CurvefiAssetSource 104 ) 105 106 log.Infof("Init rest client for %s.", exchange.BlockChain.Name) 107 restClient, err = ethclient.Dial(utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_URI_REST", restDial)) 108 if err != nil { 109 log.Fatal("init rest client: ", err) 110 } 111 112 var waitTime int 113 waitTimeString := utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_WAIT_TIME", waitMilliseconds) 114 waitTime, err = strconv.Atoi(waitTimeString) 115 if err != nil { 116 log.Error("could not parse wait time: ", err) 117 waitTime = 500 118 } 119 cas = &CurvefiAssetSource{ 120 RestClient: restClient, 121 assetChannel: assetChannel, 122 doneChannel: doneChannel, 123 blockchain: exchange.BlockChain.Name, 124 waitTime: waitTime, 125 registries: registries, 126 } 127 128 return cas 129 } 130 131 func (cas *CurvefiAssetSource) Asset() chan dia.Asset { 132 return cas.assetChannel 133 } 134 135 func (cas *CurvefiAssetSource) Done() chan bool { 136 return cas.doneChannel 137 } 138 139 func (cas *CurvefiAssetSource) fetchAssets() { 140 // Load pools from registries. 141 for _, registry := range cas.registries { 142 err := cas.loadPoolsAndCoins(registry) 143 if err != nil { 144 log.Error("loadPoolsAndCoins: ", err) 145 } 146 } 147 cas.doneChannel <- true 148 } 149 150 // contract.poolList.map(contract.GetPoolCoins(pool).) 151 func (cas *CurvefiAssetSource) loadPoolsAndCoins(registry curveRegistry) (err error) { 152 153 if registry.Type == 1 { 154 log.Info("load base type pools..") 155 var contract *curvefi.CurvefiCaller 156 var poolCount *big.Int 157 contract, err = curvefi.NewCurvefiCaller(registry.Address, cas.RestClient) 158 if err != nil { 159 log.Error("NewCurvefiCaller: ", err) 160 } 161 poolCount, err = contract.PoolCount(&bind.CallOpts{}) 162 if err != nil { 163 log.Error("PoolCount: ", err) 164 } 165 log.Info("poolCount: ", int(poolCount.Int64())) 166 for i := 0; i < int(poolCount.Int64()); i++ { 167 var pool common.Address 168 pool, err = contract.PoolList(&bind.CallOpts{}, big.NewInt(int64(i))) 169 if err != nil { 170 log.Error("PoolList: ", err) 171 } 172 173 err = cas.loadPoolData(pool.Hex(), registry) 174 if err != nil { 175 log.Info("load pool data: ", err) 176 // return err 177 } 178 } 179 } 180 181 if registry.Type == 2 || registry.Type == 3 { 182 log.Info("load meta type pools...") 183 var contract *curvefimeta.CurvefimetaCaller 184 var poolCount *big.Int 185 contract, err = curvefimeta.NewCurvefimetaCaller(registry.Address, cas.RestClient) 186 if err != nil { 187 log.Error("NewCurvefiCaller: ", err) 188 } 189 190 poolCount, err = contract.PoolCount(&bind.CallOpts{}) 191 if err != nil { 192 log.Error("PoolCount: ", err) 193 } 194 log.Info("poolCount: ", int(poolCount.Int64())) 195 for i := 0; i < int(poolCount.Int64()); i++ { 196 var pool common.Address 197 pool, err = contract.PoolList(&bind.CallOpts{}, big.NewInt(int64(i))) 198 if err != nil { 199 log.Error("PoolList: ", err) 200 } 201 202 err = cas.loadPoolData(pool.Hex(), registry) 203 if err != nil { 204 log.Info("load pool data: ", err) 205 // return err 206 } 207 } 208 } 209 return err 210 } 211 212 func (cas *CurvefiAssetSource) loadPoolData(pool string, registry curveRegistry) error { 213 var poolCoins [8]common.Address 214 215 if registry.Type == 1 { 216 contract, err := curvefi.NewCurvefiCaller(registry.Address, cas.RestClient) 217 if err != nil { 218 log.Error("loadPoolData - NewCurvefiCaller: ", err) 219 } 220 221 poolCoins, err = contract.GetCoins(&bind.CallOpts{}, common.HexToAddress(pool)) 222 if err != nil { 223 log.Error("loadPoolData - GetCoins: ", err) 224 } 225 226 poolName, err := contract.GetPoolName(&bind.CallOpts{}, common.HexToAddress(pool)) 227 if err != nil { 228 log.Error("loadPoolData - GetPoolName: ", err) 229 } 230 log.Info("pool name: ", poolName) 231 } 232 233 if registry.Type == 2 { 234 // common.HexToAddress(curveFiMetaPoolsFactory) || factoryContract == common.HexToAddress(curveFiCryptoPoolFactory) { 235 contract, err := curvefimeta.NewCurvefimetaCaller(registry.Address, cas.RestClient) 236 237 if err != nil { 238 log.Error("loadPoolData - NewCurvefiCaller: ", err) 239 } 240 241 aux, err := contract.GetCoins(&bind.CallOpts{}, common.HexToAddress(pool)) 242 if err != nil { 243 log.Error("loadPoolData - GetCoins: ", err) 244 } 245 // GetCoins on meta pools returns [4]common.Address instead of [8]common.Address for standard pools. 246 //nolint 247 for i, item := range aux { 248 poolCoins[i] = item 249 } 250 251 } 252 if registry.Type == 3 { 253 log.Info("pool type 3...") 254 contract, err := curvefifactory.NewCurvefifactoryCaller(common.HexToAddress(pool), cas.RestClient) 255 if err != nil { 256 log.Error("loadPoolData - NewCurvefiCaller: ", err) 257 } 258 259 var i int64 260 poolAssetAddress, err := contract.Coins(&bind.CallOpts{}, big.NewInt(i)) 261 if err != nil { 262 log.Error("loadPoolData - GetCoins: ", err) 263 } 264 for poolAssetAddress != (common.Address{}) && i < 8 { 265 poolCoins[i] = poolAssetAddress 266 i++ 267 poolAssetAddress, err = contract.Coins(&bind.CallOpts{}, big.NewInt(i)) 268 if err != nil { 269 log.Warn("loadPoolData - GetCoins: ", err) 270 } 271 } 272 273 } 274 275 var err error 276 checkMap := make(map[string]struct{}) 277 278 for _, c := range poolCoins { 279 time.Sleep(time.Duration(cas.waitTime) * time.Millisecond) 280 var ( 281 coinCaller *token.TokenCaller 282 decimals *big.Int 283 asset dia.Asset 284 ) 285 if c == common.HexToAddress("0x0000000000000000000000000000000000000000") { 286 continue 287 } else { 288 asset.Address = c.Hex() 289 coinCaller, err = token.NewTokenCaller(c, cas.RestClient) 290 if err != nil { 291 log.Error("NewTokenCaller: ", err) 292 continue 293 } 294 asset.Symbol, err = coinCaller.Symbol(&bind.CallOpts{}) 295 if err != nil { 296 log.Error("Symbol: ", err, c.Hex()) 297 continue 298 } 299 decimals, err = coinCaller.Decimals(&bind.CallOpts{}) 300 if err != nil { 301 log.Error("Decimals: ", err) 302 continue 303 } 304 asset.Decimals = uint8(decimals.Int64()) 305 306 asset.Name, err = coinCaller.Name(&bind.CallOpts{}) 307 if err != nil { 308 log.Error("Name: ", err) 309 continue 310 } 311 } 312 asset.Blockchain = cas.blockchain 313 314 if _, ok := checkMap[asset.Address]; !ok { 315 if asset.Symbol != "" { 316 checkMap[asset.Address] = struct{}{} 317 cas.assetChannel <- asset 318 } 319 } 320 321 } 322 return err 323 }