Skip to content

Commit

Permalink
Merge branch 'bitflow-scraper-update'
Browse files Browse the repository at this point in the history
  • Loading branch information
jppade committed Jan 21, 2025
2 parents 8de3458 + 3e7788b commit 6bf366f
Show file tree
Hide file tree
Showing 9 changed files with 532 additions and 105 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-core-v-1-2",
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
120 changes: 120 additions & 0 deletions pkg/dia/helpers/stackshelper/c32.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package stackshelper

import (
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"regexp"
"strings"
)

const c32table = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
const hextable = "0123456789abcdef"

func c32address(version int, hash160hex string) (string, error) {
match, err := regexp.MatchString("^[0-9a-fA-F]{40}$", hash160hex)
if err != nil {
return "", err
}
if !match {
return "", errors.New("not a hash160 hex string")
}

c32str, err := c32checkEncode(version, hash160hex)
if err != nil {
return "", err
}
return "S" + c32str, nil
}

func c32checkEncode(version int, data string) (string, error) {
if version < 0 || version >= 32 {
return "", errors.New("invalid version (must be between 0 and 31)")
}

data = strings.ToLower(data)
if len(data)%2 != 0 {
data = "0" + data
}
versionHex := fmt.Sprintf("%02x", version)

checksum, err := c32checksum(versionHex + data)
if err != nil {
return "", err
}

c32str, err := c32encode(data + checksum)
if err != nil {
return "", err
}
return string(c32table[version]) + c32str, nil
}

func c32checksum(dataHex string) (string, error) {
bytes, err := hex.DecodeString(dataHex)
if err != nil {
return "", err
}

inner := sha256.Sum256(bytes)
checksum := sha256.Sum256(inner[:])
return hex.EncodeToString(checksum[0:4]), nil
}

func c32encode(inputHex string) (string, error) {
if len(inputHex)%2 != 0 {
inputHex = "0" + inputHex
}
inputHex = strings.ToLower(inputHex)

inputBytes, err := hex.DecodeString(inputHex)
if err != nil {
return "", err
}

res := make([]byte, 0)
carry := 0

for i := len(inputHex) - 1; i >= 0; i-- {
if carry < 4 {
currentCode := strings.IndexByte(hextable, inputHex[i]) >> carry
nextCode := 0
if i != 0 {
nextCode = strings.IndexByte(hextable, inputHex[i-1])
}

nextBits := 1 + carry
nextLowBits := nextCode % (1 << nextBits) << (5 - nextBits)
curC32Digit := c32table[currentCode+nextLowBits]

res = append([]byte{curC32Digit}, res...)
carry = nextBits
} else {
carry = 0
}
}

c32LeadingZeros := 0
for _, d := range res {
if d != '0' {
break
} else {
c32LeadingZeros++
}
}
res = res[c32LeadingZeros:]

numLeadingZeroBytesInHex := 0
for _, b := range inputBytes {
if b != 0 {
break
}
numLeadingZeroBytesInHex++
}

for i := 0; i < numLeadingZeroBytesInHex; i++ {
res = append([]byte{c32table[0]}, res...)
}
return string(res), nil
}
41 changes: 40 additions & 1 deletion pkg/dia/helpers/stackshelper/clarity.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package stackshelper

import (
"encoding/binary"
"encoding/hex"
"errors"
"math/big"
"sort"
Expand All @@ -18,10 +19,13 @@ const (
boolFalseCV = 0x04
principalStandard = 0x05
principalContract = 0x06
responseOkCV = 0x07
responseErrCV = 0x08
optionNoneCV = 0x09
optionSomeCV = 0x0a
tupleCV = 0x0c
stringASCII = 0x0d
stringUTF8 = 0x0e
)

type CVTuple map[string][]byte
Expand All @@ -47,6 +51,35 @@ func DeserializeCVUint(src []byte) (*big.Int, error) {
return value, nil
}

func DeserializeCVPrincipal(src []byte) (string, error) {
switch src[0] {
case principalStandard:
version, hash160 := deserializeAddress(src[1:])
return c32address(version, hash160)
case principalContract:
version, hash160 := deserializeAddress(src[1:])
cAddress, err := c32address(version, hash160)
if err != nil {
return "", err
}
contractName, _ := deserializeLPString(src[clarityPrincipalByteSize+2:])
return cAddress + "." + contractName, nil
default:
return "", errors.New("value is not a CV principal")
}
}

func DeserializeCVResponse(src []byte) ([]byte, bool) {
switch src[0] {
case responseOkCV:
return src[1:], true
case responseErrCV:
return src[1:], false
default:
return nil, false
}
}

// SerializeCVTuple converts a clarity value tuple into its binary representation
// that can be used to call stacks smart contract functions.
func SerializeCVTuple(tuple CVTuple) []byte {
Expand Down Expand Up @@ -112,7 +145,7 @@ func DeserializeCVTuple(src []byte) (CVTuple, error) {
entrySize := len(serializeLPString(k)) + len(v)
valueSize += entrySize
}
case stringASCII:
case stringASCII, stringUTF8:
size := readClarityTypeSize(src[offset+1:])
valueSize += 4 + int(size)
default:
Expand Down Expand Up @@ -147,6 +180,12 @@ func deserializeLPString(val []byte) (string, int) {
return string(val[1 : size+1]), size
}

func deserializeAddress(src []byte) (int, string) {
version := int(src[0])
hash160 := hex.EncodeToString(src[1 : clarityPrincipalByteSize+1])
return version, hash160
}

func readClarityTypeSize(src []byte) uint32 {
return binary.BigEndian.Uint32(src[0:4])
}
54 changes: 44 additions & 10 deletions pkg/dia/helpers/stackshelper/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func (c *StacksClient) GetLatestBlock() (Block, error) {
var block Block

url := fmt.Sprintf("%s/extended/v2/blocks/latest", StacksURL)
req, _ := http.NewRequest("GET", url, http.NoBody)
req, _ := http.NewRequest(http.MethodGet, url, http.NoBody)

err := c.callStacksAPI(req, &block)
if err != nil {
Expand All @@ -76,7 +76,7 @@ func (c *StacksClient) GetTransactionAt(txID string) (Transaction, error) {
var transaction Transaction

url := fmt.Sprintf("%s/extended/v1/tx/%s", StacksURL, txID)
req, _ := http.NewRequest("GET", url, http.NoBody)
req, _ := http.NewRequest(http.MethodGet, url, http.NoBody)

err := c.callStacksAPI(req, &transaction)
if err != nil {
Expand All @@ -95,7 +95,7 @@ func (c *StacksClient) GetAllBlockTransactions(height int) ([]Transaction, error

for offset := 0; offset < total; offset += MaxPageLimit {
url := fmt.Sprintf("%s?limit=%d&offset=%d", baseURL, MaxPageLimit, offset)
req, _ := http.NewRequest("GET", url, http.NoBody)
req, _ := http.NewRequest(http.MethodGet, url, http.NoBody)

err := c.callStacksAPI(req, &resp)
if err != nil {
Expand Down Expand Up @@ -123,26 +123,25 @@ func (c *StacksClient) GetAddressTransactions(address string, limit, offset int)
offset,
)

req, _ := http.NewRequest("GET", url, http.NoBody)
req, _ := http.NewRequest(http.MethodGet, url, http.NoBody)
err := c.callStacksAPI(req, &resp)
if err != nil {
return resp, err
}
return resp, nil
}

func (c *StacksClient) GetDataMapEntry(contractId, mapName, key string) ([]byte, error) {
address := strings.Split(contractId, ".")
func (c *StacksClient) GetDataMapEntry(contractID, mapName, key string) ([]byte, error) {
address := strings.Split(contractID, ".")

url := fmt.Sprintf("%s/v2/map_entry/%s/%s/%s", StacksURL, address[0], address[1], mapName)
body := []byte(fmt.Sprintf(`"%s"`, key))

req, _ := http.NewRequest("POST", url, bytes.NewBuffer(body))
req, _ := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")

var entry DataMapEntry
err := c.callStacksAPI(req, &entry)
if err != nil {
var entry ContractValue
if err := c.callStacksAPI(req, &entry); err != nil {
return nil, err
}

Expand All @@ -160,6 +159,41 @@ func (c *StacksClient) GetDataMapEntry(contractId, mapName, key string) ([]byte,
return result, nil
}

func (c *StacksClient) GetDataVar(contractAddress, contractName, dataVar string) ([]byte, error) {
url := fmt.Sprintf("%s/v2/data_var/%s/%s/%s", StacksURL, contractAddress, contractName, dataVar)
req, _ := http.NewRequest(http.MethodGet, url, http.NoBody)

var result ContractValue
if err := c.callStacksAPI(req, &result); err != nil {
return nil, err
}
return hex.DecodeString(result.Data[2:])
}

func (c *StacksClient) CallContractFunction(contractAddress, contractName, functionName string, args ContractCallArgs) ([]byte, error) {
url := fmt.Sprintf("%s/v2/contracts/call-read/%s/%s/%s", StacksURL, contractAddress, contractName, functionName)
body, err := json.Marshal(args)
if err != nil {
return nil, err
}

req, _ := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")

var resp ContractCallResult
if err := c.callStacksAPI(req, &resp); err != nil {
return nil, err
}

if !resp.Okay {
err = errors.New(resp.Cause)
c.logger.WithError(err).Error("failed to call a read-only function")
return nil, err
}

return hex.DecodeString(resp.Result[2:])
}

func (c *StacksClient) callStacksAPI(request *http.Request, target interface{}) error {
if len(c.apiKey) > 0 {
request.Header.Add("X-API-Key", c.apiKey)
Expand Down
Loading

0 comments on commit 6bf366f

Please sign in to comment.