> 백엔드 개발 > Golang > 솔라나는 바둑으로 눈을 깜빡인다

솔라나는 바둑으로 눈을 깜빡인다

王林
풀어 주다: 2024-08-24 06:37:44
원래의
392명이 탐색했습니다.

블링크는 다른 앱이나 웹페이지로 이동할 필요 없이 솔라나 생태계 전반에 걸쳐 온체인 활동을 표현하고 활성화하는 메타데이터가 풍부한 링크입니다.

Blinks는 Solana Actions를 통해 다양한 활동을 지원합니다
주로 사용자가 소셜 미디어 및 기타 오프체인 플랫폼을 통해 블록체인과 상호 작용할 수 있도록 합니다.

사용 사례에는 다음이 포함됩니다:

  • NFT 거래 및 채굴
  • 기부,
  • 크라우드 펀딩
  • 토큰 스왑,
  • 복권/카지노 앱 등

이 기사에서는 Go를 사용하여 NFT를 발행하는 데 초점을 맞춘 간단한 Blink 앱을 살펴보겠습니다. 기사는 Go에 초점을 맞추고 있지만 핵심 개념은 모든 Blink 앱에 적용됩니다. GitHub에서 전체 코드를 찾을 수 있습니다.

사양에 정의된 필수 CORS 구성과 함께 Gin 프레임워크를 사용하여 기본 웹 서버를 설정하는 것부터 시작하겠습니다. 또한 아래에서 자세히 설명할 몇 가지 엔드포인트를 정의하겠습니다.

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 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 핸들러: 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 기능은 NFT 발행을 위해 약간의 조정만으로 Solana-Go-SDK를 활용합니다.

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 파일은 도메인 루트에 저장되어야 합니다. Solana Actions를 지원하는 URL에 대한 지침을 클라이언트에 제공하고 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의 공식 리소스를 확인하세요

위 내용은 솔라나는 바둑으로 눈을 깜빡인다의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:dev.to
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿