github.com/diadata-org/diadata@v1.4.593/pkg/dia/scraper/liquidity-scrapers/PlatypusScraper.go (about) 1 package liquidityscrapers 2 3 import ( 4 "math" 5 "math/big" 6 "strings" 7 "time" 8 9 "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/platypusfinance" 10 platypusAssetABI "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/platypusfinance/asset" 11 platypusPoolABI "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/platypusfinance/pool" 12 platypusTokenABI "github.com/diadata-org/diadata/pkg/dia/scraper/exchange-scrapers/platypusfinance/token" 13 models "github.com/diadata-org/diadata/pkg/model" 14 "github.com/diadata-org/diadata/pkg/utils" 15 16 "github.com/diadata-org/diadata/pkg/dia" 17 "github.com/ethereum/go-ethereum/accounts/abi/bind" 18 "github.com/ethereum/go-ethereum/common" 19 "github.com/ethereum/go-ethereum/ethclient" 20 ) 21 22 const ( 23 platypusRestDialEth = "https://api.avax.network/ext/bc/C/rpc" 24 platypusMasterRegV3Addr = "0x7125B4211357d7C3a90F796c956c12c681146EbB" 25 platypusMasterRegV2Addr = "0x68c5f4374228BEEdFa078e77b5ed93C28a2f713E" 26 platypusMasterRegV1Addr = "0xB0523f9F473812FB195Ee49BC7d2ab9873a98044" 27 ) 28 29 type platypusRegistry struct { 30 Address common.Address 31 Version int 32 } 33 34 type PlatypusCoin struct { 35 Symbol string 36 Decimals uint8 37 Address string 38 Name string 39 } 40 41 // The scraper object for Platypus Finance 42 type PlatypusScraper struct { 43 RestClient *ethclient.Client 44 datastore *models.DB 45 poolChannel chan dia.Pool 46 doneChannel chan bool 47 blockchain string 48 exchangeName string 49 50 platypusCoins map[string]*PlatypusCoin 51 52 basePoolRegistry platypusRegistry 53 } 54 55 // Returns a new exchange scraper 56 func NewPlatypusScraper(exchange dia.Exchange, datastore *models.DB) *PlatypusScraper { 57 58 var ( 59 restClient *ethclient.Client 60 err error 61 poolChannel = make(chan dia.Pool) 62 doneChannel = make(chan bool) 63 scraper *PlatypusScraper 64 ) 65 registries := []platypusRegistry{ 66 {Version: 3, Address: common.HexToAddress(platypusMasterRegV3Addr)}, 67 {Version: 2, Address: common.HexToAddress(platypusMasterRegV2Addr)}, 68 {Version: 1, Address: common.HexToAddress(platypusMasterRegV1Addr)}, 69 } 70 71 log.Infof("init rest and ws client for %s", exchange.BlockChain.Name) 72 restClient, err = ethclient.Dial(utils.Getenv(strings.ToUpper(exchange.BlockChain.Name)+"_URI_REST", platypusRestDialEth)) 73 if err != nil { 74 log.Fatal("init rest client: ", err) 75 } 76 77 scraper = &PlatypusScraper{ 78 blockchain: exchange.BlockChain.Name, 79 exchangeName: exchange.Name, 80 RestClient: restClient, 81 datastore: datastore, 82 poolChannel: poolChannel, 83 doneChannel: doneChannel, 84 85 platypusCoins: make(map[string]*PlatypusCoin), 86 } 87 88 // // Load metadata from master registries 89 // for _, registry := range registries { 90 // err := scraper.loadPoolsAndCoins(registry) 91 // if err != nil { 92 // log.Errorf("loadPoolsAndCoins error w %s registry (v%d): %s", registry.Address.Hex(), registry.Version, err) 93 // } 94 // log.Infof("metadata loaded, now scraper have %d pools data and %d coins", len(scraper.pools.pools), len(scraper.platypusCoins)) 95 // } 96 97 scraper.basePoolRegistry = platypusRegistry{Version: 3, Address: common.HexToAddress(platypusMasterRegV3Addr)} 98 99 go func() { 100 for _, registry := range registries { 101 scraper.fetchPools(registry) 102 } 103 scraper.doneChannel <- true 104 }() 105 106 return scraper 107 } 108 109 // Load pools and coins metadata from master registry 110 func (scraper *PlatypusScraper) fetchPools(registry platypusRegistry) (err error) { 111 log.Infof("loading master contract %s version %d and querying registry", registry.Address.Hex(), registry.Version) 112 contractMaster, err := platypusfinance.NewBaseMasterPlatypusCaller(registry.Address, scraper.RestClient) 113 if err != nil { 114 log.Error("NewBaseMasterPlatypusCaller: ", err) 115 return err 116 } 117 118 poolCount, err := contractMaster.PoolLength(&bind.CallOpts{}) 119 if err != nil { 120 log.Error("PoolLength: ", err) 121 return err 122 } 123 124 for i := 0; i < int(poolCount.Int64()); i++ { 125 asset, errPoolInfo := contractMaster.PoolInfo(&bind.CallOpts{}, big.NewInt(int64(i))) 126 if errPoolInfo != nil { 127 log.Error("PoolInfo: ", errPoolInfo) 128 return errPoolInfo 129 } 130 131 pool := scraper.loadPoolData(asset.LpToken.Hex()) 132 scraper.Pool() <- pool 133 } 134 135 return err 136 } 137 138 func (scraper *PlatypusScraper) loadPoolData(poolAddress string) (pool dia.Pool) { 139 var err error 140 141 contractAsset, err := platypusAssetABI.NewAssetCaller(common.HexToAddress(poolAddress), scraper.RestClient) 142 if err != nil { 143 log.Error("NewAssetCaller: ", err) 144 } 145 146 poolContract, err := contractAsset.Pool(&bind.CallOpts{}) 147 if err != nil { 148 log.Error("Pool: ", err) 149 } 150 151 poolCaller, err := platypusPoolABI.NewPoolCaller(poolContract, scraper.RestClient) 152 if err != nil { 153 log.Error("NewPoolCaller: ", err) 154 } 155 156 poolTokenAddresses, errGetTokens := poolCaller.GetTokenAddresses(&bind.CallOpts{}) 157 if errGetTokens != nil { 158 symbol, err := contractAsset.Symbol(&bind.CallOpts{}) 159 if err != nil { 160 log.Error("contractAsset.Symbol: ", err) 161 } 162 log.Warnf("error calling GetTokenAddresses for %s %s asset: %s", symbol, poolAddress, errGetTokens) 163 } 164 165 for _, c := range poolTokenAddresses { 166 var ( 167 symbol string 168 decimalsBig *big.Int 169 decimals uint8 170 balance *big.Int 171 name string 172 ) 173 174 if c == common.HexToAddress("0x0000000000000000000000000000000000000000") { 175 continue 176 } else { 177 contractToken, err := platypusTokenABI.NewTokenCaller(c, scraper.RestClient) 178 if err != nil { 179 log.Error("NewTokenCaller: ", err) 180 continue 181 } 182 183 symbol, err = contractToken.Symbol(&bind.CallOpts{}) 184 if err != nil { 185 log.Error("Symbol: ", err, c.Hex()) 186 continue 187 } 188 189 decimalsBig, err = contractToken.Decimals(&bind.CallOpts{}) 190 if err != nil { 191 log.Error("Decimals: ", err) 192 continue 193 } 194 decimals = uint8(decimalsBig.Uint64()) 195 196 name, err = contractToken.Name(&bind.CallOpts{}) 197 if err != nil { 198 log.Error("Name: ", err) 199 continue 200 } 201 202 balance, err = contractToken.BalanceOf(&bind.CallOpts{}, common.HexToAddress(poolAddress)) 203 if err != nil { 204 log.Error("get balance of: ", err) 205 } 206 207 } 208 209 poolAsset := dia.Asset{ 210 Address: c.Hex(), 211 Blockchain: scraper.blockchain, 212 Decimals: decimals, 213 Symbol: symbol, 214 Name: name, 215 } 216 log.Info("pool asset: ", poolAsset) 217 218 volume, _ := new(big.Float).Quo(big.NewFloat(0).SetInt(balance), new(big.Float).SetFloat64(math.Pow10(int(decimals)))).Float64() 219 pool.Assetvolumes = append(pool.Assetvolumes, dia.AssetVolume{ 220 Asset: poolAsset, 221 Volume: volume, 222 }) 223 224 } 225 226 // Determine USD liquidity. 227 if pool.SufficientNativeBalance(GLOBAL_NATIVE_LIQUIDITY_THRESHOLD) { 228 scraper.datastore.GetPoolLiquiditiesUSD(&pool, priceCache) 229 } 230 231 pool.Exchange = dia.Exchange{Name: scraper.exchangeName} 232 pool.Blockchain = dia.BlockChain{Name: scraper.blockchain} 233 pool.Address = poolAddress 234 pool.Time = time.Now() 235 236 return pool 237 } 238 239 func (scraper *PlatypusScraper) Pool() chan dia.Pool { 240 return scraper.poolChannel 241 } 242 243 func (scraper *PlatypusScraper) Done() chan bool { 244 return scraper.doneChannel 245 }