首頁  >  文章  >  後端開發  >  Solana 與 Go 一起閃爍

Solana 與 Go 一起閃爍

王林
王林原創
2024-08-24 06:37:44343瀏覽

Blinks 是元資料豐富的鏈接,代表並支援整個 Solana 生態系統的鏈上活動,而無需導航到不同的應用程式或網頁。

Blinks 支援 Solana Actions 啟用的廣泛活動
主要允許用戶透過社群媒體和其他鏈下平台與區塊鏈進行互動。

用例包括

  • NFT 交易與鑄造,
  • 捐款,
  • 眾籌,
  • 代幣交換,
  • 彩票/賭場應用程式等等

在本文中,我們將探索一個簡單的 Blink 應用程序,專注於使用 Go 鑄造 NFT。雖然本文重點關注 Go,但核心概念適用於任何 Blink 應用程式。您可以在 GitHub 上找到完整的程式碼。

我們將首先使用 Gin 框架設定一個基本的 Web 伺服器,以及規範定義的必要的 CORS 配置。我們還將定義一些端點,以下將詳細討論。

func main() {
    var (
        corsConfig = cors.DefaultConfig()
        router     = gin.Default()
        port       = os.Getenv("PORT")
    )

    corsConfig.AllowAllOrigins = true
    corsConfig.AddAllowHeaders([]string{"Content-Length", "Content-Type", "Access-Control-Allow-Origin"}...)
    corsConfig.AddAllowMethods([]string{"GET", "POST", "OPTIONS"}...)

    router.Use(cors.New(corsConfig))

    router.GET("/actions.json", app.ActionsRulesHandler)
    router.GET("/api/actions/mint_nft", app.GetActionsHandler)
    router.OPTIONS("/api/actions/mint_nft", app.OptionsHandler)
    router.POST("/api/actions/mint_nft", app.PostHandler)

    log.Println("StickyLabs Blink Active ?")

    if port == "" {
        port = "8081"
    }

    log.Println("Server is running")
    err := router.Run(fmt.Sprintf(":%v", port))
    if err != nil {
        log.Fatal(err)
        return
    }
}

任何 Blinks 應用程式的核心都在於複製 Solana Actions API 規範。以下是 Blinks 工作原理的直覺展示。

Solana Blinks with Go

動作處理程序

Solana 上的 Blinks 使用 Action URL 方案來提供元資料豐富的鏈接,從而實現各種鏈上活動。本節概述了負責處理 /api/actions/mint_nft

上的 mint NFT 操作的主要處理程序
  • GET Handler:傳回元資料、支援的操作和所需參數。
type ActionGetResponse struct {
    Title       string `json:"title"`
    Icon        string `json:"icon"`
    Description string `json:"description"`
    Label       string `json:"label"`
    Links       struct {
        Actions []Actions `json:"actions"`
    } `json:"links"`
}

type Actions struct {
    Label      string             `json:"label"`
    Href       string             `json:"href"`
    Parameters []ActionParameters `json:"parameters,omitempty"`
}

type ActionParameters struct {
    Name     string `json:"name"`
    Label    string `json:"label"`
    Required bool   `json:"required"`
}

func GetActionsHandler(c *gin.Context) {
    payload := ActionGetResponse{
        Title: "Actions Example - Mint NFT",
        Icon:        c.Request.URL.Scheme + "://" + c.Request.URL.Host + "/solana_devs.jpg",
        Description: "Transfer SOL to another Solana wallet",
        Label:       "Transfer",
    }
    payload.Links.Actions = []Actions{
        {"Mint NFT", "/api/actions/mint_nft", []ActionParameters{
            {"name", "Enter the Name of the NFT", true},
            {"symbol", "Enter the Symbol of the NFT", true},
            {"uri", "Enter the Uri of the NFT", true},
        }},
    }

    c.JSON(http.StatusOK, payload)
}
  • OPTIONS :OPTIONS 處理程序處理 CORS 要求,確保與瀏覽器和其他客戶端請求機制的相容性。
var ACTIONS_CORS_HEADERS = map[string]string{
    "Access-Control-Allow-Origin":  "*",
    "Access-Control-Allow-Methods": "GET,POST,OPTIONS",
    "Access-Control-Allow-Headers": "Content-Type",
}

func OptionsHandler(c *gin.Context) {
    for key, value := range ACTIONS_CORS_HEADERS {
        c.Header(key, value)
    }
    c.Status(http.StatusOK)
}
  • POST Handler :POST 處理程序接受查詢參數,解析以 JSON 形式提供的 Base58 帳戶,並傳回一個 Base64 編碼的序列化交易以及一條供使用者簽署和執行的訊息。
type MintNFTParams struct {
    Name   string `form:"name"   binding:"required"`
    Symbol string `form:"symbol" binding:"required"`
    URI    string `form:"uri"    binding:"required"`
}

// {  "account": "<account>" } //JSON
type ActionPostRequest struct {
    Account string `json:"account"`
}

type ActionPostResponse struct {
    Fields ActionPostResponseFields `json:"fields"`
}
type ActionPostResponseFields struct {
    Transaction string `json:"transaction"`
    Message     string `json:"message"`
}

func PostHandler(c *gin.Context) {
    var (
        qPayload MintNFTParams
        request  ActionPostRequest
        response ActionPostResponse
    )

    if err := c.ShouldBindQuery(&qPayload); err != nil {
        c.JSON(http.StatusBadRequest, ActionError{Message: "Invalid Query Params"})
        return
    }

    if err := c.ShouldBindJSON(&request); err != nil {
        log.Println(err)
        c.JSON(http.StatusBadRequest, ActionError{Message: "Invalid request"})
        return
    }

    account, err := types.AccountFromBase58(request.Account)
    if err != nil {
        log.Println(err)
        c.JSON(http.StatusBadRequest, ActionError{Message: "Invalid request; Error validating account"})
        return
    }
    response.Fields.Transaction, response.Fields.Message = mintNFT(qPayload, account)

    c.JSON(http.StatusOK, response)
}


  • 鑄造 NFT

mintNFT 函數利用 Solana-Go-SDK 來鑄造 NFT,只需進行少量調整。

func mintNFT(metadata MintNFTParams, feePayer types.Account) (transaction, message string) {

    message = fmt.Sprintf("Mint NFT %s", metadata.Name)

    c := client.NewClient(rpc.DevnetRPCEndpoint)
    log.Println(metadata)

    mint := types.NewAccount()
    fmt.Printf("NFT: %v\n", mint.PublicKey.ToBase58())

    collection := types.NewAccount()
    fmt.Printf("collection: %v\n", collection.PublicKey.ToBase58())

    ata, _, err := common.FindAssociatedTokenAddress(feePayer.PublicKey, mint.PublicKey)
    if err != nil {
        log.Fatalf("failed to find a valid ata, err: %v", err)
    }

    tokenMetadataPubkey, err := token_metadata.GetTokenMetaPubkey(mint.PublicKey)
    if err != nil {
        log.Fatalf("failed to find a valid token metadata, err: %v", err)

    }
    tokenMasterEditionPubkey, err := token_metadata.GetMasterEdition(mint.PublicKey)
    if err != nil {
        log.Fatalf("failed to find a valid master edition, err: %v", err)
    }

    mintAccountRent, err := c.GetMinimumBalanceForRentExemption(context.Background(), token.MintAccountSize)
    if err != nil {
        log.Fatalf("failed to get mint account rent, err: %v", err)
    }

    recentBlockhashResponse, err := c.GetLatestBlockhash(context.Background())
    if err != nil {
        log.Fatalf("failed to get recent blockhash, err: %v", err)
    }

    tx, err := types.NewTransaction(types.NewTransactionParam{
        Signers: []types.Account{mint, feePayer},
        Message: types.NewMessage(types.NewMessageParam{
            FeePayer:        feePayer.PublicKey,
            RecentBlockhash: recentBlockhashResponse.Blockhash,
            Instructions: []types.Instruction{
                system.CreateAccount(system.CreateAccountParam{
                    From:     feePayer.PublicKey,
                    New:      mint.PublicKey,
                    Owner:    common.TokenProgramID,
                    Lamports: mintAccountRent,
                    Space:    token.MintAccountSize,
                }),
                token.InitializeMint(token.InitializeMintParam{
                    Decimals:   0,
                    Mint:       mint.PublicKey,
                    MintAuth:   feePayer.PublicKey,
                    FreezeAuth: &feePayer.PublicKey,
                }),
                token_metadata.CreateMetadataAccountV3(token_metadata.CreateMetadataAccountV3Param{
                    Metadata:                tokenMetadataPubkey,
                    Mint:                    mint.PublicKey,
                    MintAuthority:           feePayer.PublicKey,
                    Payer:                   feePayer.PublicKey,
                    UpdateAuthority:         feePayer.PublicKey,
                    UpdateAuthorityIsSigner: true,
                    IsMutable:               true,
                    Data: token_metadata.DataV2{
                        Name:                 metadata.Name,
                        Symbol:               metadata.Symbol,
                        Uri:                  metadata.URI,
                        SellerFeeBasisPoints: 100,
                        Creators: &[]token_metadata.Creator{
                            // tODO rede && Minter
                            {
                                Address:  feePayer.PublicKey,
                                Verified: true,
                                Share:    100,
                            },
                        },
                        Collection: &token_metadata.Collection{
                            Verified: false,
                            Key:      collection.PublicKey,
                        },
                        Uses: nil,
                    },
                    CollectionDetails: nil,
                }),
                associated_token_account.Create(associated_token_account.CreateParam{
                    Funder:                 feePayer.PublicKey,
                    Owner:                  feePayer.PublicKey,
                    Mint:                   mint.PublicKey,
                    AssociatedTokenAccount: ata,
                }),
                token.MintTo(token.MintToParam{
                    Mint:   mint.PublicKey,
                    To:     ata,
                    Auth:   feePayer.PublicKey,
                    Amount: 1,
                }),
                token_metadata.CreateMasterEditionV3(token_metadata.CreateMasterEditionParam{
                    Edition:         tokenMasterEditionPubkey,
                    Mint:            mint.PublicKey,
                    UpdateAuthority: feePayer.PublicKey,
                    MintAuthority:   feePayer.PublicKey,
                    Metadata:        tokenMetadataPubkey,
                    Payer:           feePayer.PublicKey,
                    MaxSupply:       pointer.Get[uint64](0),
                }),
            },
        }),
    })
    if err != nil {
        log.Fatalf("failed to new a tx, err: %v", err)
    }

    serialized, err := tx.Serialize()
    if err != nil {
        log.Fatal(err)
    }

    transaction = base64.StdEncoding.EncodeToString(serialized)
    return
}

  • 錯誤處理:操作應以以下格式傳回使用者友善的錯誤。
// { "message" : "Insert Error Message" } //JSON
type ActionError struct {
    Message string `json:"message"`
}

  • actions.json:actions.json 檔案應儲存在網域的根目錄下。它向用戶端提供有關哪些 URL 支援 Solana 操作的說明,並提供可用於向 Blink 應用程式執行 GET 請求的對應。為簡單起見,我們將從 url 路徑傳回 JSON 回應
func ActionsRulesHandler(c *gin.Context) {
    payload := gin.H{
        "rules": []gin.H{
            {
                "pathPattern": "/*",
                "apiPath":     "/api/actions/*",
            },
            {
                "pathPattern": "/api/actions/**",
                "apiPath":     "/api/actions/**",
            },
        },
    }

    c.JSON(http.StatusOK, payload)
}

測試你的眨眼

部署應用程式後,您可以使用 Blinks Inspector 應用程式進行測試。

Solana Blinks with Go

結論

我希望本文提供有關使用 Go 在 Solana 上建立 Blinks 應用程式的實用介紹。完整程式碼可以在這裡找到。

要深入了解 Solana Actions 框架和詳細文檔,請查看 Solana 的官方資源

以上是Solana 與 Go 一起閃爍的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn