github.com/hashgraph/hedera-sdk-go/v2@v2.48.0/token_airdrop_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 type TokenAirdropTransaction struct { 32 Transaction 33 tokenTransfers map[TokenID]*_TokenTransfer 34 nftTransfers map[TokenID][]*_TokenNftTransfer 35 } 36 37 func NewTokenAirdropTransaction() *TokenAirdropTransaction { 38 tx := TokenAirdropTransaction{ 39 Transaction: _NewTransaction(), 40 tokenTransfers: make(map[TokenID]*_TokenTransfer), 41 nftTransfers: make(map[TokenID][]*_TokenNftTransfer), 42 } 43 44 tx._SetDefaultMaxTransactionFee(NewHbar(1)) 45 46 return &tx 47 } 48 49 func _TokenAirdropTransactionFromProtobuf(tx Transaction, pb *services.TransactionBody) *TokenAirdropTransaction { 50 tokenTransfers := make(map[TokenID]*_TokenTransfer) 51 nftTransfers := make(map[TokenID][]*_TokenNftTransfer) 52 53 for _, tokenTransfersList := range pb.GetTokenAirdrop().GetTokenTransfers() { 54 tok := _TokenIDFromProtobuf(tokenTransfersList.Token) 55 tokenTransfers[*tok] = _TokenTransferPrivateFromProtobuf(tokenTransfersList) 56 } 57 58 for _, tokenTransfersList := range pb.GetTokenAirdrop().GetTokenTransfers() { 59 if tokenID := _TokenIDFromProtobuf(tokenTransfersList.Token); tokenID != nil { 60 for _, aa := range tokenTransfersList.GetNftTransfers() { 61 if nftTransfers[*tokenID] == nil { 62 nftTransfers[*tokenID] = make([]*_TokenNftTransfer, 0) 63 } 64 nftTransfer := _NftTransferFromProtobuf(aa) 65 nftTransfers[*tokenID] = append(nftTransfers[*tokenID], &nftTransfer) 66 } 67 } 68 } 69 70 return &TokenAirdropTransaction{ 71 Transaction: tx, 72 tokenTransfers: tokenTransfers, 73 nftTransfers: nftTransfers, 74 } 75 } 76 77 // SetTokenTransferApproval Sets the desired token unit balance adjustments 78 func (tx *TokenAirdropTransaction) SetTokenTransferApproval(tokenID TokenID, accountID AccountID, approval bool) *TokenAirdropTransaction { //nolint 79 for token, tokenTransfer := range tx.tokenTransfers { 80 if token.equals(tokenID) { 81 for _, transfer := range tokenTransfer.Transfers { 82 if transfer.accountID._Equals(accountID) { 83 transfer.IsApproved = approval 84 } 85 } 86 } 87 } 88 89 return tx 90 } 91 92 // SetNftTransferApproval Sets the desired nft token unit balance adjustments 93 func (tx *TokenAirdropTransaction) SetNftTransferApproval(nftID NftID, approval bool) *TokenAirdropTransaction { 94 for token, nftTransfers := range tx.nftTransfers { 95 if token.equals(nftID.TokenID) { 96 for _, nftTransfer := range nftTransfers { 97 if nftTransfer.SerialNumber == nftID.SerialNumber { 98 nftTransfer.IsApproved = approval 99 } 100 } 101 } 102 } 103 return tx 104 } 105 106 // GetNftTransfers returns the nft transfers 107 func (tx *TokenAirdropTransaction) GetNftTransfers() map[TokenID][]_TokenNftTransfer { 108 nftResult := make(map[TokenID][]_TokenNftTransfer) 109 for token, nftTransfers := range tx.nftTransfers { 110 tempArray := make([]_TokenNftTransfer, 0) 111 for _, nftTransfer := range nftTransfers { 112 tempArray = append(tempArray, *nftTransfer) 113 } 114 115 nftResult[token] = tempArray 116 } 117 118 return nftResult 119 } 120 121 // GetTokenTransfers returns the token transfers 122 func (tx *TokenAirdropTransaction) GetTokenTransfers() map[TokenID][]TokenTransfer { 123 transfers := make(map[TokenID][]TokenTransfer) 124 for tokenID, tokenTransfers := range tx.tokenTransfers { 125 tokenTransfersList := make([]TokenTransfer, 0) 126 127 for _, transfer := range tokenTransfers.Transfers { 128 var acc AccountID 129 if transfer.accountID != nil { 130 acc = *transfer.accountID 131 } 132 tokenTransfersList = append(tokenTransfersList, TokenTransfer{ 133 AccountID: acc, 134 Amount: transfer.Amount.AsTinybar(), 135 IsApproved: transfer.IsApproved, 136 }) 137 } 138 139 tempTokenTransferList := _TokenTransfers{tokenTransfersList} 140 141 transfers[tokenID] = tempTokenTransferList.transfers 142 } 143 144 return transfers 145 } 146 147 // GetTokenIDDecimals returns the token decimals 148 func (tx *TokenAirdropTransaction) GetTokenIDDecimals() map[TokenID]uint32 { 149 result := make(map[TokenID]uint32) 150 for token, tokenTransfer := range tx.tokenTransfers { 151 if tokenTransfer.ExpectedDecimals != nil { 152 result[token] = *tokenTransfer.ExpectedDecimals 153 } 154 } 155 return result 156 } 157 158 // AddTokenTransferWithDecimals Sets the desired token unit balance adjustments with decimals 159 func (tx *TokenAirdropTransaction) AddTokenTransferWithDecimals(tokenID TokenID, accountID AccountID, value int64, decimal uint32) *TokenAirdropTransaction { //nolint 160 tx._RequireNotFrozen() 161 162 for token, tokenTransfer := range tx.tokenTransfers { 163 if token.equals(tokenID) { 164 for _, transfer := range tokenTransfer.Transfers { 165 if transfer.accountID._Equals(accountID) { 166 transfer.Amount = HbarFromTinybar(transfer.Amount.AsTinybar() + value) 167 tokenTransfer.ExpectedDecimals = &decimal 168 169 return tx 170 } 171 } 172 } 173 } 174 175 if v, ok := tx.tokenTransfers[tokenID]; ok { 176 v.Transfers = append(v.Transfers, &_HbarTransfer{ 177 accountID: &accountID, 178 Amount: HbarFromTinybar(value), 179 IsApproved: false, 180 }) 181 v.ExpectedDecimals = &decimal 182 183 return tx 184 } 185 186 tx.tokenTransfers[tokenID] = &_TokenTransfer{ 187 Transfers: []*_HbarTransfer{{ 188 accountID: &accountID, 189 Amount: HbarFromTinybar(value), 190 IsApproved: false, 191 }}, 192 ExpectedDecimals: &decimal, 193 } 194 195 return tx 196 } 197 198 // AddTokenTransfer Sets the desired token unit balance adjustments 199 // Applicable to tokens of type FUNGIBLE_COMMON. 200 func (tx *TokenAirdropTransaction) AddTokenTransfer(tokenID TokenID, accountID AccountID, value int64) *TokenAirdropTransaction { //nolint 201 tx._RequireNotFrozen() 202 203 for token, tokenTransfer := range tx.tokenTransfers { 204 if token.equals(tokenID) { 205 for _, transfer := range tokenTransfer.Transfers { 206 if transfer.accountID._Equals(accountID) { 207 transfer.Amount = HbarFromTinybar(transfer.Amount.AsTinybar() + value) 208 209 return tx 210 } 211 } 212 } 213 } 214 215 if v, ok := tx.tokenTransfers[tokenID]; ok { 216 v.Transfers = append(v.Transfers, &_HbarTransfer{ 217 accountID: &accountID, 218 Amount: HbarFromTinybar(value), 219 IsApproved: false, 220 }) 221 222 return tx 223 } 224 225 tx.tokenTransfers[tokenID] = &_TokenTransfer{ 226 Transfers: []*_HbarTransfer{{ 227 accountID: &accountID, 228 Amount: HbarFromTinybar(value), 229 IsApproved: false, 230 }}, 231 } 232 233 return tx 234 } 235 236 // AddNftTransfer Sets the desired nft token unit balance adjustments 237 // Applicable to tokens of type NON_FUNGIBLE_UNIQUE. 238 func (tx *TokenAirdropTransaction) AddNftTransfer(nftID NftID, sender AccountID, receiver AccountID) *TokenAirdropTransaction { 239 tx._RequireNotFrozen() 240 241 if tx.nftTransfers == nil { 242 tx.nftTransfers = make(map[TokenID][]*_TokenNftTransfer) 243 } 244 245 if tx.nftTransfers[nftID.TokenID] == nil { 246 tx.nftTransfers[nftID.TokenID] = make([]*_TokenNftTransfer, 0) 247 } 248 249 tx.nftTransfers[nftID.TokenID] = append(tx.nftTransfers[nftID.TokenID], &_TokenNftTransfer{ 250 SenderAccountID: sender, 251 ReceiverAccountID: receiver, 252 SerialNumber: nftID.SerialNumber, 253 }) 254 255 return tx 256 } 257 258 // AddApprovedTokenTransferWithDecimals adds an approved token transfer with decimals 259 func (tx *TokenAirdropTransaction) AddApprovedTokenTransferWithDecimals(tokenID TokenID, accountID AccountID, value int64, decimal uint32, approve bool) *TokenAirdropTransaction { //nolint 260 tx._RequireNotFrozen() 261 262 for token, tokenTransfer := range tx.tokenTransfers { 263 if token.equals(tokenID) { 264 for _, transfer := range tokenTransfer.Transfers { 265 if transfer.accountID._Equals(accountID) { 266 transfer.Amount = HbarFromTinybar(transfer.Amount.AsTinybar() + value) 267 tokenTransfer.ExpectedDecimals = &decimal 268 for _, transfer := range tokenTransfer.Transfers { 269 transfer.IsApproved = approve 270 } 271 272 return tx 273 } 274 } 275 } 276 } 277 278 if v, ok := tx.tokenTransfers[tokenID]; ok { 279 v.Transfers = append(v.Transfers, &_HbarTransfer{ 280 accountID: &accountID, 281 Amount: HbarFromTinybar(value), 282 IsApproved: approve, 283 }) 284 v.ExpectedDecimals = &decimal 285 286 return tx 287 } 288 289 tx.tokenTransfers[tokenID] = &_TokenTransfer{ 290 Transfers: []*_HbarTransfer{{ 291 accountID: &accountID, 292 Amount: HbarFromTinybar(value), 293 IsApproved: approve, 294 }}, 295 ExpectedDecimals: &decimal, 296 } 297 298 return tx 299 } 300 301 // AddApprovedTokenTransfer adds an approved token transfer 302 func (tx *TokenAirdropTransaction) AddApprovedTokenTransfer(tokenID TokenID, accountID AccountID, value int64, approve bool) *TokenAirdropTransaction { //nolint 303 tx._RequireNotFrozen() 304 305 for token, tokenTransfer := range tx.tokenTransfers { 306 if token.equals(tokenID) { 307 for _, transfer := range tokenTransfer.Transfers { 308 if transfer.accountID._Equals(accountID) { 309 transfer.Amount = HbarFromTinybar(transfer.Amount.AsTinybar() + value) 310 transfer.IsApproved = approve 311 312 return tx 313 } 314 } 315 } 316 } 317 318 if v, ok := tx.tokenTransfers[tokenID]; ok { 319 v.Transfers = append(v.Transfers, &_HbarTransfer{ 320 accountID: &accountID, 321 Amount: HbarFromTinybar(value), 322 IsApproved: approve, 323 }) 324 325 return tx 326 } 327 328 tx.tokenTransfers[tokenID] = &_TokenTransfer{ 329 Transfers: []*_HbarTransfer{{ 330 accountID: &accountID, 331 Amount: HbarFromTinybar(value), 332 IsApproved: approve, 333 }}, 334 } 335 336 return tx 337 } 338 339 // AddApprovedNftTransfer adds an approved nft transfer 340 func (tx *TokenAirdropTransaction) AddApprovedNftTransfer(nftID NftID, sender AccountID, receiver AccountID, approve bool) *TokenAirdropTransaction { 341 tx._RequireNotFrozen() 342 343 if tx.nftTransfers == nil { 344 tx.nftTransfers = make(map[TokenID][]*_TokenNftTransfer) 345 } 346 347 if tx.nftTransfers[nftID.TokenID] == nil { 348 tx.nftTransfers[nftID.TokenID] = make([]*_TokenNftTransfer, 0) 349 } 350 351 tx.nftTransfers[nftID.TokenID] = append(tx.nftTransfers[nftID.TokenID], &_TokenNftTransfer{ 352 SenderAccountID: sender, 353 ReceiverAccountID: receiver, 354 SerialNumber: nftID.SerialNumber, 355 IsApproved: approve, 356 }) 357 358 return tx 359 } 360 361 // ---- Required Interfaces ---- // 362 363 // Sign uses the provided privateKey to sign the transaction. 364 func (tx *TokenAirdropTransaction) Sign(privateKey PrivateKey) *TokenAirdropTransaction { 365 tx.Transaction.Sign(privateKey) 366 return tx 367 } 368 369 // SignWithOperator signs the transaction with client's operator privateKey. 370 func (tx *TokenAirdropTransaction) SignWithOperator(client *Client) (*TokenAirdropTransaction, error) { 371 _, err := tx.Transaction.signWithOperator(client, tx) 372 if err != nil { 373 return nil, err 374 } 375 return tx, nil 376 } 377 378 // SignWith executes the TransactionSigner and adds the resulting signature data to the Transaction's signature map 379 // with the publicKey as the map key. 380 func (tx *TokenAirdropTransaction) SignWith( 381 publicKey PublicKey, 382 signer TransactionSigner, 383 ) *TokenAirdropTransaction { 384 tx.Transaction.SignWith(publicKey, signer) 385 return tx 386 } 387 388 // AddSignature adds a signature to the transaction. 389 func (tx *TokenAirdropTransaction) AddSignature(publicKey PublicKey, signature []byte) *TokenAirdropTransaction { 390 tx.Transaction.AddSignature(publicKey, signature) 391 return tx 392 } 393 394 // When execution is attempted, a single attempt will timeout when this deadline is reached. (The SDK may subsequently retry the execution.) 395 func (tx *TokenAirdropTransaction) SetGrpcDeadline(deadline *time.Duration) *TokenAirdropTransaction { 396 tx.Transaction.SetGrpcDeadline(deadline) 397 return tx 398 } 399 400 func (tx *TokenAirdropTransaction) Freeze() (*TokenAirdropTransaction, error) { 401 return tx.FreezeWith(nil) 402 } 403 404 func (tx *TokenAirdropTransaction) FreezeWith(client *Client) (*TokenAirdropTransaction, error) { 405 _, err := tx.Transaction.freezeWith(client, tx) 406 return tx, err 407 } 408 409 // SetMaxTransactionFee sets the max transaction fee for this TokenAirdropTransaction. 410 func (tx *TokenAirdropTransaction) SetMaxTransactionFee(fee Hbar) *TokenAirdropTransaction { 411 tx.Transaction.SetMaxTransactionFee(fee) 412 return tx 413 } 414 415 // SetRegenerateTransactionID sets if transaction IDs should be regenerated when `TRANSACTION_EXPIRED` is received 416 func (tx *TokenAirdropTransaction) SetRegenerateTransactionID(regenerateTransactionID bool) *TokenAirdropTransaction { 417 tx.Transaction.SetRegenerateTransactionID(regenerateTransactionID) 418 return tx 419 } 420 421 // SetTransactionMemo sets the memo for this TokenAirdropTransaction. 422 func (tx *TokenAirdropTransaction) SetTransactionMemo(memo string) *TokenAirdropTransaction { 423 tx.Transaction.SetTransactionMemo(memo) 424 return tx 425 } 426 427 // SetTransactionValidDuration sets the valid duration for this TokenAirdropTransaction. 428 func (tx *TokenAirdropTransaction) SetTransactionValidDuration(duration time.Duration) *TokenAirdropTransaction { 429 tx.Transaction.SetTransactionValidDuration(duration) 430 return tx 431 } 432 433 // ToBytes serialise the tx to bytes, no matter if it is signed (locked), or not 434 func (tx *TokenAirdropTransaction) ToBytes() ([]byte, error) { 435 bytes, err := tx.Transaction.toBytes(tx) 436 if err != nil { 437 return nil, err 438 } 439 return bytes, nil 440 } 441 442 // SetTransactionID sets the TransactionID for this TokenAirdropTransaction. 443 func (tx *TokenAirdropTransaction) SetTransactionID(transactionID TransactionID) *TokenAirdropTransaction { 444 tx.Transaction.SetTransactionID(transactionID) 445 return tx 446 } 447 448 // SetNodeAccountIDs sets the _Node AccountID for this TokenAirdropTransaction. 449 func (tx *TokenAirdropTransaction) SetNodeAccountIDs(nodeID []AccountID) *TokenAirdropTransaction { 450 tx.Transaction.SetNodeAccountIDs(nodeID) 451 return tx 452 } 453 454 // SetMaxRetry sets the max number of errors before execution will fail. 455 func (tx *TokenAirdropTransaction) SetMaxRetry(count int) *TokenAirdropTransaction { 456 tx.Transaction.SetMaxRetry(count) 457 return tx 458 } 459 460 // SetMaxBackoff The maximum amount of time to wait between retries. 461 // Every retry attempt will increase the wait time exponentially until it reaches this time. 462 func (tx *TokenAirdropTransaction) SetMaxBackoff(max time.Duration) *TokenAirdropTransaction { 463 tx.Transaction.SetMaxBackoff(max) 464 return tx 465 } 466 467 // SetMinBackoff sets the minimum amount of time to wait between retries. 468 func (tx *TokenAirdropTransaction) SetMinBackoff(min time.Duration) *TokenAirdropTransaction { 469 tx.Transaction.SetMinBackoff(min) 470 return tx 471 } 472 473 func (tx *TokenAirdropTransaction) SetLogLevel(level LogLevel) *TokenAirdropTransaction { 474 tx.Transaction.SetLogLevel(level) 475 return tx 476 } 477 478 func (tx *TokenAirdropTransaction) Execute(client *Client) (TransactionResponse, error) { 479 return tx.Transaction.execute(client, tx) 480 } 481 482 func (tx *TokenAirdropTransaction) Schedule() (*ScheduleCreateTransaction, error) { 483 return tx.Transaction.schedule(tx) 484 } 485 486 // ----------- Overridden functions ---------------- 487 488 func (tx *TokenAirdropTransaction) getName() string { 489 return "TokenAirdropTransaction" 490 } 491 492 func (tx *TokenAirdropTransaction) validateNetworkOnIDs(client *Client) error { 493 if client == nil || !client.autoValidateChecksums { 494 return nil 495 } 496 var err error 497 for token, tokenTransfer := range tx.tokenTransfers { 498 err = token.ValidateChecksum(client) 499 if err != nil { 500 return err 501 } 502 for _, transfer := range tokenTransfer.Transfers { 503 err = transfer.accountID.ValidateChecksum(client) 504 if err != nil { 505 return err 506 } 507 } 508 if err != nil { 509 return err 510 } 511 } 512 for token, nftTransfers := range tx.nftTransfers { 513 err = token.ValidateChecksum(client) 514 if err != nil { 515 return err 516 } 517 for _, nftTransfer := range nftTransfers { 518 err = nftTransfer.SenderAccountID.ValidateChecksum(client) 519 if err != nil { 520 return err 521 } 522 err = nftTransfer.ReceiverAccountID.ValidateChecksum(client) 523 if err != nil { 524 return err 525 } 526 } 527 } 528 529 return nil 530 } 531 532 func (tx *TokenAirdropTransaction) build() *services.TransactionBody { 533 return &services.TransactionBody{ 534 TransactionFee: tx.transactionFee, 535 Memo: tx.Transaction.memo, 536 TransactionValidDuration: _DurationToProtobuf(tx.GetTransactionValidDuration()), 537 TransactionID: tx.transactionID._ToProtobuf(), 538 Data: &services.TransactionBody_TokenAirdrop{ 539 TokenAirdrop: tx.buildProtoBody(), 540 }, 541 } 542 } 543 544 func (tx *TokenAirdropTransaction) buildScheduled() (*services.SchedulableTransactionBody, error) { 545 return &services.SchedulableTransactionBody{ 546 TransactionFee: tx.transactionFee, 547 Memo: tx.Transaction.memo, 548 Data: &services.SchedulableTransactionBody_TokenAirdrop{ 549 TokenAirdrop: tx.buildProtoBody(), 550 }, 551 }, nil 552 } 553 554 func (tx *TokenAirdropTransaction) buildProtoBody() *services.TokenAirdropTransactionBody { 555 body := &services.TokenAirdropTransactionBody{ 556 TokenTransfers: []*services.TokenTransferList{}, 557 } 558 559 if len(tx.tokenTransfers) > 0 { 560 if body.TokenTransfers == nil { 561 body.TokenTransfers = make([]*services.TokenTransferList, 0) 562 } 563 564 for tokenID := range tx.tokenTransfers { 565 transfers := tx.tokenTransfers[tokenID]._ToProtobuf() 566 567 bod := &services.TokenTransferList{ 568 Token: tokenID._ToProtobuf(), 569 Transfers: transfers, 570 } 571 572 if tx.tokenTransfers[tokenID].ExpectedDecimals != nil { 573 bod.ExpectedDecimals = &wrapperspb.UInt32Value{Value: *tx.tokenTransfers[tokenID].ExpectedDecimals} 574 } 575 576 body.TokenTransfers = append(body.TokenTransfers, bod) 577 } 578 } 579 580 if len(tx.nftTransfers) > 0 { 581 if body.TokenTransfers == nil { 582 body.TokenTransfers = make([]*services.TokenTransferList, 0) 583 } 584 585 for tokenID, nftTransferList := range tx.nftTransfers { 586 nftTransfers := make([]*services.NftTransfer, 0) 587 588 for _, nftT := range nftTransferList { 589 nftTransfers = append(nftTransfers, nftT._ToProtobuf()) 590 } 591 592 body.TokenTransfers = append(body.TokenTransfers, &services.TokenTransferList{ 593 Token: tokenID._ToProtobuf(), 594 NftTransfers: nftTransfers, 595 }) 596 } 597 } 598 599 return body 600 } 601 602 func (tx *TokenAirdropTransaction) getMethod(channel *_Channel) _Method { 603 return _Method{ 604 transaction: channel._GetToken().AirdropTokens, 605 } 606 } 607 608 func (tx *TokenAirdropTransaction) _ConstructScheduleProtobuf() (*services.SchedulableTransactionBody, error) { 609 return tx.buildScheduled() 610 }