github.com/diadata-org/diadata@v1.4.593/pkg/dia/scraper/exchange-scrapers/BancorScraper.go (about) 1 package scrapers 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "math" 8 "math/big" 9 "strings" 10 "sync" 11 "time" 12 13 ConverterRegistry "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/bancor" 14 "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/bancor/BancorNetwork" 15 "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/bancor/ConverterTypeFour" 16 ConvertertypeOne "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/bancor/ConverterTypeOne" 17 "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/bancor/ConverterTypeThree" 18 "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/bancor/ConverterTypeZero" 19 uniswapcontract "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/uniswap" 20 21 "github.com/diadata-org/diadata/pkg/dia" 22 "github.com/diadata-org/diadata/pkg/utils" 23 "github.com/ethereum/go-ethereum/accounts/abi/bind" 24 "github.com/ethereum/go-ethereum/common" 25 "github.com/ethereum/go-ethereum/ethclient" 26 ) 27 28 type BancorPool struct { 29 Reserves []struct { 30 DltID string `json:"dlt_id"` 31 Symbol string `json:"symbol"` 32 Name string `json:"name"` 33 Balance struct { 34 Usd string `json:"usd"` 35 } `json:"balance"` 36 Weight int `json:"weight"` 37 Price struct { 38 Usd string `json:"usd"` 39 } `json:"price"` 40 Price24HAgo struct { 41 Usd string `json:"usd"` 42 } `json:"price_24h_ago"` 43 Volume24H struct { 44 Usd string `json:"usd"` 45 Base string `json:"base"` 46 } `json:"volume_24h"` 47 } `json:"reserves"` 48 DltType string `json:"dlt_type"` 49 DltID string `json:"dlt_id"` 50 Type int `json:"type"` 51 Version int `json:"version"` 52 Symbol string `json:"symbol"` 53 Name string `json:"name"` 54 Supply string `json:"supply"` 55 ConverterDltID string `json:"converter_dlt_id"` 56 ConversionFee string `json:"conversion_fee"` 57 Liquidity struct { 58 Usd string `json:"usd"` 59 } `json:"liquidity"` 60 Volume24H struct { 61 Usd string `json:"usd"` 62 } `json:"volume_24h"` 63 Fees24H struct { 64 Usd string `json:"usd"` 65 } `json:"fees_24h"` 66 } 67 68 type BancorPools struct { 69 Data []BancorPool `json:"data"` 70 Timestamp struct { 71 Ethereum struct { 72 Block int `json:"block"` 73 Timestamp int64 `json:"timestamp"` 74 } `json:"ethereum"` 75 } `json:"timestamp"` 76 } 77 78 type BancorSwap struct { 79 Pair dia.ExchangePair 80 FromAmount float64 81 ToAmount float64 82 ID string 83 Timestamp int64 84 } 85 86 type BancorScraper struct { 87 WsClient *ethclient.Client 88 RestClient *ethclient.Client 89 90 exchangeName string 91 92 // channels to signal events 93 run bool 94 initDone chan nothing 95 shutdown chan nothing 96 shutdownDone chan nothing 97 98 errorLock sync.RWMutex 99 error error 100 closed bool 101 102 pairScrapers map[string]*BancorPairScraper 103 productPairIds map[string]int 104 chanTrades chan *dia.Trade 105 } 106 107 func NewBancorScraper(exchange dia.Exchange, scrape bool) *BancorScraper { 108 var wsClient, restClient *ethclient.Client 109 var err error 110 111 restClient, err = ethclient.Dial(utils.Getenv("ETH_URI_REST", restDialEth)) 112 if err != nil { 113 log.Fatal("init rest client: ", err) 114 } 115 116 wsClient, err = ethclient.Dial(utils.Getenv("ETH_URI_WS", wsDialEth)) 117 if err != nil { 118 log.Fatal("init ws client: ", err) 119 } 120 121 scraper := &BancorScraper{ 122 exchangeName: exchange.Name, 123 WsClient: wsClient, 124 RestClient: restClient, 125 initDone: make(chan nothing), 126 shutdown: make(chan nothing), 127 shutdownDone: make(chan nothing), 128 productPairIds: make(map[string]int), 129 pairScrapers: make(map[string]*BancorPairScraper), 130 chanTrades: make(chan *dia.Trade), 131 } 132 133 if scrape { 134 go scraper.mainLoop() 135 } 136 return scraper 137 } 138 139 func (scraper *BancorScraper) mainLoop() { 140 141 scraper.GetpoolAddress() 142 143 sink, err := scraper.GetConversion() 144 if err != nil { 145 log.Errorln("Error GetConversion", err) 146 } 147 148 go func() { 149 for { 150 151 rawSwap := <-sink 152 revRawSwap := reverseBNTSwap(*rawSwap) 153 154 var address []common.Address 155 swap, err := scraper.normalizeSwap(revRawSwap) 156 if err != nil { 157 log.Error("error normalizeSwap: ", err) 158 159 } 160 161 price, volume := scraper.getSwapData(swap) 162 address = append(address, revRawSwap.FromToken) 163 address = append(address, revRawSwap.ToToken) 164 165 pair := scraper.GetPair(address) 166 167 trade := &dia.Trade{ 168 Symbol: pair.Symbol, 169 Pair: pair.ForeignName, 170 Price: price, 171 Volume: volume, 172 Time: time.Now(), 173 ForeignTradeID: revRawSwap.Raw.TxHash.String(), 174 Source: scraper.exchangeName, 175 BaseToken: pair.UnderlyingPair.BaseToken, 176 QuoteToken: pair.UnderlyingPair.QuoteToken, 177 VerifiedPair: true, 178 } 179 180 log.Info("Got Trade: ", trade) 181 scraper.chanTrades <- trade 182 183 } 184 }() 185 if scraper.error == nil { 186 scraper.error = errors.New("Main loop terminated by Close().") 187 } 188 scraper.cleanup(nil) 189 } 190 191 // Reverse swap involving BNT such that pair is always XXX-BNT. 192 func reverseBNTSwap(rawSwap BancorNetwork.BancorNetworkConversion) BancorNetwork.BancorNetworkConversion { 193 var revRawSwap BancorNetwork.BancorNetworkConversion 194 fmt.Println("raw swap ToToken: ", rawSwap.ToToken.Hex()) 195 if rawSwap.FromToken.Hex() == "0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C" { 196 revRawSwap.ToAmount = rawSwap.FromAmount 197 revRawSwap.FromAmount = rawSwap.ToAmount 198 revRawSwap.FromToken = rawSwap.ToToken 199 revRawSwap.ToToken = rawSwap.FromToken 200 revRawSwap.Raw = rawSwap.Raw 201 return revRawSwap 202 } 203 return rawSwap 204 } 205 206 func (scraper *BancorScraper) GetpoolAddress() { 207 // Get All Anchors 208 209 converTerRegistryAddress := common.HexToAddress("0xC0205e203F423Bcd8B2a4d6f8C8A154b0Aa60F19") 210 211 converter, err := ConverterRegistry.NewConverterRegistryCaller(converTerRegistryAddress, scraper.RestClient) 212 if err != nil { 213 log.Errorln("Error Getting Anchors", err) 214 } 215 216 _, err = converter.GetAnchors(&bind.CallOpts{}) 217 if err != nil { 218 log.Errorln("Error Getting Anchors", err) 219 } 220 221 } 222 223 func (scraper *BancorScraper) GetConversion() (chan *BancorNetwork.BancorNetworkConversion, error) { 224 225 sink := make(chan *BancorNetwork.BancorNetworkConversion) 226 227 var conversionFiltererContract *BancorNetwork.BancorNetworkFilterer 228 229 address := common.HexToAddress("0x2F9EC37d6CcFFf1caB21733BdaDEdE11c823cCB0") // bancor Network 230 conversionFiltererContract, err := BancorNetwork.NewBancorNetworkFilterer(address, scraper.WsClient) 231 if err != nil { 232 return nil, err 233 } 234 235 subs, err := conversionFiltererContract.WatchConversion(&bind.WatchOpts{}, sink, []common.Address{}, []common.Address{}, []common.Address{}) 236 if err != nil { 237 log.Error("error in get swaps channel: ", err) 238 } 239 log.Infoln("Subscribed", subs) 240 241 return sink, nil 242 243 } 244 245 // normalizeUniswapSwap takes a swap as returned by the swap contract's channel and converts it to a UniswapSwap type 246 func (scrapper *BancorScraper) normalizeSwap(swap BancorNetwork.BancorNetworkConversion) (BancorSwap, error) { 247 var normalizedSwap BancorSwap 248 if swap.FromToken.Hex() == "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" { 249 amount0, _ := new(big.Float).Quo(big.NewFloat(0).SetInt(swap.FromAmount), new(big.Float).SetFloat64(math.Pow10(18))).Float64() 250 normalizedSwap.FromAmount = amount0 251 } else { 252 fromToken, err := uniswapcontract.NewIERC20(swap.FromToken, scrapper.RestClient) 253 if err != nil { 254 return normalizedSwap, err 255 } 256 fromTokenDecimal, err := fromToken.Decimals(&bind.CallOpts{}) 257 if err != nil { 258 return normalizedSwap, err 259 } 260 decimals0 := int(fromTokenDecimal) 261 amount0, _ := new(big.Float).Quo(big.NewFloat(0).SetInt(swap.FromAmount), new(big.Float).SetFloat64(math.Pow10(decimals0))).Float64() 262 normalizedSwap.FromAmount = amount0 263 } 264 265 if swap.ToToken.Hex() == "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" { 266 amount1, _ := new(big.Float).Quo(big.NewFloat(0).SetInt(swap.ToAmount), new(big.Float).SetFloat64(math.Pow10(18))).Float64() 267 normalizedSwap.ToAmount = amount1 268 } else { 269 toToken, err := uniswapcontract.NewIERC20(swap.ToToken, scrapper.RestClient) 270 if err != nil { 271 return normalizedSwap, err 272 } 273 toTokenDecimal, err := toToken.Decimals(&bind.CallOpts{}) 274 if err != nil { 275 return normalizedSwap, err 276 } 277 decimals1 := int(toTokenDecimal) 278 amount1, _ := new(big.Float).Quo(big.NewFloat(0).SetInt(swap.ToAmount), new(big.Float).SetFloat64(math.Pow10(decimals1))).Float64() 279 normalizedSwap.ToAmount = amount1 280 } 281 282 pair := scrapper.GetPair([]common.Address{swap.ToToken, swap.FromToken}) 283 pair, _ = scrapper.NormalizePair(pair) 284 normalizedSwap.Pair = pair 285 normalizedSwap.ID = swap.Raw.TxHash.Hex() 286 normalizedSwap.Timestamp = time.Now().Unix() 287 288 return normalizedSwap, nil 289 } 290 291 func (scrapper *BancorScraper) getSwapData(swap BancorSwap) (price float64, volume float64) { 292 volume = swap.FromAmount 293 price = swap.ToAmount / swap.FromAmount 294 return 295 } 296 297 func (scraper *BancorScraper) NormalizePair(pair dia.ExchangePair) (dia.ExchangePair, error) { 298 if pair.UnderlyingPair.BaseToken.Address == "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" { 299 pair.UnderlyingPair.BaseToken.Address = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" 300 } 301 if pair.UnderlyingPair.QuoteToken.Address == "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" { 302 pair.UnderlyingPair.QuoteToken.Address = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" 303 } 304 return pair, nil 305 } 306 307 func (scraper *BancorScraper) ConverterTypeZero(address common.Address) (tokenAddress []common.Address, err error) { 308 309 contract, err := ConverterTypeZero.NewConverterTypeZeroCaller(address, scraper.RestClient) 310 if err != nil { 311 return 312 } 313 314 tokenCount, err := contract.ConnectorTokenCount(&bind.CallOpts{}) 315 if err != nil { 316 return 317 } 318 319 if tokenCount == 2 { 320 token1, err := contract.ConnectorTokens(&bind.CallOpts{}, big.NewInt(1)) 321 if err != nil { 322 log.Println("Error", err) 323 } 324 tokenAddress = append(tokenAddress, token1) 325 token2, err := contract.ConnectorTokens(&bind.CallOpts{}, big.NewInt(0)) 326 if err != nil { 327 log.Println("Error", err) 328 } 329 tokenAddress = append(tokenAddress, token2) 330 331 } 332 333 return 334 335 } 336 337 func (scraper *BancorScraper) ConverterTypeOne(address common.Address) (tokenAddress []common.Address, err error) { 338 339 contract, err := ConvertertypeOne.NewConvertertypeOne(address, scraper.RestClient) 340 if err != nil { 341 return 342 } 343 344 tokenCount, err := contract.ConnectorTokenCount(&bind.CallOpts{}) 345 if err != nil { 346 return 347 } 348 349 if tokenCount == 2 { 350 token1, err := contract.ConnectorTokens(&bind.CallOpts{}, big.NewInt(1)) 351 if err != nil { 352 log.Println("Error", err) 353 } 354 tokenAddress = append(tokenAddress, token1) 355 token2, err := contract.ConnectorTokens(&bind.CallOpts{}, big.NewInt(0)) 356 if err != nil { 357 log.Println("Error", err) 358 } 359 tokenAddress = append(tokenAddress, token2) 360 361 } 362 363 return 364 365 } 366 367 func (scraper *BancorScraper) ConverterTypeThree(address common.Address) (tokenAddress []common.Address, err error) { 368 369 contract, err := ConverterTypeThree.NewConverterTypeThree(address, scraper.RestClient) 370 if err != nil { 371 return 372 } 373 374 tokenCount, err := contract.ConnectorTokenCount(&bind.CallOpts{}) 375 if err != nil { 376 return 377 } 378 379 log.Println("tokenCount", tokenCount) 380 381 if tokenCount == 2 { 382 token1, err := contract.ConnectorTokens(&bind.CallOpts{}, big.NewInt(1)) 383 if err != nil { 384 log.Println("Error", err) 385 } 386 tokenAddress = append(tokenAddress, token1) 387 token2, err := contract.ConnectorTokens(&bind.CallOpts{}, big.NewInt(0)) 388 if err != nil { 389 log.Println("Error", err) 390 } 391 tokenAddress = append(tokenAddress, token2) 392 393 } 394 395 return 396 397 } 398 399 func (scraper *BancorScraper) ConverterTypeFour(address common.Address) (tokenAddress []common.Address, err error) { 400 401 contract, err := ConverterTypeFour.NewConverterTypeFour(address, scraper.RestClient) 402 if err != nil { 403 return 404 } 405 406 tokenCount, err := contract.ConnectorTokenCount(&bind.CallOpts{}) 407 if err != nil { 408 return 409 } 410 411 if tokenCount == 2 { 412 token1, err := contract.ConnectorTokens(&bind.CallOpts{}, big.NewInt(1)) 413 if err != nil { 414 log.Println("Error", err) 415 } 416 tokenAddress = append(tokenAddress, token1) 417 token2, err := contract.ConnectorTokens(&bind.CallOpts{}, big.NewInt(0)) 418 if err != nil { 419 log.Println("Error", err) 420 } 421 tokenAddress = append(tokenAddress, token2) 422 423 } 424 425 return 426 427 } 428 429 func (scraper *BancorScraper) FillSymbolData(symbol string) (dia.Asset, error) { 430 return dia.Asset{Symbol: symbol}, nil 431 } 432 433 func (scraper *BancorScraper) FetchAvailablePairs() (pairs []dia.ExchangePair, err error) { 434 pools, err := scraper.readPools() 435 if err != nil { 436 log.Error("Couldn't obtain Bancor product ids:", err) 437 } 438 for _, pool := range pools { 439 var address []common.Address 440 441 switch pool.Type { 442 case 0: 443 { 444 address, err = scraper.ConverterTypeZero(common.HexToAddress(pool.ConverterDltID)) 445 if err != nil { 446 log.Errorln("Error getting Address", err) 447 } 448 } 449 case 1: 450 { 451 address, err = scraper.ConverterTypeOne(common.HexToAddress(pool.ConverterDltID)) 452 if err != nil { 453 log.Errorln("Error getting Address", err) 454 } 455 } 456 case 3: 457 { 458 address, err = scraper.ConverterTypeThree(common.HexToAddress(pool.ConverterDltID)) 459 if err != nil { 460 log.Errorln("Error getting Address", err) 461 } 462 } 463 case 4: 464 { 465 address, err = scraper.ConverterTypeFour(common.HexToAddress(pool.ConverterDltID)) 466 if err != nil { 467 log.Errorln("Error getting Address", err) 468 } 469 } 470 } 471 472 pair := scraper.GetPair(address) 473 474 if pair.Symbol != "" && strings.Split(pair.ForeignName, "-")[1] != "" { 475 log.Println("found pair: ", pair) 476 pairs = append(pairs, pair) 477 } 478 479 } 480 481 return 482 } 483 484 func (scraper *BancorScraper) GetPair(address []common.Address) dia.ExchangePair { 485 var ( 486 symbol0 string 487 symbol1 string 488 ) 489 490 token0, err := uniswapcontract.NewIERC20Caller(address[0], scraper.RestClient) 491 if err != nil { 492 log.Errorln("Error Getting token 0 ", err) 493 } 494 token1, err := uniswapcontract.NewIERC20Caller(address[1], scraper.RestClient) 495 if err != nil { 496 log.Errorln("Error Getting token 1 ", err) 497 } 498 499 if address[0].Hex() == "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" { 500 address[0] = common.HexToAddress("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2") 501 symbol0 = "WETH" 502 } else { 503 symbol0, err = token0.Symbol(&bind.CallOpts{}) 504 if err != nil { 505 log.Error(err) 506 } 507 } 508 if address[1].Hex() == "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" { 509 address[0] = common.HexToAddress("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2") 510 symbol1 = "WETH" 511 } else { 512 symbol1, err = token1.Symbol(&bind.CallOpts{}) 513 if err != nil { 514 log.Error(err) 515 } 516 } 517 518 basetoken := dia.Asset{ 519 Symbol: symbol1, 520 Address: address[1].Hex(), 521 Blockchain: dia.ETHEREUM, 522 } 523 quotetoken := dia.Asset{ 524 Symbol: symbol0, 525 Address: address[0].Hex(), 526 Blockchain: dia.ETHEREUM, 527 } 528 529 return dia.ExchangePair{ 530 ForeignName: symbol0 + "-" + symbol1, 531 Symbol: symbol0, 532 Exchange: scraper.exchangeName, 533 UnderlyingPair: dia.Pair{BaseToken: basetoken, QuoteToken: quotetoken}, 534 } 535 } 536 537 func (scraper *BancorScraper) readPools() ([]BancorPool, error) { 538 var bpools BancorPools 539 pairs, _, err := utils.GetRequest("https://api-v2.bancor.network/pools") 540 if err != nil { 541 return nil, err 542 } 543 err = json.Unmarshal(pairs, &bpools) 544 if err != nil { 545 log.Error("Error reading json", err) 546 547 } 548 return bpools.Data, nil 549 550 } 551 552 func (scraper *BancorScraper) ScrapePair(pair dia.ExchangePair) (PairScraper, error) { 553 scraper.errorLock.RLock() 554 defer scraper.errorLock.RUnlock() 555 556 if scraper.error != nil { 557 return nil, scraper.error 558 } 559 560 if scraper.closed { 561 return nil, errors.New("BancorScraper is closed") 562 } 563 564 pairScraper := &BancorPairScraper{ 565 parent: scraper, 566 pair: pair, 567 } 568 569 scraper.pairScrapers[pair.ForeignName] = pairScraper 570 571 return pairScraper, nil 572 } 573 func (scraper *BancorScraper) cleanup(err error) { 574 scraper.errorLock.Lock() 575 defer scraper.errorLock.Unlock() 576 if err != nil { 577 scraper.error = err 578 } 579 scraper.closed = true 580 close(scraper.shutdownDone) 581 } 582 583 func (scraper *BancorScraper) Close() error { 584 // close the pair scraper channels 585 scraper.run = false 586 for _, pairScraper := range scraper.pairScrapers { 587 pairScraper.closed = true 588 } 589 590 close(scraper.shutdown) 591 <-scraper.shutdownDone 592 return nil 593 } 594 595 type BancorPairScraper struct { 596 parent *BancorScraper 597 pair dia.ExchangePair 598 closed bool 599 } 600 601 func (pairScraper *BancorPairScraper) Pair() dia.ExchangePair { 602 return pairScraper.pair 603 } 604 605 func (scraper *BancorScraper) Channel() chan *dia.Trade { 606 return scraper.chanTrades 607 } 608 609 func (pairScraper *BancorPairScraper) Error() error { 610 s := pairScraper.parent 611 s.errorLock.RLock() 612 defer s.errorLock.RUnlock() 613 return s.error 614 } 615 616 func (pairScraper *BancorPairScraper) Close() error { 617 pairScraper.parent.errorLock.RLock() 618 defer pairScraper.parent.errorLock.RUnlock() 619 pairScraper.closed = true 620 return nil 621 }