From 788dc310a69d8e3107ae658d11e2e900bb203480 Mon Sep 17 00:00:00 2001 From: ivaylogarnev Date: Thu, 16 Jan 2025 17:04:02 +0200 Subject: [PATCH] chore(TCK): Added token management transaction endpoints (#1217) * fix: Changed account transaction int64 params to string Signed-off-by: ivaylogarnev-limechain * feat: Added token management transaction TCK endpoints Signed-off-by: ivaylogarnev-limechain * fix: Comments, plus moved MapSerialNumbersToString to a helper directory Signed-off-by: ivaylogarnev-limechain * fix: Changed gRPP to jRPC typo Signed-off-by: ivaylogarnev-limechain --------- Signed-off-by: ivaylogarnev-limechain --- tck/cmd/server.go | 10 + tck/methods/token.go | 446 ++++++++++++++++++++++++++++++++++++++++++ tck/param/token.go | 37 ++++ tck/response/token.go | 13 ++ tck/utils/mappers.go | 14 ++ 5 files changed, 520 insertions(+) create mode 100644 tck/utils/mappers.go diff --git a/tck/cmd/server.go b/tck/cmd/server.go index dcffacc2..90ba547a 100644 --- a/tck/cmd/server.go +++ b/tck/cmd/server.go @@ -45,6 +45,16 @@ func main() { "updateToken": postHandler(HandleError, handler.New(tokenService.UpdateToken)), "deleteToken": postHandler(HandleError, handler.New(tokenService.DeleteToken)), "updateTokenFeeSchedule": postHandler(HandleError, handler.New(tokenService.UpdateTokenFeeSchedule)), + "associateToken": postHandler(HandleError, handler.New(tokenService.AssociateToken)), + "dissociateToken": postHandler(HandleError, handler.New(tokenService.DissociatesToken)), + "pauseToken": postHandler(HandleError, handler.New(tokenService.PauseToken)), + "unpauseToken": postHandler(HandleError, handler.New(tokenService.UnpauseToken)), + "freezeToken": postHandler(HandleError, handler.New(tokenService.FreezeToken)), + "unfreezeToken": postHandler(HandleError, handler.New(tokenService.UnfreezeToken)), + "grantTokenKyc": postHandler(HandleError, handler.New(tokenService.GrantTokenKyc)), + "revokeTokenKyc": postHandler(HandleError, handler.New(tokenService.RevokeTokenKyc)), + "mintToken": postHandler(HandleError, handler.New(tokenService.MintToken)), + "burnToken": postHandler(HandleError, handler.New(tokenService.BurnToken)), "generateKey": postHandler(HandleError, handler.New(methods.GenerateKey)), } diff --git a/tck/methods/token.go b/tck/methods/token.go index 688e7409..a18d4faf 100644 --- a/tck/methods/token.go +++ b/tck/methods/token.go @@ -4,6 +4,8 @@ package methods import ( "context" + "encoding/hex" + "fmt" "strconv" "time" @@ -365,6 +367,7 @@ func (t *TokenService) UpdateTokenFeeSchedule(_ context.Context, params param.Up } transaction.SetTokenID(tokenId) } + if params.CustomFees != nil { customFees, err := utils.ParseCustomFees(*params.CustomFees) if err != nil { @@ -387,3 +390,446 @@ func (t *TokenService) UpdateTokenFeeSchedule(_ context.Context, params param.Up return &response.TokenResponse{Status: receipt.Status.String()}, nil } + +// AssociateToken jRPC method for associateToken +func (t *TokenService) AssociateToken(_ context.Context, params param.AssociateDissociatesTokenParams) (*response.TokenResponse, error) { + + transaction := hiero.NewTokenAssociateTransaction().SetGrpcDeadline(&threeSecondsDuration) + + if params.AccountId != nil { + accountId, err := hiero.AccountIDFromString(*params.AccountId) + + if err != nil { + return nil, err + } + transaction.SetAccountID(accountId) + } + + if params.TokenIds != nil { + + // Dereference the pointer to access the slice + tokenIds := *params.TokenIds + + // Create a slice to hold the parsed Token IDs + var parsedTokenIds []hiero.TokenID + + // Iterate and parse each Token ID + for _, tokenIDStr := range tokenIds { + + parsedTokenID, err := hiero.TokenIDFromString(tokenIDStr) + + if err != nil { + return nil, err + } + + parsedTokenIds = append(parsedTokenIds, parsedTokenID) + } + + // Set the parsed Token IDs in the transaction + transaction.SetTokenIDs(parsedTokenIds...) + + } + + if params.CommonTransactionParams != nil { + params.CommonTransactionParams.FillOutTransaction(transaction, t.sdkService.Client) + } + + txResponse, err := transaction.Execute(t.sdkService.Client) + if err != nil { + return nil, err + } + receipt, err := txResponse.GetReceipt(t.sdkService.Client) + if err != nil { + return nil, err + } + + return &response.TokenResponse{Status: receipt.Status.String()}, nil +} + +// DisassociateToken jRPC method for dissociateToken +func (t *TokenService) DissociatesToken(_ context.Context, params param.AssociateDissociatesTokenParams) (*response.TokenResponse, error) { + + transaction := hiero.NewTokenDissociateTransaction().SetGrpcDeadline(&threeSecondsDuration) + + if params.AccountId != nil { + accountId, err := hiero.AccountIDFromString(*params.AccountId) + + if err != nil { + return nil, err + } + transaction.SetAccountID(accountId) + } + + if params.TokenIds != nil { + + tokenIds := *params.TokenIds + var parsedTokenIds []hiero.TokenID + + for _, tokenIDStr := range tokenIds { + parsedTokenID, err := hiero.TokenIDFromString(tokenIDStr) + if err != nil { + return nil, err + } + + parsedTokenIds = append(parsedTokenIds, parsedTokenID) + } + + // Set the parsed Token IDs in the transaction + transaction.SetTokenIDs(parsedTokenIds...) + + } + + if params.CommonTransactionParams != nil { + params.CommonTransactionParams.FillOutTransaction(transaction, t.sdkService.Client) + } + + txResponse, err := transaction.Execute(t.sdkService.Client) + if err != nil { + return nil, err + } + receipt, err := txResponse.GetReceipt(t.sdkService.Client) + if err != nil { + return nil, err + } + + return &response.TokenResponse{Status: receipt.Status.String()}, nil +} + +// PauseToken jRPC method for pauseToken +func (t *TokenService) PauseToken(_ context.Context, params param.PauseUnPauseTokenParams) (*response.TokenResponse, error) { + + transaction := hiero.NewTokenPauseTransaction().SetGrpcDeadline(&threeSecondsDuration) + + if params.TokenId != nil { + tokenId, err := hiero.TokenIDFromString(*params.TokenId) + + if err != nil { + return nil, err + } + transaction.SetTokenID(tokenId) + } + + if params.CommonTransactionParams != nil { + params.CommonTransactionParams.FillOutTransaction(transaction, t.sdkService.Client) + } + + txResponse, err := transaction.Execute(t.sdkService.Client) + if err != nil { + return nil, err + } + receipt, err := txResponse.GetReceipt(t.sdkService.Client) + if err != nil { + return nil, err + } + + return &response.TokenResponse{Status: receipt.Status.String()}, nil +} + +// UnpauseToken jRPC method for unpauseToken +func (t *TokenService) UnpauseToken(_ context.Context, params param.PauseUnPauseTokenParams) (*response.TokenResponse, error) { + + transaction := hiero.NewTokenUnpauseTransaction().SetGrpcDeadline(&threeSecondsDuration) + + if params.TokenId != nil { + tokenId, err := hiero.TokenIDFromString(*params.TokenId) + + if err != nil { + return nil, err + } + transaction.SetTokenID(tokenId) + } + + if params.CommonTransactionParams != nil { + params.CommonTransactionParams.FillOutTransaction(transaction, t.sdkService.Client) + } + + txResponse, err := transaction.Execute(t.sdkService.Client) + if err != nil { + return nil, err + } + receipt, err := txResponse.GetReceipt(t.sdkService.Client) + if err != nil { + return nil, err + } + + return &response.TokenResponse{Status: receipt.Status.String()}, nil +} + +// FreezeToken jRPC method for freezeToken +func (t *TokenService) FreezeToken(_ context.Context, params param.FreezeUnFreezeTokenParams) (*response.TokenResponse, error) { + + transaction := hiero.NewTokenFreezeTransaction().SetGrpcDeadline(&threeSecondsDuration) + + if params.AccountId != nil { + accountId, err := hiero.AccountIDFromString(*params.AccountId) + + if err != nil { + return nil, err + } + transaction.SetAccountID(accountId) + } + + if params.TokenId != nil { + tokenId, err := hiero.TokenIDFromString(*params.TokenId) + + if err != nil { + return nil, err + } + transaction.SetTokenID(tokenId) + } + + if params.CommonTransactionParams != nil { + params.CommonTransactionParams.FillOutTransaction(transaction, t.sdkService.Client) + } + + txResponse, err := transaction.Execute(t.sdkService.Client) + if err != nil { + return nil, err + } + receipt, err := txResponse.GetReceipt(t.sdkService.Client) + if err != nil { + return nil, err + } + + return &response.TokenResponse{Status: receipt.Status.String()}, nil +} + +// UnfreezeToken jRPC method for unfreezeToken +func (t *TokenService) UnfreezeToken(_ context.Context, params param.FreezeUnFreezeTokenParams) (*response.TokenResponse, error) { + + transaction := hiero.NewTokenUnfreezeTransaction().SetGrpcDeadline(&threeSecondsDuration) + + if params.AccountId != nil { + accountId, err := hiero.AccountIDFromString(*params.AccountId) + + if err != nil { + return nil, err + } + transaction.SetAccountID(accountId) + } + + if params.TokenId != nil { + tokenId, err := hiero.TokenIDFromString(*params.TokenId) + + if err != nil { + return nil, err + } + transaction.SetTokenID(tokenId) + } + + if params.CommonTransactionParams != nil { + params.CommonTransactionParams.FillOutTransaction(transaction, t.sdkService.Client) + } + + txResponse, err := transaction.Execute(t.sdkService.Client) + if err != nil { + return nil, err + } + receipt, err := txResponse.GetReceipt(t.sdkService.Client) + if err != nil { + return nil, err + } + + return &response.TokenResponse{Status: receipt.Status.String()}, nil +} + +// GrantTokenKyc jRPC method for grantTokenKyc +func (t *TokenService) GrantTokenKyc(_ context.Context, params param.GrantRevokeTokenKycParams) (*response.TokenResponse, error) { + + transaction := hiero.NewTokenGrantKycTransaction().SetGrpcDeadline(&threeSecondsDuration) + + if params.AccountId != nil { + accountId, err := hiero.AccountIDFromString(*params.AccountId) + + if err != nil { + return nil, err + } + transaction.SetAccountID(accountId) + } + + if params.TokenId != nil { + tokenId, err := hiero.TokenIDFromString(*params.TokenId) + + if err != nil { + return nil, err + } + transaction.SetTokenID(tokenId) + } + + if params.CommonTransactionParams != nil { + params.CommonTransactionParams.FillOutTransaction(transaction, t.sdkService.Client) + } + + txResponse, err := transaction.Execute(t.sdkService.Client) + if err != nil { + return nil, err + } + receipt, err := txResponse.GetReceipt(t.sdkService.Client) + if err != nil { + return nil, err + } + + return &response.TokenResponse{Status: receipt.Status.String()}, nil +} + +// RevokeTokenKyc jRPC method for revokeTokenKyc +func (t *TokenService) RevokeTokenKyc(_ context.Context, params param.GrantRevokeTokenKycParams) (*response.TokenResponse, error) { + + transaction := hiero.NewTokenRevokeKycTransaction().SetGrpcDeadline(&threeSecondsDuration) + + if params.AccountId != nil { + accountId, err := hiero.AccountIDFromString(*params.AccountId) + + if err != nil { + return nil, err + } + transaction.SetAccountID(accountId) + } + + if params.TokenId != nil { + tokenId, err := hiero.TokenIDFromString(*params.TokenId) + + if err != nil { + return nil, err + } + transaction.SetTokenID(tokenId) + } + + if params.CommonTransactionParams != nil { + params.CommonTransactionParams.FillOutTransaction(transaction, t.sdkService.Client) + } + + txResponse, err := transaction.Execute(t.sdkService.Client) + if err != nil { + return nil, err + } + receipt, err := txResponse.GetReceipt(t.sdkService.Client) + if err != nil { + return nil, err + } + + return &response.TokenResponse{Status: receipt.Status.String()}, nil +} + +// MintToken jRPC method for mintToken +func (t *TokenService) MintToken(_ context.Context, params param.MintTokenParams) (*response.TokenMintResponse, error) { + + transaction := hiero.NewTokenMintTransaction().SetGrpcDeadline(&threeSecondsDuration) + + if params.TokenId != nil { + tokenId, err := hiero.TokenIDFromString(*params.TokenId) + + if err != nil { + return nil, err + } + transaction.SetTokenID(tokenId) + } + + if params.Metadata != nil { + var allMetadata [][]byte + for _, metadataValue := range *params.Metadata { + decodedMetadata, err := hex.DecodeString(metadataValue) + if err != nil { + return nil, fmt.Errorf("failed to decode metadata: %w", err) + } + allMetadata = append(allMetadata, decodedMetadata) + } + + // Set the separate metadata slices on the transaction + transaction.SetMetadatas(allMetadata) + } + + if params.Amount != nil { + amount, err := strconv.ParseUint(*params.Amount, 10, 64) + if err != nil { + return nil, err + } + + transaction.SetAmount(uint64(amount)) + } + + if params.CommonTransactionParams != nil { + params.CommonTransactionParams.FillOutTransaction(transaction, t.sdkService.Client) + } + + txResponse, err := transaction.Execute(t.sdkService.Client) + if err != nil { + return nil, err + } + receipt, err := txResponse.GetReceipt(t.sdkService.Client) + if err != nil { + return nil, err + } + + // Construct the response + status := receipt.Status.String() + newTotalSupply := strconv.FormatUint(receipt.TotalSupply, 10) + serialNumbers := utils.MapSerialNumbersToString(receipt.SerialNumbers) + + return &response.TokenMintResponse{ + TokenId: params.TokenId, + NewTotalSupply: &newTotalSupply, + SerialNumbers: &serialNumbers, + Status: &status, + }, nil +} + +// BurnToken jRPC method for burnToken +func (t *TokenService) BurnToken(_ context.Context, params param.BurnTokenParams) (*response.TokenBurnResponse, error) { + + transaction := hiero.NewTokenBurnTransaction().SetGrpcDeadline(&threeSecondsDuration) + + if params.TokenId != nil { + tokenId, err := hiero.TokenIDFromString(*params.TokenId) + + if err != nil { + return nil, err + } + transaction.SetTokenID(tokenId) + } + + if params.Amount != nil { + amount, err := strconv.ParseUint(*params.Amount, 10, 64) + if err != nil { + return nil, err + } + + transaction.SetAmount(uint64(amount)) + } + + if params.SerialNumbers != nil { + var allSerialNumbers []int64 + + for _, serialNumber := range *params.SerialNumbers { + serial, err := strconv.ParseInt(serialNumber, 10, 64) + if err != nil { + return nil, fmt.Errorf("failed to parse serial number: %w", err) + } + allSerialNumbers = append(allSerialNumbers, serial) + } + transaction.SetSerialNumbers(allSerialNumbers) + } + + if params.CommonTransactionParams != nil { + params.CommonTransactionParams.FillOutTransaction(transaction, t.sdkService.Client) + } + + txResponse, err := transaction.Execute(t.sdkService.Client) + if err != nil { + return nil, err + } + receipt, err := txResponse.GetReceipt(t.sdkService.Client) + if err != nil { + return nil, err + } + + // Construct the response + status := receipt.Status.String() + newTotalSupply := strconv.FormatUint(receipt.TotalSupply, 10) + + return &response.TokenBurnResponse{ + TokenId: params.TokenId, + NewTotalSupply: &newTotalSupply, + Status: &status, + }, nil +} diff --git a/tck/param/token.go b/tck/param/token.go index 68af6fbf..cf73c713 100644 --- a/tck/param/token.go +++ b/tck/param/token.go @@ -60,3 +60,40 @@ type UpdateTokenFeeScheduleParams struct { CommonTransactionParams *CommonTransactionParams `json:"commonTransactionParams,omitempty"` CustomFees *[]CustomFee `json:"customFees,omitempty"` } + +type AssociateDissociatesTokenParams struct { + AccountId *string `json:"accountId,omitempty"` + TokenIds *[]string `json:"tokenIds,omitempty"` + CommonTransactionParams *CommonTransactionParams `json:"commonTransactionParams,omitempty"` +} + +type PauseUnPauseTokenParams struct { + TokenId *string `json:"tokenId,omitempty"` + CommonTransactionParams *CommonTransactionParams `json:"commonTransactionParams,omitempty"` +} + +type FreezeUnFreezeTokenParams struct { + AccountId *string `json:"accountId,omitempty"` + TokenId *string `json:"tokenId,omitempty"` + CommonTransactionParams *CommonTransactionParams `json:"commonTransactionParams,omitempty"` +} + +type GrantRevokeTokenKycParams struct { + AccountId *string `json:"accountId,omitempty"` + TokenId *string `json:"tokenId,omitempty"` + CommonTransactionParams *CommonTransactionParams `json:"commonTransactionParams,omitempty"` +} + +type MintTokenParams struct { + TokenId *string `json:"tokenId,omitempty"` + Amount *string `json:"amount,omitempty"` + Metadata *[]string `json:"metadata,omitempty"` + CommonTransactionParams *CommonTransactionParams `json:"commonTransactionParams,omitempty"` +} + +type BurnTokenParams struct { + TokenId *string `json:"tokenId,omitempty"` + Amount *string `json:"amount,omitempty"` + SerialNumbers *[]string `json:"serialNumbers,omitempty"` + CommonTransactionParams *CommonTransactionParams `json:"commonTransactionParams,omitempty"` +} diff --git a/tck/response/token.go b/tck/response/token.go index 5987fc52..2cbbabf1 100644 --- a/tck/response/token.go +++ b/tck/response/token.go @@ -6,3 +6,16 @@ type TokenResponse struct { TokenId string `json:"tokenId"` Status string `json:"status"` } + +type TokenMintResponse struct { + TokenId *string `json:"tokenId,omitempty"` + NewTotalSupply *string `json:"newTotalSupply,omitempty"` + SerialNumbers *[]string `json:"serialNumbers,omitempty"` + Status *string `json:"status,omitempty"` +} + +type TokenBurnResponse struct { + TokenId *string `json:"tokenId,omitempty"` + NewTotalSupply *string `json:"newTotalSupply,omitempty"` + Status *string `json:"status,omitempty"` +} diff --git a/tck/utils/mappers.go b/tck/utils/mappers.go new file mode 100644 index 00000000..15f853b4 --- /dev/null +++ b/tck/utils/mappers.go @@ -0,0 +1,14 @@ +package utils + +import "fmt" + +// SPDX-License-Identifier: Apache-2.0 + +// Helper function to map serial numbers to strings +func MapSerialNumbersToString(serials []int64) []string { + serialStrings := make([]string, len(serials)) + for i, serial := range serials { + serialStrings[i] = fmt.Sprintf("%d", serial) + } + return serialStrings +}