与Go购买碎片数字
#go #webscraping #区块链 #ton

本文是我喜欢做的两件事的组合。第一个是我在古巴时开始做的事情。我曾经是世界网站的scrape,主要是在Python。使用诸如Beautiful SoupRequests之类的库,我曾经获取大量数据。我真的很喜欢这样做,这已经有一段时间了,因为我还没有这样做,所以现在该再次练习了。另一方面,最近我开始了解有关区块链的更多信息。现在我已经失业了,我有空闲时间去做,这真是太棒了。具体来说,我一直专注于TON(Telegram Open Network)区块链。

今天,我将与您分享一种刮擦Fragment Marketplace的方式,寻找最便宜的Anonymous Numbers并至少购买其中一些。从刮擦中,我们将获得有关数字的信息,我们将购买与此NFT相关的智能合约进行交互。该智能合约的来源是公开的,可以找到here。然后走吧。

源代码

该脚本的源代码可以在Gealber/blog_recipes/fragment_buyer

中找到

目标

我用脚本的目标

  1. 编写一个脚本,让我可以以最便宜的价格购买Anonymous Numbers

要求

这只需要Golang。

流动

流程将很简单

sketch flow

代码

让我们首先看看如何从碎片市场中检索电话号码。为此,我们将使用go-colly

文件:utils/fragment.go

package utils

import (
    "fmt"
    "strconv"
    "strings"

    "github.com/gocolly/colly"
)

func ScrapeFragmentMarket() ([]AnonNumbers, error) {
    numbers := make([]AnonNumbers, 0)
    c := colly.NewCollector()

    c.OnHTML("tr td.thin-last-col a", func(e *colly.HTMLElement) {
        name := strings.Trim(e.Attr("href"), "/number/")
        valueStr := e.ChildText("div.table-cell-value.tm-value.icon-before.icon-ton")
        value, err := strconv.Atoi(valueStr)
        if err != nil {
            return
        }       

        numbers = append(numbers, AnonNumbers{Name: name, Value: value})
    })

    c.OnRequest(func(r *colly.Request) {
        fmt.Println("Visiting", r.URL)
    })

    err := c.Visit("https://fragment.com/numbers?sort=price_asc&filter=sale")
    if err != nil {
        return nil, err
    }

    return numbers, nil
}

现在我拥有数字或令牌名称,我将需要以某种方式知道其地址。现在是我们对Telemint smart contract的了解的时候。阅读此智能合约,您可以注意到有一种方法可用于要求本合同中给定的NFT项目的地址。该方法称为get_nft_address_by_index,此方法是一种getter方法,因此可以在链接中要求,而无需成本。我将需要计算item_index,不幸的是,我们没有这样的方法。但是,阅读合同您可以注意到,该item_index计算为token_namehash,并且我们确实拥有它,所以这很容易。

文件:utils/address.go


package utils

import (
    "crypto/sha256"
    "math/big"

    // other imports as well …
)

// … other piece of code goes here

func itemIndex(tokenName string) *big.Int {
    h := sha256.New()
    h.Write([]byte(tokenName))
    hash := h.Sum(nil)
    index := new(big.Int)
    index.SetBytes(hash)

    return index
}

获取地址可以通过这种方式完成


package utils

import (
    "context"
    "crypto/sha256"
    "fmt"
    "math/big"
    "time"

    "github.com/xssnick/tonutils-go/address"
    "github.com/xssnick/tonutils-go/ton"
)

const (
    MainnetConfig = "https://ton-blockchain.github.io/global.config.json"
    MaxRetries    = 3
    Timeout       = 50 * time.Millisecond
)

var (
    AnonymouAddress = address.MustParseAddr("EQAOQdwdw8kGftJCSFgOErM1mBjYPe4DBPq8-AhF6vr9si5N")
)

func GetAnonymousNumberAddress(ctx context.Context, api *ton.APIClient, tokenName string) (string, error) {
    index := itemIndex(tokenName)

    nftAddr, err := numberAddress(ctx, api, index)
    if err != nil {
        return "", err
    }

    return nftAddr, nil
}

func itemIndex(tokenName string) *big.Int {
    h := sha256.New()
    h.Write([]byte(tokenName))
    hash := h.Sum(nil)
    index := new(big.Int)
    index.SetBytes(hash)

    return index
}

func numberAddress(ctx context.Context, api *ton.APIClient, itemIndex *big.Int) (string, error) {
    tries := 0
START:
    block, err := api.CurrentMasterchainInfo(ctx)
    if err != nil {
        return "", fmt.Errorf("CurrentMasterchainInfo::%v\n", err)
    }
    tries++

    res, err := api.RunGetMethod(ctx, block, AnonymouAddress, "get_nft_address_by_index", itemIndex)
    if err != nil {
        if lsErr, ok := err.(ton.LSError); ok && lsErr.Code == 651 && tries <= MaxRetries {
            fmt.Println("Sleeping ", Timeout)
            time.Sleep(Timeout)
            goto START
        }

        return "", fmt.Errorf("RunGetMethod::%v\n", err)
    }

    cs := res.MustSlice(0)
    addr := cs.MustLoadAddr()   

    return addr.String(), nil
}

鉴于tonutils-go实现了连接池,因此可能尚未在一个节点上应用一些块,因此可能导致错误。为了解决这个问题,我设置了三次最大策略。一种更好的方法是将您与一个节点保持联系,但这需要您进行一吨节点运行,这对我们来说太昂贵了。

注意:如果您要使用此脚本

,我很差可以随意捐赠给此地址

EQC9n6aFb2oxQPMTPrHOnZDFcvvC2YLYIgBUms2yAB_LcAtv

停止索要钱兄弟,没有人会捐出狗屎。

好吧,总而言之,我们现在在区块链中拥有项目地址,因此我们现在可以与智能合约进行互动以购买此NFT。

package utils

import (
    "context"
    "fmt"
    "log"
    "strconv"
    "time"

    "github.com/xssnick/tonutils-go/address"
    "github.com/xssnick/tonutils-go/tlb"
    "github.com/xssnick/tonutils-go/ton"
    "github.com/xssnick/tonutils-go/ton/wallet"
    "github.com/xssnick/tonutils-go/tvm/cell"
)

const (
    // another declaration goes here…
    nanoTons     int64 = 1e9
)

func buyNumber(
    ctx context.Context,
    w *wallet.Wallet,
    tokenAddress string,
    value int,
) error {
    nftAddr, err := address.ParseAddr(tokenAddress)
    if err != nil {
        return err
    }

    transfer := wallet.SimpleMessage(nftAddr, tlb.MustFromTON(strconv.Itoa(value)), nil)

    return w.Send(ctx, transfer, true)
}

// code related to auction of numbers goes here…

func BuyNumbers(
    ctx context.Context,
    w *wallet.Wallet,
    api *ton.APIClient,
    numbers []AnonNumbers,
) error {
    // check available balance
    block, err := api.CurrentMasterchainInfo(ctx)
    if err != nil {
        return fmt.Errorf("CurrentMasterchainInfo::%v\n", err)
    }

    balanceCoins, err := w.GetBalance(ctx, block)
    if err != nil {
        return fmt.Errorf("GetBalance::%v+\n", err)
    }

    balanceStr := balanceCoins.TON()
    balance, err := strconv.ParseFloat(balanceStr, 64)
    if err != nil {
        return err
    }

    // check if we have enough balance
    if enoughBudget(numbers, int(balance)) {
        for _, number := range numbers {
            if !number.OnSell {
                err = buyNumber(ctx, w, number.Address, number.Value)
                if err != nil {
                    return err
                }

                // add this number into db maybe
                log.Printf("Number %s bought\n", number.Name)
            }
        }
    }

    return nil
}

// more code go here…

几乎完成了,为了查看整个代码,我建议您克隆此repo。与往常一样,我不会将整个代码放在这里,因为它没有意义,作为开发人员,您应该习惯阅读其他人的代码。因此,请随时挖掘该存储库。

结论

这两种工具的组合可能会很棒。自己尝试一下,请随时与我联系。再见。