From 91c3298698977ec78764ca736ea655abbcc287ee Mon Sep 17 00:00:00 2001 From: "valeri.ivanchev@limechain.tech" Date: Thu, 30 May 2024 21:05:26 +0300 Subject: [PATCH] Adding script for token transfer --- scripts/README.md | 13 ++ scripts/token/transfer/transfer.go | 190 +++++++++++++++++++++++++++++ 2 files changed, 203 insertions(+) create mode 100644 scripts/token/transfer/transfer.go diff --git a/scripts/README.md b/scripts/README.md index 8967b0e91..f76f47910 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -33,3 +33,16 @@ 2. Run setup-from-config.go with privateKey, accountID, network, members, adminKey, topicThreshold, wrappedTokenThreshold, configPath to fully deploy bridge with all Hedera Tokens from specified extended bridge config. `go run ./scripts/bridge/setup/from-config/cmd/setup-from-config.go --privateKey=/your private key/ --accountID=/your account id/ --network=/previewnet|testnet|mainnet/ --members=/int, the count of the wanted bridge custodians/ --adminKey=/your admin key/ --topicThreshold=/topic threshold of signs needed to submit a message/ --wrappedTokenThreshold=/threshold of signs needed to manage a wrapped token/ --configPath=/the path to the extended bridge config/` + +## Transfer tokens +Param Name | Description + --- | --- +privateKey | Private Key of the `Sender` +senderAccountId | `Sender` Account id. +recipientAccountId | `Recipient` Account id. +network | Hedera network: `mainet` or `testnet`. +tokenIDs | IDs of the tokens that will be transfered. They must be seperated by coma. +hbarAmount | Amount of `HBAR`'s that will be transfered to the `Recipient` + +1. Run `transfer.go` +`go run ./scripts/token/transfer/transfer.go -senderAccountId=/your sender account id/ --privateKey=/your private key/ --network=/testnet|mainnet/ --recipientAccountId=/recipient account id/ --tokenIds=/ids of the tokens you want to transfer (separated by coma)/ --hbarAmount=/amount of HBARs that will be send to the user/` \ No newline at end of file diff --git a/scripts/token/transfer/transfer.go b/scripts/token/transfer/transfer.go new file mode 100644 index 000000000..7df5f5fd3 --- /dev/null +++ b/scripts/token/transfer/transfer.go @@ -0,0 +1,190 @@ +/* + * Copyright 2022 LimeChain Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "flag" + "fmt" + "log" + "strings" + + "github.com/hashgraph/hedera-sdk-go/v2" + mirror_node "github.com/limechain/hedera-eth-bridge-validator/app/clients/hedera/mirror-node" + mirrorNodeClient "github.com/limechain/hedera-eth-bridge-validator/app/domain/client" + "github.com/limechain/hedera-eth-bridge-validator/config" + "github.com/limechain/hedera-eth-bridge-validator/scripts/client" +) + +type AccountBalance struct { + Balance uint64 `json:"balance"` + Tokens []TokenBalance `json:"tokens"` +} + +type TokenBalance struct { + TokenID string `json:"token_id"` + Balance uint64 `json:"balance"` +} + +const ( + HederaMainnetNetworkId = 295 + HederaTestnetNetworkId = 296 +) + +func getAccountBalanceFromMirrorNode(mirrorClient mirrorNodeClient.MirrorNode, accountID string) (map[string]int, error) { + account, err := mirrorClient.GetAccount(accountID) + if err != nil { + return nil, fmt.Errorf("error getting account balance from mirror node: %w", err) + } + hederaTokenBalancesByAddress := make(map[string]int) + for _, token := range account.Balance.Tokens { + hederaTokenBalancesByAddress[token.TokenID] = token.Balance + } + + return hederaTokenBalancesByAddress, nil +} + +func transferTokens(client *hedera.Client, mirrorClient mirrorNodeClient.MirrorNode, senderAccountID hedera.AccountID, recipientAccountId hedera.AccountID, tokenIds []hedera.TokenID, hbarAmount *hedera.Hbar) (*hedera.TransactionReceipt, error) { + accountBalance, err := getAccountBalanceFromMirrorNode(mirrorClient, senderAccountID.String()) + if err != nil { + return nil, fmt.Errorf("error retrieving account balance: %w", err) + } + + fmt.Println("Preparing transaction") + + transferTransaction := hedera.NewTransferTransaction() + + if hbarAmount != nil { + transferTransaction. + AddHbarTransfer(senderAccountID, hbarAmount.Negated()). + AddHbarTransfer(recipientAccountId, *hbarAmount) + } + + for _, tokenID := range tokenIds { + if balance, ok := accountBalance[tokenID.String()]; ok { + transferTransaction.AddTokenTransfer(tokenID, senderAccountID, -int64(balance)). + AddTokenTransfer(tokenID, recipientAccountId, int64(balance)) + } else { + log.Printf("No balance found for token ID: %v", tokenID) + } + } + + txResponse, err := transferTransaction.Execute(client) + + if err != nil { + return nil, fmt.Errorf("error retrieving receipt: %w", err) + } + + receipt, err := txResponse.GetReceipt(client) + if err != nil { + return nil, fmt.Errorf("error retrieving receipt: %w", err) + } + + if receipt.Status != hedera.StatusSuccess { + return nil, fmt.Errorf("transaction failed with status: %v", receipt.Status) + } + + return &receipt, nil +} + +func main() { + privateKey := flag.String("privateKey", "0x0", "Hedera Private Key") + senderAccountId := flag.String("senderAccountId", "0.0", "Sender Account ID") + recipientAccountId := flag.String("recipientAccountId", "", "Recipient account ID") + network := flag.String("network", "", "Hedera Network Type") + tokenIDs := flag.String("tokenIds", "", "Comma-separated list of token IDs") + hbarAmount := flag.Float64("hbarAmount", 0, "Amount of HBAR to transfer (optional)") + + flag.Parse() + if *privateKey == "0x0" { + panic("Private key was not provided") + } + if *senderAccountId == "0.0" { + panic("Account id was not provided") + } + if *tokenIDs == "" { + panic("Token ids was not provided") + } + if *recipientAccountId == "" { + panic("Recipient id was not provided") + } + if *network == "" { + panic("Network was not provided") + } + + parsedSenderAccountId, err := hedera.AccountIDFromString(*senderAccountId) + if err != nil { + log.Fatalf("Error parsing sender account ID: %v", err) + } + + parsedRecipientAccountId, err := hedera.AccountIDFromString(*recipientAccountId) + if err != nil { + log.Fatalf("Error parsing recipient account ID: %v", err) + } + + client := client.Init(*privateKey, *senderAccountId, *network) + + tokenIDStrings := strings.Split(*tokenIDs, ",") + var hederaTokenIDs []hedera.TokenID + for _, tokenIDStr := range tokenIDStrings { + tokenID, err := hedera.TokenIDFromString(tokenIDStr) + if err != nil { + log.Fatalf("Error parsing token ID '%s': %v", tokenIDStr, err) + } + hederaTokenIDs = append(hederaTokenIDs, tokenID) + } + + var hbarAmountHedera *hedera.Hbar + if *hbarAmount > 0 { + hbarValue := hedera.HbarFrom(*hbarAmount, hedera.HbarUnits.Hbar) + hbarAmountHedera = &hbarValue + } + + var hederaNetworkId uint64 + if *network != "testnet" { + var confirmation string + fmt.Printf("Network is set to [%s]. Are you sure you what to proceed?\n", *network) + fmt.Println("Y/N:") + fmt.Scanln(&confirmation) + if confirmation != "Y" { + panic("Exiting") + } + hederaNetworkId = HederaMainnetNetworkId + } else { + hederaNetworkId = HederaTestnetNetworkId + } + + mirrorNodeConfigByNetwork := map[uint64]config.MirrorNode{ + HederaMainnetNetworkId: { + ClientAddress: "mainnet-public.mirrornode.hedera.com/:443", + ApiAddress: "https://mainnet-public.mirrornode.hedera.com/api/v1/", + }, + HederaTestnetNetworkId: { + ClientAddress: "hcs.testnet.mirrornode.hedera.com:5600", + ApiAddress: "https://testnet.mirrornode.hedera.com/api/v1/", + }, + } + + mirrorClient := mirror_node.NewClient(mirrorNodeConfigByNetwork[hederaNetworkId]) + + _, err = transferTokens(client, mirrorClient, parsedSenderAccountId, parsedRecipientAccountId, hederaTokenIDs, hbarAmountHedera) + + if err != nil { + log.Fatalf("Error transferring tokens and HBAR: %v", err) + } + + fmt.Println("Tokens and HBAR successfully transferred") +}