ソラナは Go で点滅します

王林
リリース: 2024-08-24 06:37:44
オリジナル
271 人が閲覧しました

点滅は、別のアプリや Web ページに移動することなく、Solana エコシステム全体のオンチェーン アクティビティを表し、有効にするメタデータが豊富なリンクです。

Blinks は、Solana Actions によって可能になる幅広いアクティビティをサポートします
そして主にユーザーがソーシャルメディアやその他のオフチェーンプラットフォームを通じてブロックチェーンと対話できるようにします。

ユースケースには次のものが含まれます:

  • NFT 取引と鋳造、
  • 寄付、
  • クラウドファンディング、
  • トークンスワップ、
  • 宝くじ/カジノ アプリなど

この記事では、Go を使用した NFT の鋳造に焦点を当てたシンプルな Blink アプリを検討します。この記事は Go に焦点を当てていますが、中心となる概念はあらゆる Blink アプリに当てはまります。完全なコードは GitHub で見つけることができます。

仕様で定義されている必要な CORS 構成とともに、Gin フレームワークを使用して基本的な Web サーバーをセットアップすることから始めます。また、以下で詳しく説明するいくつかのエンドポイントも定義します。

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 仕様を複製することにあります。以下は、Blink がどのように機能するかを視覚的に表現したものです。

Solana Blinks with Go

アクションハンドラー

Blinks on Solana は、アクション URL スキームを使用してメタデータが豊富なリンクを提供し、さまざまなオンチェーン アクティビティを可能にします。このセクションでは、/api/actions/mint_nft

で mint NFT アクションの処理を担当するプライマリ ハンドラーの概要を説明します。
  • GET ハンドラー : メタデータ、サポートされているアクション、および必須パラメーターを返します。
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 ハンドラー :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 の公式リソースをチェックしてください

以上がソラナは Go で点滅しますの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:dev.to
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!