Skip to content

Commit

Permalink
fix bitflow asset collector and start extension of liquidity scraper.
Browse files Browse the repository at this point in the history
  • Loading branch information
jppade committed Jan 14, 2025
1 parent 48d7472 commit 99fc05e
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 78 deletions.
49 changes: 41 additions & 8 deletions pkg/dia/helpers/bitflowhelper/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,48 @@ import (
"github.com/sirupsen/logrus"
)

const DeployerAddress = "SPQC38PW542EQJ5M11CR25P7BS1CA6QT4TBXGB3M"
var StableSwapContracts = [...]SwapContract{
{
DeployerAddress: "SPQC38PW542EQJ5M11CR25P7BS1CA6QT4TBXGB3M",
ContractRegistry: "stableswap-stx-ststx-v-1-2",
ContractType: 0,
},
{
DeployerAddress: "SPQC38PW542EQJ5M11CR25P7BS1CA6QT4TBXGB3M",
ContractRegistry: "stableswap-usda-susdt-v-1-2",
ContractType: 0,
},
{
DeployerAddress: "SPQC38PW542EQJ5M11CR25P7BS1CA6QT4TBXGB3M",
ContractRegistry: "stableswap-aeusdc-susdt-v-1-2",
ContractType: 0,
},
{
DeployerAddress: "SPQC38PW542EQJ5M11CR25P7BS1CA6QT4TBXGB3M",
ContractRegistry: "stableswap-usda-aeusdc-v-1-2",
ContractType: 0,
},
{
DeployerAddress: "SPQC38PW542EQJ5M11CR25P7BS1CA6QT4TBXGB3M",
ContractRegistry: "stableswap-usda-aeusdc-v-1-4",
ContractType: 0,
},
{
DeployerAddress: "SPQC38PW542EQJ5M11CR25P7BS1CA6QT4TBXGB3M",
ContractRegistry: "stableswap-abtc-xbtc-v-1-2",
ContractType: 0,
},
{
DeployerAddress: "SM1793C4R5PZ4NS4VQ4WMP7SKKYVH8JZEWSZ9HCCR",
ContractRegistry: "xyk-pool-sbtc-stx-v-1-1",
ContractType: 1,
},
}

var StableSwapContracts = [...]string{
"stableswap-stx-ststx-v-1-2",
"stableswap-usda-susdt-v-1-2",
"stableswap-aeusdc-susdt-v-1-2",
"stableswap-usda-aeusdc-v-1-2",
"stableswap-usda-aeusdc-v-1-4",
"stableswap-abtc-xbtc-v-1-2",
type SwapContract struct {
DeployerAddress string
ContractRegistry string
ContractType int
}

type BitflowClient struct {
Expand Down
15 changes: 11 additions & 4 deletions pkg/dia/helpers/bitflowhelper/model.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
package bitflowhelper

type TokenMetadata struct {
TokenID string `json:"token-id"`
Name string `json:"name"`
Symbol string `json:"symbol"`
type WrapToken struct {
TokenDecimals uint `json:"tokenDecimals"`
TokenContract string `json:"tokenContract"`
Name string `json:"tokenName"`
}

type TokenMetadata struct {
TokenID string `json:"token-id"`
Name string `json:"name"`
Symbol string `json:"symbol"`
TokenDecimals uint `json:"tokenDecimals"`
TokenContract string `json:"tokenContract"`
WrapTokens map[string]WrapToken `json:"wrapTokens"`
}

type GetAllTokensResponse struct {
Expand Down
4 changes: 2 additions & 2 deletions pkg/dia/scraper/exchange-scrapers/BitflowScraper.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ func NewBitflowScraper(exchange dia.Exchange, scrape bool, relDB *models.RelDB)

swapContracts := make(map[string]nothing, len(bitflowhelper.StableSwapContracts))

for _, contractName := range bitflowhelper.StableSwapContracts {
contractId := fmt.Sprintf("%s.%s", bitflowhelper.DeployerAddress, contractName)
for _, contract := range bitflowhelper.StableSwapContracts {
contractId := fmt.Sprintf("%s.%s", contract.DeployerAddress, contract.ContractRegistry)
swapContracts[contractId] = nothing{}
}

Expand Down
186 changes: 122 additions & 64 deletions pkg/dia/scraper/liquidity-scrapers/BitflowScraper.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package liquidityscrapers
import (
"context"
"encoding/hex"
"errors"
"fmt"
"strings"
"time"
Expand Down Expand Up @@ -75,16 +76,17 @@ func NewBitflowLiquidityScraper(exchange dia.Exchange, relDB *models.RelDB, data
}

func (s *BitflowLiquidityScraper) fetchPools() {
poolTxs := make([]stackshelper.Transaction, 0)

swapContracts := bitflowhelper.StableSwapContracts[:]
if s.targetSwapContract != "" {
swapContracts = []string{s.targetSwapContract}
swapContracts = []bitflowhelper.SwapContract{
{ContractRegistry: s.targetSwapContract},
}
}

for _, contractName := range swapContracts {
s.logger.Infof("Fetching pools of %s", contractName)
contractId := fmt.Sprintf("%s.%s", bitflowhelper.DeployerAddress, contractName)
for _, contract := range swapContracts {
s.logger.Infof("Fetching pools of %s", contract.ContractRegistry)
contractId := fmt.Sprintf("%s.%s", contract.DeployerAddress, contract.ContractRegistry)

total := stackshelper.MaxPageLimit

Expand All @@ -96,98 +98,148 @@ func (s *BitflowLiquidityScraper) fetchPools() {
}

total = resp.Total
filtered := s.fetchPoolTransactions(resp.Results)
poolTxs = append(poolTxs, filtered...)
filtered := s.fetchPoolTransactions(resp.Results, contract.ContractType)
for _, tx := range filtered {
pool, err := s.parseTx(tx, contract.ContractType)
if err != nil {
continue
}
// s.logger.WithField("pool", pool).Info("sending pool to poolChannel")
s.poolChannel <- pool
}
}
}

for _, tx := range poolTxs {
args := make(map[string]stackshelper.FunctionArg, len(tx.ContractCall.FunctionArgs))
for _, item := range tx.ContractCall.FunctionArgs {
args[item.Name] = item
}
s.doneChannel <- true
}

tokens := [...]string{"", args["y-token"].Repr[1:]}
func (s *BitflowLiquidityScraper) parseTx(tx stackshelper.Transaction, contractType int) (dia.Pool, error) {
args := make(map[string]stackshelper.FunctionArg, len(tx.ContractCall.FunctionArgs))
for _, item := range tx.ContractCall.FunctionArgs {
args[item.Name] = item
}

var tokens [2]string
if contractType == 0 {
tokens = [...]string{"", args["y-token"].Repr[1:]}
if xToken, ok := args["x-token"]; ok {
tokens[0] = xToken.Repr[1:]
}
} else if contractType == 1 {
tokens = [...]string{"", args["y-token-trait"].Repr[1:]}
if xToken, ok := args["x-token-trait"]; ok {
tokens[0] = xToken.Repr[1:]
}
}

dbAssets := make([]dia.Asset, 0, len(tokens))

for _, address := range tokens {
// Workaround to fetch the native STX token data from DB
key := address
if address == "" {
key = "0x0000000000000000000000000000000000000000"
}
dbAssets := make([]dia.Asset, 0, len(tokens))

assset, err := s.relDB.GetAsset(key, s.blockchain)
if err != nil {
s.logger.WithError(err).Errorf("failed to GetAsset with key: %s", key)
continue
}
dbAssets = append(dbAssets, assset)
for _, address := range tokens {
// Workaround to fetch the native STX token data from DB
key := address
if address == "" {
key = "0x0000000000000000000000000000000000000000"
}

if len(dbAssets) != len(tokens) {
s.logger.Error("found less than 2 assets for the pool pair")
assset, err := s.relDB.GetAsset(key, s.blockchain)
if err != nil {
s.logger.WithError(err).Errorf("failed to GetAsset with key: %s", key)
continue
}
dbAssets = append(dbAssets, assset)
}

if len(dbAssets) != len(tokens) {
return dia.Pool{}, errors.New("found less than 2 assets for the pool pair")
}

balances, err := s.fetchPoolBalances(
var balances []float64
var err error
if contractType == 0 {
balances, err = s.fetchPoolBalances(
tx.ContractCall.ContractID,
args["x-token"].Hex,
args["y-token"].Hex,
args["lp-token"].Hex,
contractType,
)
} else if contractType == 1 {
balances, err = s.fetchPoolBalances(
tx.ContractCall.ContractID,
args["x-token-trait"].Hex,
args["y-token-trait"].Hex,
args["pool-trait"].Hex,
contractType,
)
}
if err != nil {
return dia.Pool{}, errors.New("failed to fetch bitflow pool balances")
}

if err != nil {
s.logger.WithError(err).Error("failed to fetch bitflow pool balances")
continue
}

assetVolumes := make([]dia.AssetVolume, len(balances))
assetVolumes := make([]dia.AssetVolume, len(balances))

for i, balance := range balances {
assetVolumes[i] = dia.AssetVolume{
Index: uint8(i),
Asset: dbAssets[i],
Volume: balance,
}
for i, balance := range balances {
assetVolumes[i] = dia.AssetVolume{
Index: uint8(i),
Asset: dbAssets[i],
Volume: balance,
}
}

pool := dia.Pool{
Exchange: dia.Exchange{Name: s.exchangeName},
Blockchain: dia.BlockChain{Name: s.blockchain},
Address: args["lp-token"].Repr[1:],
Time: time.Now(),
Assetvolumes: assetVolumes,
}
pool := dia.Pool{
Exchange: dia.Exchange{Name: s.exchangeName},
Blockchain: dia.BlockChain{Name: s.blockchain},
Time: time.Now(),
Assetvolumes: assetVolumes,
}

if pool.SufficientNativeBalance(GLOBAL_NATIVE_LIQUIDITY_THRESHOLD) {
s.datastore.GetPoolLiquiditiesUSD(&pool, priceCache)
}
// TO DO: This could go into fetchPoolBalances
if contractType == 0 {
pool.Address = args["lp-token"].Repr[1:]
} else if contractType == 1 {
pool.Address = args["pool-trait"].Repr[1:]
}

s.logger.WithField("pool", pool).Info("sending pool to poolChannel")
s.poolChannel <- pool
if pool.SufficientNativeBalance(GLOBAL_NATIVE_LIQUIDITY_THRESHOLD) {
s.datastore.GetPoolLiquiditiesUSD(&pool, priceCache)
}
return pool, nil

s.doneChannel <- true
}

func (s *BitflowLiquidityScraper) fetchPoolBalances(stableSwapContract, xToken, yToken, lpToken string) ([]float64, error) {
func (s *BitflowLiquidityScraper) fetchPoolBalances(stableSwapContract, xToken, yToken, lpToken string, contractType int) ([]float64, error) {

yTokenBytes, _ := hex.DecodeString(yToken[2:])
lpTokenBytes, _ := hex.DecodeString(lpToken[2:])
pairKey := stackshelper.CVTuple{"lp-token": lpTokenBytes, "y-token": yTokenBytes}

if xToken != "" {
xTokenBytes, _ := hex.DecodeString(xToken[2:])
pairKey["x-token"] = xTokenBytes
var pairKey stackshelper.CVTuple
if contractType == 0 {
pairKey = stackshelper.CVTuple{"lp-token": lpTokenBytes, "y-token": yTokenBytes}
} else if contractType == 1 {
pairKey = stackshelper.CVTuple{"pool-trait": lpTokenBytes, "y-token-trait": yTokenBytes}
}

if contractType == 0 {
if xToken != "" {
xTokenBytes, _ := hex.DecodeString(xToken[2:])
pairKey["x-token"] = xTokenBytes
}
} else if contractType == 1 {
if xToken != "" {
xTokenBytes, _ := hex.DecodeString(xToken[2:])
pairKey["x-token-trait"] = xTokenBytes
}
}

encodedKey := "0x" + hex.EncodeToString(stackshelper.SerializeCVTuple(pairKey))

entry, err := s.api.GetDataMapEntry(stableSwapContract, "PairsDataMap", encodedKey)
var entry []byte
var err error
if contractType == 0 {
entry, err = s.api.GetDataMapEntry(stableSwapContract, "PairsDataMap", encodedKey)
} else if contractType == 1 {
entry, err = s.api.GetDataMapEntry(stableSwapContract, "pools", encodedKey)
}
if err != nil {
s.logger.WithError(err).Error("failed to GetDataMapEntry")
return nil, err
Expand All @@ -212,12 +264,18 @@ func (s *BitflowLiquidityScraper) fetchPoolBalances(stableSwapContract, xToken,
return balances, nil
}

func (s *BitflowLiquidityScraper) fetchPoolTransactions(txs []stackshelper.AddressTransaction) []stackshelper.Transaction {
func (s *BitflowLiquidityScraper) fetchPoolTransactions(txs []stackshelper.AddressTransaction, poolType int) []stackshelper.Transaction {
poolTxs := make([]stackshelper.Transaction, 0)

for _, item := range txs {
isCreatePairCall := item.Tx.TxType == "contract_call" &&
item.Tx.ContractCall.FunctionName == "create-pair"
var isCreatePairCall bool
if poolType == 0 {
isCreatePairCall = item.Tx.TxType == "contract_call" &&
item.Tx.ContractCall.FunctionName == "create-pair"
} else if poolType == 1 {
isCreatePairCall = item.Tx.TxType == "contract_call" &&
item.Tx.ContractCall.FunctionName == "create-pool"
}

if isCreatePairCall && item.Tx.TxStatus == "success" {
// This is a temporary workaround introduced due to a bug in hiro stacks API.
Expand Down
8 changes: 8 additions & 0 deletions pkg/dia/service/assetservice/source/bitflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ func (s *BitflowAssetSource) fetchAssets() {
Decimals: uint8(token.TokenDecimals),
Blockchain: s.blockchain,
}
for _, wrappedToken := range token.WrapTokens {
s.assetChannel <- dia.Asset{
Address: wrappedToken.TokenContract,
Name: wrappedToken.Name,
Decimals: uint8(wrappedToken.TokenDecimals),
Blockchain: s.blockchain,
}
}
}

s.doneChannel <- true
Expand Down

0 comments on commit 99fc05e

Please sign in to comment.