首页 后端开发 Golang Solana 与 Go 一起闪烁

Solana 与 Go 一起闪烁

Aug 24, 2024 am 06:37 AM

Blinks 是元数据丰富的链接,代表并支持整个 Solana 生态系统的链上活动,而无需导航到不同的应用程序或网页。

Blinks 支持 Solana Actions 启用的广泛活动
主要允许用户通过社交媒体和其他链下平台与区块链进行交互。

用例包括

  • NFT 交易和铸造,
  • 捐款,
  • 众筹,
  • 代币交换,
  • 彩票/赌场应用程序等等

在本文中,我们将探索一个简单的 Blink 应用程序,专注于使用 Go 铸造 NFT。虽然本文重点关注 Go,但核心概念适用于任何 Blink 应用程序。您可以在 GitHub 上找到完整的代码。

我们将首先使用 Gin 框架设置一个基本的 Web 服务器,以及规范定义的必要的 CORS 配置。我们还将定义一些端点,下面将详细讨论。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

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:返回元数据、支持的操作和所需参数。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

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 要求,确保与浏览器和其他客户端请求机制的兼容性。

1

2

3

4

5

6

7

8

9

10

11

12

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 编码的序列化交易以及一条供用户签名和执行的消息。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

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,只需进行少量调整。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

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

}

登录后复制
  • 错误处理:操作应按以下格式返回用户友好的错误。

1

2

3

4

// { "message" : "Insert Error Message" } //JSON

type ActionError struct {

    Message string `json:"message"`

}

登录后复制
  • actions.json:actions.json 文件应存储在域的根目录下。它向客户端提供有关哪些 URL 支持 Solana 操作的说明,并提供可用于向 Blink 应用程序执行 GET 请求的映射。为简单起见,我们将从 url 路径返回 JSON 响应

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

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

热门文章

仓库:如何复兴队友
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.能量晶体解释及其做什么(黄色晶体)
1 周前 By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒险:如何获得巨型种子
3 周前 By 尊渡假赌尊渡假赌尊渡假赌

热门文章

仓库:如何复兴队友
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.能量晶体解释及其做什么(黄色晶体)
1 周前 By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒险:如何获得巨型种子
3 周前 By 尊渡假赌尊渡假赌尊渡假赌

热门文章标签

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

Go语言包导入:带下划线和不带下划线的区别是什么? Go语言包导入:带下划线和不带下划线的区别是什么? Mar 03, 2025 pm 05:17 PM

Go语言包导入:带下划线和不带下划线的区别是什么?

Beego框架中NewFlash()函数如何实现页面间短暂信息传递? Beego框架中NewFlash()函数如何实现页面间短暂信息传递? Mar 03, 2025 pm 05:22 PM

Beego框架中NewFlash()函数如何实现页面间短暂信息传递?

如何编写模拟对象和存根以进行测试? 如何编写模拟对象和存根以进行测试? Mar 10, 2025 pm 05:38 PM

如何编写模拟对象和存根以进行测试?

Go语言中如何将MySQL查询结果List转换为自定义结构体切片? Go语言中如何将MySQL查询结果List转换为自定义结构体切片? Mar 03, 2025 pm 05:18 PM

Go语言中如何将MySQL查询结果List转换为自定义结构体切片?

如何定义GO中仿制药的自定义类型约束? 如何定义GO中仿制药的自定义类型约束? Mar 10, 2025 pm 03:20 PM

如何定义GO中仿制药的自定义类型约束?

如何使用跟踪工具了解GO应用程序的执行流? 如何使用跟踪工具了解GO应用程序的执行流? Mar 10, 2025 pm 05:36 PM

如何使用跟踪工具了解GO应用程序的执行流?

您如何在GO中编写单元测试? 您如何在GO中编写单元测试? Mar 21, 2025 pm 06:34 PM

您如何在GO中编写单元测试?

Go语言如何便捷地写入文件? Go语言如何便捷地写入文件? Mar 03, 2025 pm 05:15 PM

Go语言如何便捷地写入文件?

See all articles