本文是我喜欢做的两件事的组合。第一个是我在古巴时开始做的事情。我曾经是世界网站的scrape,主要是在Python。使用诸如Beautiful Soup和Requests之类的库,我曾经获取大量数据。我真的很喜欢这样做,这已经有一段时间了,因为我还没有这样做,所以现在该再次练习了。另一方面,最近我开始了解有关区块链的更多信息。现在我已经失业了,我有空闲时间去做,这真是太棒了。具体来说,我一直专注于TON(Telegram Open Network)区块链。
今天,我将与您分享一种刮擦Fragment Marketplace的方式,寻找最便宜的Anonymous Numbers并至少购买其中一些。从刮擦中,我们将获得有关数字的信息,我们将购买与此NFT相关的智能合约进行交互。该智能合约的来源是公开的,可以找到here。然后走吧。
源代码
该脚本的源代码可以在Gealber/blog_recipes/fragment_buyer
中找到目标
我用脚本的目标
- 编写一个脚本,让我可以以最便宜的价格购买Anonymous Numbers。
要求
这只需要Golang。
流动
流程将很简单
代码
让我们首先看看如何从碎片市场中检索电话号码。为此,我们将使用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_name的hash,并且我们确实拥有它,所以这很容易。
文件: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。与往常一样,我不会将整个代码放在这里,因为它没有意义,作为开发人员,您应该习惯阅读其他人的代码。因此,请随时挖掘该存储库。
结论
这两种工具的组合可能会很棒。自己尝试一下,请随时与我联系。再见。