Blinks 是元数据丰富的链接,代表并支持整个 Solana 生态系统的链上活动,而无需导航到不同的应用程序或网页。
Blinks 支持 Solana Actions 启用的广泛活动
主要允许用户通过社交媒体和其他链下平台与区块链进行交互。
用例包括:
在本文中,我们将探索一个简单的 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 使用 Action URL 方案来提供元数据丰富的链接,从而实现各种链上活动。本节概述了负责处理 /api/actions/mint_nft
上的 mint NFT 操作的主要处理程序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) }
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) }
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) }
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"` }
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 应用程序进行测试。
我希望本文提供有关使用 Go 在 Solana 上构建 Blinks 应用程序的实用介绍。完整代码可以在这里找到。
要深入了解 Solana Actions 框架和详细文档,请查看 Solana 的官方资源
以上是Solana 与 Go 一起闪烁的详细内容。更多信息请关注PHP中文网其他相关文章!