github.com/hashgraph/hedera-sdk-go/v2@v2.48.0/transfer_transaction.go (about)

     1  package hedera
     2  
     3  /*-
     4   *
     5   * Hedera Go SDK
     6   *
     7   * Copyright (C) 2020 - 2024 Hedera Hashgraph, LLC
     8   *
     9   * Licensed under the Apache License, Version 2.0 (the "License");
    10   * you may not use this file except in compliance with the License.
    11   * You may obtain a copy of the License at
    12   *
    13   *      http://www.apache.org/licenses/LICENSE-2.0
    14   *
    15   * Unless required by applicable law or agreed to in writing, software
    16   * distributed under the License is distributed on an "AS IS" BASIS,
    17   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    18   * See the License for the specific language governing permissions and
    19   * limitations under the License.
    20   *
    21   */
    22  
    23  import (
    24  	"time"
    25  
    26  	"google.golang.org/protobuf/types/known/wrapperspb"
    27  
    28  	"github.com/hashgraph/hedera-protobufs-go/services"
    29  )
    30  
    31  // TransferTransaction
    32  // Transfers cryptocurrency among two or more accounts by making the desired adjustments to their
    33  // balances. Each transfer list can specify up to 10 adjustments. Each negative amount is withdrawn
    34  // from the corresponding account (a sender), and each positive one is added to the corresponding
    35  // account (a receiver). The amounts list must sum to zero. Each amount is a number of tinybars
    36  // (there are 100,000,000 tinybars in one hbar).  If any sender account fails to have sufficient
    37  // hbars, then the entire transaction fails, and none of those transfers occur, though the
    38  // transaction fee is still charged. This transaction must be signed by the keys for all the sending
    39  // accounts, and for any receiving accounts that have receiverSigRequired == true. The signatures
    40  // are in the same order as the accounts, skipping those accounts that don't need a signature.
    41  type TransferTransaction struct {
    42  	Transaction
    43  	tokenTransfers map[TokenID]*_TokenTransfer
    44  	hbarTransfers  []*_HbarTransfer
    45  	nftTransfers   map[TokenID][]*_TokenNftTransfer
    46  }
    47  
    48  // NewTransferTransaction creates TransferTransaction which
    49  // transfers cryptocurrency among two or more accounts by making the desired adjustments to their
    50  // balances. Each transfer list can specify up to 10 adjustments. Each negative amount is withdrawn
    51  // from the corresponding account (a sender), and each positive one is added to the corresponding
    52  // account (a receiver). The amounts list must sum to zero. Each amount is a number of tinybars
    53  // (there are 100,000,000 tinybars in one hbar).  If any sender account fails to have sufficient
    54  // hbars, then the entire transaction fails, and none of those transfers occur, though the
    55  // transaction fee is still charged. This transaction must be signed by the keys for all the sending
    56  // accounts, and for any receiving accounts that have receiverSigRequired == true. The signatures
    57  // are in the same order as the accounts, skipping those accounts that don't need a signature.
    58  func NewTransferTransaction() *TransferTransaction {
    59  	tx := TransferTransaction{
    60  		Transaction:    _NewTransaction(),
    61  		tokenTransfers: make(map[TokenID]*_TokenTransfer),
    62  		hbarTransfers:  make([]*_HbarTransfer, 0),
    63  		nftTransfers:   make(map[TokenID][]*_TokenNftTransfer),
    64  	}
    65  
    66  	tx._SetDefaultMaxTransactionFee(NewHbar(1))
    67  
    68  	return &tx
    69  }
    70  
    71  func _TransferTransactionFromProtobuf(tx Transaction, pb *services.TransactionBody) *TransferTransaction {
    72  	tokenTransfers := make(map[TokenID]*_TokenTransfer)
    73  	nftTransfers := make(map[TokenID][]*_TokenNftTransfer)
    74  
    75  	for _, tokenTransfersList := range pb.GetCryptoTransfer().GetTokenTransfers() {
    76  		tok := _TokenIDFromProtobuf(tokenTransfersList.Token)
    77  		tokenTransfers[*tok] = _TokenTransferPrivateFromProtobuf(tokenTransfersList)
    78  	}
    79  
    80  	for _, tokenTransfersList := range pb.GetCryptoTransfer().GetTokenTransfers() {
    81  		if tokenID := _TokenIDFromProtobuf(tokenTransfersList.Token); tokenID != nil {
    82  			for _, aa := range tokenTransfersList.GetNftTransfers() {
    83  				if nftTransfers[*tokenID] == nil {
    84  					nftTransfers[*tokenID] = make([]*_TokenNftTransfer, 0)
    85  				}
    86  				nftTransfer := _NftTransferFromProtobuf(aa)
    87  				nftTransfers[*tokenID] = append(nftTransfers[*tokenID], &nftTransfer)
    88  			}
    89  		}
    90  	}
    91  
    92  	return &TransferTransaction{
    93  		Transaction:    tx,
    94  		hbarTransfers:  _HbarTransferFromProtobuf(pb.GetCryptoTransfer().GetTransfers().GetAccountAmounts()),
    95  		tokenTransfers: tokenTransfers,
    96  		nftTransfers:   nftTransfers,
    97  	}
    98  }
    99  
   100  // SetTokenTransferApproval Sets the desired token unit balance adjustments
   101  func (tx *TransferTransaction) SetTokenTransferApproval(tokenID TokenID, accountID AccountID, approval bool) *TransferTransaction { //nolint
   102  	for token, tokenTransfer := range tx.tokenTransfers {
   103  		if token.Compare(tokenID) == 0 {
   104  			for _, transfer := range tokenTransfer.Transfers {
   105  				if transfer.accountID.Compare(accountID) == 0 {
   106  					transfer.IsApproved = approval
   107  				}
   108  			}
   109  		}
   110  	}
   111  
   112  	return tx
   113  }
   114  
   115  // SetHbarTransferApproval Sets the desired hbar balance adjustments
   116  func (tx *TransferTransaction) SetHbarTransferApproval(spenderAccountID AccountID, approval bool) *TransferTransaction { //nolint
   117  	for _, k := range tx.hbarTransfers {
   118  		if k.accountID.String() == spenderAccountID.String() {
   119  			k.IsApproved = approval
   120  		}
   121  	}
   122  	return tx
   123  }
   124  
   125  // SetNftTransferApproval Sets the desired nft token unit balance adjustments
   126  func (tx *TransferTransaction) SetNftTransferApproval(nftID NftID, approval bool) *TransferTransaction {
   127  	for token, nftTransfers := range tx.nftTransfers {
   128  		if token.Compare(nftID.TokenID) == 0 {
   129  			for _, nftTransfer := range nftTransfers {
   130  				if nftTransfer.SerialNumber == nftID.SerialNumber {
   131  					nftTransfer.IsApproved = approval
   132  				}
   133  			}
   134  		}
   135  	}
   136  	return tx
   137  }
   138  
   139  // GetNftTransfers returns the nft transfers
   140  func (tx *TransferTransaction) GetNftTransfers() map[TokenID][]_TokenNftTransfer {
   141  	nftResult := make(map[TokenID][]_TokenNftTransfer)
   142  	for token, nftTransfers := range tx.nftTransfers {
   143  		tempArray := make([]_TokenNftTransfer, 0)
   144  		for _, nftTransfer := range nftTransfers {
   145  			tempArray = append(tempArray, *nftTransfer)
   146  		}
   147  
   148  		nftResult[token] = tempArray
   149  	}
   150  
   151  	return nftResult
   152  }
   153  
   154  // GetTokenTransfers returns the token transfers
   155  func (tx *TransferTransaction) GetTokenTransfers() map[TokenID][]TokenTransfer {
   156  	transfers := make(map[TokenID][]TokenTransfer)
   157  	for tokenID, tokenTransfers := range tx.tokenTransfers {
   158  		tokenTransfersList := make([]TokenTransfer, 0)
   159  
   160  		for _, transfer := range tokenTransfers.Transfers {
   161  			var acc AccountID
   162  			if transfer.accountID != nil {
   163  				acc = *transfer.accountID
   164  			}
   165  			tokenTransfersList = append(tokenTransfersList, TokenTransfer{
   166  				AccountID:  acc,
   167  				Amount:     transfer.Amount.AsTinybar(),
   168  				IsApproved: transfer.IsApproved,
   169  			})
   170  		}
   171  
   172  		tempTokenTransferList := _TokenTransfers{tokenTransfersList}
   173  
   174  		transfers[tokenID] = tempTokenTransferList.transfers
   175  	}
   176  
   177  	return transfers
   178  }
   179  
   180  // GetHbarTransfers returns the hbar transfers
   181  func (tx *TransferTransaction) GetHbarTransfers() map[AccountID]Hbar {
   182  	result := make(map[AccountID]Hbar)
   183  	for _, hbarTransfers := range tx.hbarTransfers {
   184  		result[*hbarTransfers.accountID] = hbarTransfers.Amount
   185  	}
   186  	return result
   187  }
   188  
   189  // AddHbarTransfer Sets The desired hbar balance adjustments
   190  func (tx *TransferTransaction) AddHbarTransfer(accountID AccountID, amount Hbar) *TransferTransaction {
   191  	tx._RequireNotFrozen()
   192  
   193  	for _, transfer := range tx.hbarTransfers {
   194  		if transfer.accountID.Compare(accountID) == 0 {
   195  			transfer.Amount = HbarFromTinybar(amount.AsTinybar() + transfer.Amount.AsTinybar())
   196  			return tx
   197  		}
   198  	}
   199  
   200  	tx.hbarTransfers = append(tx.hbarTransfers, &_HbarTransfer{
   201  		accountID:  &accountID,
   202  		Amount:     amount,
   203  		IsApproved: false,
   204  	})
   205  
   206  	return tx
   207  }
   208  
   209  // GetTokenIDDecimals returns the token decimals
   210  func (tx *TransferTransaction) GetTokenIDDecimals() map[TokenID]uint32 {
   211  	result := make(map[TokenID]uint32)
   212  	for token, tokenTransfer := range tx.tokenTransfers {
   213  		if tokenTransfer.ExpectedDecimals != nil {
   214  			result[token] = *tokenTransfer.ExpectedDecimals
   215  		}
   216  	}
   217  	return result
   218  }
   219  
   220  // AddTokenTransferWithDecimals Sets the desired token unit balance adjustments with decimals
   221  func (tx *TransferTransaction) AddTokenTransferWithDecimals(tokenID TokenID, accountID AccountID, value int64, decimal uint32) *TransferTransaction { //nolint
   222  	tx._RequireNotFrozen()
   223  
   224  	for token, tokenTransfer := range tx.tokenTransfers {
   225  		if token.Compare(tokenID) == 0 {
   226  			for _, transfer := range tokenTransfer.Transfers {
   227  				if transfer.accountID.Compare(accountID) == 0 {
   228  					transfer.Amount = HbarFromTinybar(transfer.Amount.AsTinybar() + value)
   229  					tokenTransfer.ExpectedDecimals = &decimal
   230  
   231  					return tx
   232  				}
   233  			}
   234  		}
   235  	}
   236  
   237  	if v, ok := tx.tokenTransfers[tokenID]; ok {
   238  		v.Transfers = append(v.Transfers, &_HbarTransfer{
   239  			accountID:  &accountID,
   240  			Amount:     HbarFromTinybar(value),
   241  			IsApproved: false,
   242  		})
   243  		v.ExpectedDecimals = &decimal
   244  
   245  		return tx
   246  	}
   247  
   248  	tx.tokenTransfers[tokenID] = &_TokenTransfer{
   249  		Transfers: []*_HbarTransfer{{
   250  			accountID:  &accountID,
   251  			Amount:     HbarFromTinybar(value),
   252  			IsApproved: false,
   253  		}},
   254  		ExpectedDecimals: &decimal,
   255  	}
   256  
   257  	return tx
   258  }
   259  
   260  // AddTokenTransfer Sets the desired token unit balance adjustments
   261  // Applicable to tokens of type FUNGIBLE_COMMON.
   262  func (tx *TransferTransaction) AddTokenTransfer(tokenID TokenID, accountID AccountID, value int64) *TransferTransaction { //nolint
   263  	tx._RequireNotFrozen()
   264  
   265  	for token, tokenTransfer := range tx.tokenTransfers {
   266  		if token.Compare(tokenID) == 0 {
   267  			for _, transfer := range tokenTransfer.Transfers {
   268  				if transfer.accountID.Compare(accountID) == 0 {
   269  					transfer.Amount = HbarFromTinybar(transfer.Amount.AsTinybar() + value)
   270  
   271  					return tx
   272  				}
   273  			}
   274  		}
   275  	}
   276  
   277  	if v, ok := tx.tokenTransfers[tokenID]; ok {
   278  		v.Transfers = append(v.Transfers, &_HbarTransfer{
   279  			accountID:  &accountID,
   280  			Amount:     HbarFromTinybar(value),
   281  			IsApproved: false,
   282  		})
   283  
   284  		return tx
   285  	}
   286  
   287  	tx.tokenTransfers[tokenID] = &_TokenTransfer{
   288  		Transfers: []*_HbarTransfer{{
   289  			accountID:  &accountID,
   290  			Amount:     HbarFromTinybar(value),
   291  			IsApproved: false,
   292  		}},
   293  	}
   294  
   295  	return tx
   296  }
   297  
   298  // AddNftTransfer Sets the desired nft token unit balance adjustments
   299  // Applicable to tokens of type NON_FUNGIBLE_UNIQUE.
   300  func (tx *TransferTransaction) AddNftTransfer(nftID NftID, sender AccountID, receiver AccountID) *TransferTransaction {
   301  	tx._RequireNotFrozen()
   302  
   303  	if tx.nftTransfers == nil {
   304  		tx.nftTransfers = make(map[TokenID][]*_TokenNftTransfer)
   305  	}
   306  
   307  	if tx.nftTransfers[nftID.TokenID] == nil {
   308  		tx.nftTransfers[nftID.TokenID] = make([]*_TokenNftTransfer, 0)
   309  	}
   310  
   311  	tx.nftTransfers[nftID.TokenID] = append(tx.nftTransfers[nftID.TokenID], &_TokenNftTransfer{
   312  		SenderAccountID:   sender,
   313  		ReceiverAccountID: receiver,
   314  		SerialNumber:      nftID.SerialNumber,
   315  	})
   316  
   317  	return tx
   318  }
   319  
   320  // AddHbarTransferWithDecimals adds an approved hbar transfer
   321  func (tx *TransferTransaction) AddApprovedHbarTransfer(accountID AccountID, amount Hbar, approve bool) *TransferTransaction {
   322  	tx._RequireNotFrozen()
   323  
   324  	for _, transfer := range tx.hbarTransfers {
   325  		if transfer.accountID.Compare(accountID) == 0 {
   326  			transfer.Amount = HbarFromTinybar(amount.AsTinybar() + transfer.Amount.AsTinybar())
   327  			transfer.IsApproved = approve
   328  			return tx
   329  		}
   330  	}
   331  
   332  	tx.hbarTransfers = append(tx.hbarTransfers, &_HbarTransfer{
   333  		accountID:  &accountID,
   334  		Amount:     amount,
   335  		IsApproved: approve,
   336  	})
   337  
   338  	return tx
   339  }
   340  
   341  // AddHbarTransfer adds an approved hbar transfer with decimals
   342  func (tx *TransferTransaction) AddApprovedTokenTransferWithDecimals(tokenID TokenID, accountID AccountID, value int64, decimal uint32, approve bool) *TransferTransaction { //nolint
   343  	tx._RequireNotFrozen()
   344  
   345  	for token, tokenTransfer := range tx.tokenTransfers {
   346  		if token.Compare(tokenID) == 0 {
   347  			for _, transfer := range tokenTransfer.Transfers {
   348  				if transfer.accountID.Compare(accountID) == 0 {
   349  					transfer.Amount = HbarFromTinybar(transfer.Amount.AsTinybar() + value)
   350  					tokenTransfer.ExpectedDecimals = &decimal
   351  					for _, transfer := range tokenTransfer.Transfers {
   352  						transfer.IsApproved = approve
   353  					}
   354  
   355  					return tx
   356  				}
   357  			}
   358  		}
   359  	}
   360  
   361  	if v, ok := tx.tokenTransfers[tokenID]; ok {
   362  		v.Transfers = append(v.Transfers, &_HbarTransfer{
   363  			accountID:  &accountID,
   364  			Amount:     HbarFromTinybar(value),
   365  			IsApproved: approve,
   366  		})
   367  		v.ExpectedDecimals = &decimal
   368  
   369  		return tx
   370  	}
   371  
   372  	tx.tokenTransfers[tokenID] = &_TokenTransfer{
   373  		Transfers: []*_HbarTransfer{{
   374  			accountID:  &accountID,
   375  			Amount:     HbarFromTinybar(value),
   376  			IsApproved: approve,
   377  		}},
   378  		ExpectedDecimals: &decimal,
   379  	}
   380  
   381  	return tx
   382  }
   383  
   384  // AddHbarTransfer adds an approved hbar transfer
   385  func (tx *TransferTransaction) AddApprovedTokenTransfer(tokenID TokenID, accountID AccountID, value int64, approve bool) *TransferTransaction { //nolint
   386  	tx._RequireNotFrozen()
   387  
   388  	for token, tokenTransfer := range tx.tokenTransfers {
   389  		if token.Compare(tokenID) == 0 {
   390  			for _, transfer := range tokenTransfer.Transfers {
   391  				if transfer.accountID.Compare(accountID) == 0 {
   392  					transfer.Amount = HbarFromTinybar(transfer.Amount.AsTinybar() + value)
   393  					transfer.IsApproved = approve
   394  
   395  					return tx
   396  				}
   397  			}
   398  		}
   399  	}
   400  
   401  	if v, ok := tx.tokenTransfers[tokenID]; ok {
   402  		v.Transfers = append(v.Transfers, &_HbarTransfer{
   403  			accountID:  &accountID,
   404  			Amount:     HbarFromTinybar(value),
   405  			IsApproved: approve,
   406  		})
   407  
   408  		return tx
   409  	}
   410  
   411  	tx.tokenTransfers[tokenID] = &_TokenTransfer{
   412  		Transfers: []*_HbarTransfer{{
   413  			accountID:  &accountID,
   414  			Amount:     HbarFromTinybar(value),
   415  			IsApproved: approve,
   416  		}},
   417  	}
   418  
   419  	return tx
   420  }
   421  
   422  // AddNftTransfer adds an approved nft transfer
   423  func (tx *TransferTransaction) AddApprovedNftTransfer(nftID NftID, sender AccountID, receiver AccountID, approve bool) *TransferTransaction {
   424  	tx._RequireNotFrozen()
   425  
   426  	if tx.nftTransfers == nil {
   427  		tx.nftTransfers = make(map[TokenID][]*_TokenNftTransfer)
   428  	}
   429  
   430  	if tx.nftTransfers[nftID.TokenID] == nil {
   431  		tx.nftTransfers[nftID.TokenID] = make([]*_TokenNftTransfer, 0)
   432  	}
   433  
   434  	tx.nftTransfers[nftID.TokenID] = append(tx.nftTransfers[nftID.TokenID], &_TokenNftTransfer{
   435  		SenderAccountID:   sender,
   436  		ReceiverAccountID: receiver,
   437  		SerialNumber:      nftID.SerialNumber,
   438  		IsApproved:        approve,
   439  	})
   440  
   441  	return tx
   442  }
   443  
   444  // ---- Required Interfaces ---- //
   445  
   446  // Sign uses the provided privateKey to sign the transaction.
   447  func (tx *TransferTransaction) Sign(privateKey PrivateKey) *TransferTransaction {
   448  	tx.Transaction.Sign(privateKey)
   449  	return tx
   450  }
   451  
   452  // SignWithOperator signs the transaction with client's operator privateKey.
   453  func (tx *TransferTransaction) SignWithOperator(client *Client) (*TransferTransaction, error) {
   454  	_, err := tx.Transaction.signWithOperator(client, tx)
   455  	if err != nil {
   456  		return nil, err
   457  	}
   458  	return tx, nil
   459  }
   460  
   461  // SignWith executes the TransactionSigner and adds the resulting signature data to the Transaction's signature map
   462  // with the publicKey as the map key.
   463  func (tx *TransferTransaction) SignWith(
   464  	publicKey PublicKey,
   465  	signer TransactionSigner,
   466  ) *TransferTransaction {
   467  	tx.Transaction.SignWith(publicKey, signer)
   468  	return tx
   469  }
   470  
   471  // AddSignature adds a signature to the transaction.
   472  func (tx *TransferTransaction) AddSignature(publicKey PublicKey, signature []byte) *TransferTransaction {
   473  	tx.Transaction.AddSignature(publicKey, signature)
   474  	return tx
   475  }
   476  
   477  // When execution is attempted, a single attempt will timeout when this deadline is reached. (The SDK may subsequently retry the execution.)
   478  func (tx *TransferTransaction) SetGrpcDeadline(deadline *time.Duration) *TransferTransaction {
   479  	tx.Transaction.SetGrpcDeadline(deadline)
   480  	return tx
   481  }
   482  
   483  func (tx *TransferTransaction) Freeze() (*TransferTransaction, error) {
   484  	return tx.FreezeWith(nil)
   485  }
   486  
   487  func (tx *TransferTransaction) FreezeWith(client *Client) (*TransferTransaction, error) {
   488  	_, err := tx.Transaction.freezeWith(client, tx)
   489  	return tx, err
   490  }
   491  
   492  // SetMaxTransactionFee sets the max transaction fee for this TransferTransaction.
   493  func (tx *TransferTransaction) SetMaxTransactionFee(fee Hbar) *TransferTransaction {
   494  	tx.Transaction.SetMaxTransactionFee(fee)
   495  	return tx
   496  }
   497  
   498  // SetRegenerateTransactionID sets if transaction IDs should be regenerated when `TRANSACTION_EXPIRED` is received
   499  func (tx *TransferTransaction) SetRegenerateTransactionID(regenerateTransactionID bool) *TransferTransaction {
   500  	tx.Transaction.SetRegenerateTransactionID(regenerateTransactionID)
   501  	return tx
   502  }
   503  
   504  // SetTransactionMemo sets the memo for this TransferTransaction.
   505  func (tx *TransferTransaction) SetTransactionMemo(memo string) *TransferTransaction {
   506  	tx.Transaction.SetTransactionMemo(memo)
   507  	return tx
   508  }
   509  
   510  // SetTransactionValidDuration sets the valid duration for this TransferTransaction.
   511  func (tx *TransferTransaction) SetTransactionValidDuration(duration time.Duration) *TransferTransaction {
   512  	tx.Transaction.SetTransactionValidDuration(duration)
   513  	return tx
   514  }
   515  
   516  // ToBytes serialise the tx to bytes, no matter if it is signed (locked), or not
   517  func (tx *TransferTransaction) ToBytes() ([]byte, error) {
   518  	bytes, err := tx.Transaction.toBytes(tx)
   519  	if err != nil {
   520  		return nil, err
   521  	}
   522  	return bytes, nil
   523  }
   524  
   525  // SetTransactionID sets the TransactionID for this TransferTransaction.
   526  func (tx *TransferTransaction) SetTransactionID(transactionID TransactionID) *TransferTransaction {
   527  	tx.Transaction.SetTransactionID(transactionID)
   528  	return tx
   529  }
   530  
   531  // SetNodeAccountIDs sets the _Node AccountID for this TransferTransaction.
   532  func (tx *TransferTransaction) SetNodeAccountIDs(nodeID []AccountID) *TransferTransaction {
   533  	tx.Transaction.SetNodeAccountIDs(nodeID)
   534  	return tx
   535  }
   536  
   537  // SetMaxRetry sets the max number of errors before execution will fail.
   538  func (tx *TransferTransaction) SetMaxRetry(count int) *TransferTransaction {
   539  	tx.Transaction.SetMaxRetry(count)
   540  	return tx
   541  }
   542  
   543  // SetMaxBackoff The maximum amount of time to wait between retries.
   544  // Every retry attempt will increase the wait time exponentially until it reaches this time.
   545  func (tx *TransferTransaction) SetMaxBackoff(max time.Duration) *TransferTransaction {
   546  	tx.Transaction.SetMaxBackoff(max)
   547  	return tx
   548  }
   549  
   550  // SetMinBackoff sets the minimum amount of time to wait between retries.
   551  func (tx *TransferTransaction) SetMinBackoff(min time.Duration) *TransferTransaction {
   552  	tx.Transaction.SetMinBackoff(min)
   553  	return tx
   554  }
   555  
   556  func (tx *TransferTransaction) SetLogLevel(level LogLevel) *TransferTransaction {
   557  	tx.Transaction.SetLogLevel(level)
   558  	return tx
   559  }
   560  
   561  func (tx *TransferTransaction) Execute(client *Client) (TransactionResponse, error) {
   562  	return tx.Transaction.execute(client, tx)
   563  }
   564  
   565  func (tx *TransferTransaction) Schedule() (*ScheduleCreateTransaction, error) {
   566  	return tx.Transaction.schedule(tx)
   567  }
   568  
   569  // ----------- Overridden functions ----------------
   570  
   571  func (tx *TransferTransaction) getName() string {
   572  	return "TransferTransaction"
   573  }
   574  
   575  func (tx *TransferTransaction) validateNetworkOnIDs(client *Client) error {
   576  	if client == nil || !client.autoValidateChecksums {
   577  		return nil
   578  	}
   579  	var err error
   580  	for token, tokenTransfer := range tx.tokenTransfers {
   581  		err = token.ValidateChecksum(client)
   582  		if err != nil {
   583  			return err
   584  		}
   585  		for _, transfer := range tokenTransfer.Transfers {
   586  			err = transfer.accountID.ValidateChecksum(client)
   587  			if err != nil {
   588  				return err
   589  			}
   590  		}
   591  		if err != nil {
   592  			return err
   593  		}
   594  	}
   595  	for token, nftTransfers := range tx.nftTransfers {
   596  		err = token.ValidateChecksum(client)
   597  		if err != nil {
   598  			return err
   599  		}
   600  		for _, nftTransfer := range nftTransfers {
   601  			err = nftTransfer.SenderAccountID.ValidateChecksum(client)
   602  			if err != nil {
   603  				return err
   604  			}
   605  			err = nftTransfer.ReceiverAccountID.ValidateChecksum(client)
   606  			if err != nil {
   607  				return err
   608  			}
   609  		}
   610  	}
   611  	for _, hbarTransfer := range tx.hbarTransfers {
   612  		err = hbarTransfer.accountID.ValidateChecksum(client)
   613  		if err != nil {
   614  			return err
   615  		}
   616  	}
   617  
   618  	return nil
   619  }
   620  
   621  func (tx *TransferTransaction) build() *services.TransactionBody {
   622  	return &services.TransactionBody{
   623  		TransactionFee:           tx.transactionFee,
   624  		Memo:                     tx.Transaction.memo,
   625  		TransactionValidDuration: _DurationToProtobuf(tx.GetTransactionValidDuration()),
   626  		TransactionID:            tx.transactionID._ToProtobuf(),
   627  		Data: &services.TransactionBody_CryptoTransfer{
   628  			CryptoTransfer: tx.buildProtoBody(),
   629  		},
   630  	}
   631  }
   632  
   633  func (tx *TransferTransaction) buildScheduled() (*services.SchedulableTransactionBody, error) {
   634  	return &services.SchedulableTransactionBody{
   635  		TransactionFee: tx.transactionFee,
   636  		Memo:           tx.Transaction.memo,
   637  		Data: &services.SchedulableTransactionBody_CryptoTransfer{
   638  			CryptoTransfer: tx.buildProtoBody(),
   639  		},
   640  	}, nil
   641  }
   642  
   643  func (tx *TransferTransaction) buildProtoBody() *services.CryptoTransferTransactionBody {
   644  	body := &services.CryptoTransferTransactionBody{
   645  		Transfers: &services.TransferList{
   646  			AccountAmounts: []*services.AccountAmount{},
   647  		},
   648  		TokenTransfers: []*services.TokenTransferList{},
   649  	}
   650  
   651  	if len(tx.hbarTransfers) > 0 {
   652  		body.Transfers.AccountAmounts = make([]*services.AccountAmount, 0)
   653  		for _, hbarTransfer := range tx.hbarTransfers {
   654  			body.Transfers.AccountAmounts = append(body.Transfers.AccountAmounts, &services.AccountAmount{
   655  				AccountID:  hbarTransfer.accountID._ToProtobuf(),
   656  				Amount:     hbarTransfer.Amount.AsTinybar(),
   657  				IsApproval: hbarTransfer.IsApproved,
   658  			})
   659  		}
   660  	}
   661  
   662  	if len(tx.tokenTransfers) > 0 {
   663  		if body.TokenTransfers == nil {
   664  			body.TokenTransfers = make([]*services.TokenTransferList, 0)
   665  		}
   666  
   667  		for tokenID := range tx.tokenTransfers {
   668  			transfers := tx.tokenTransfers[tokenID]._ToProtobuf()
   669  
   670  			bod := &services.TokenTransferList{
   671  				Token:     tokenID._ToProtobuf(),
   672  				Transfers: transfers,
   673  			}
   674  
   675  			if tx.tokenTransfers[tokenID].ExpectedDecimals != nil {
   676  				bod.ExpectedDecimals = &wrapperspb.UInt32Value{Value: *tx.tokenTransfers[tokenID].ExpectedDecimals}
   677  			}
   678  
   679  			body.TokenTransfers = append(body.TokenTransfers, bod)
   680  		}
   681  	}
   682  
   683  	if len(tx.nftTransfers) > 0 {
   684  		if body.TokenTransfers == nil {
   685  			body.TokenTransfers = make([]*services.TokenTransferList, 0)
   686  		}
   687  
   688  		for tokenID, nftTransferList := range tx.nftTransfers {
   689  			nftTransfers := make([]*services.NftTransfer, 0)
   690  
   691  			for _, nftT := range nftTransferList {
   692  				nftTransfers = append(nftTransfers, nftT._ToProtobuf())
   693  			}
   694  
   695  			body.TokenTransfers = append(body.TokenTransfers, &services.TokenTransferList{
   696  				Token:        tokenID._ToProtobuf(),
   697  				NftTransfers: nftTransfers,
   698  			})
   699  		}
   700  	}
   701  
   702  	return body
   703  }
   704  
   705  func (tx *TransferTransaction) getMethod(channel *_Channel) _Method {
   706  	return _Method{
   707  		transaction: channel._GetCrypto().CryptoTransfer,
   708  	}
   709  }
   710  
   711  func (this *TransferTransaction) _ConstructScheduleProtobuf() (*services.SchedulableTransactionBody, error) {
   712  	return this.buildScheduled()
   713  }