github.com/hashgraph/hedera-sdk-go/v2@v2.48.0/token_airdrop_transaction_e2e_test.go (about) 1 //go:build all || e2e 2 // +build all e2e 3 4 package hedera 5 6 /*- 7 * 8 * Hedera Go SDK 9 * 10 * Copyright (C) 2020 - 2024 Hedera Hashgraph, LLC 11 * 12 * Licensed under the Apache License, Version 2.0 (the "License"); 13 * you may not use this file except in compliance with the License. 14 * You may obtain a copy of the License at 15 * 16 * http://www.apache.org/licenses/LICENSE-2.0 17 * 18 * Unless required by applicable law or agreed to in writing, software 19 * distributed under the License is distributed on an "AS IS" BASIS, 20 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 * See the License for the specific language governing permissions and 22 * limitations under the License. 23 * 24 */ 25 26 import ( 27 "testing" 28 29 "github.com/stretchr/testify/assert" 30 "github.com/stretchr/testify/require" 31 ) 32 33 func TestIntegrationTokenAirdropTransactionTransfersTokensWhenAssociated(t *testing.T) { 34 env := NewIntegrationTestEnv(t) 35 defer CloseIntegrationTestEnv(env, nil) 36 37 // Create ft and nft 38 tokenID, err := createFungibleToken(&env) 39 require.NoError(t, err) 40 nftID, err := createNft(&env) 41 require.NoError(t, err) 42 43 // Mint some NFTs 44 txResponse, err := NewTokenMintTransaction(). 45 SetTokenID(nftID). 46 SetMetadatas(mintMetadata). 47 Execute(env.Client) 48 require.NoError(t, err) 49 50 receipt, err := txResponse.SetValidateStatus(true).GetReceipt(env.Client) 51 require.NoError(t, err) 52 53 nftSerials := receipt.SerialNumbers 54 55 // Create receiver with unlimited auto associations and receiverSig = false 56 receiver, _, err := createAccount(&env, func(tx *AccountCreateTransaction) { 57 tx.SetMaxAutomaticTokenAssociations(-1) 58 }) 59 require.NoError(t, err) 60 61 // Airdrop the tokens 62 airdropTx, err := NewTokenAirdropTransaction(). 63 AddNftTransfer(nftID.Nft(nftSerials[0]), env.OperatorID, receiver). 64 AddNftTransfer(nftID.Nft(nftSerials[1]), env.OperatorID, receiver). 65 AddTokenTransfer(tokenID, receiver, 100). 66 AddTokenTransfer(tokenID, env.OperatorID, -100). 67 Execute(env.Client) 68 require.NoError(t, err) 69 70 _, err = airdropTx.SetValidateStatus(true).GetReceipt(env.Client) 71 require.NoError(t, err) 72 73 // Verify the receiver holds the tokens via query 74 receiverAccountBalance, err := NewAccountBalanceQuery(). 75 SetAccountID(receiver). 76 Execute(env.Client) 77 require.NoError(t, err) 78 assert.Equal(t, uint64(100), receiverAccountBalance.Tokens.Get(tokenID)) 79 assert.Equal(t, uint64(2), receiverAccountBalance.Tokens.Get(nftID)) 80 81 // Verify the operator does not hold the tokens 82 operatorBalance, err := NewAccountBalanceQuery(). 83 SetAccountID(env.OperatorID). 84 Execute(env.Client) 85 require.NoError(t, err) 86 assert.Equal(t, uint64(1_000_000-100), operatorBalance.Tokens.Get(tokenID)) 87 assert.Equal(t, uint64(8), operatorBalance.Tokens.Get(nftID)) 88 } 89 func TestIntegrationTokenAirdropTransactionPendingTokensWhenNotAssociated(t *testing.T) { 90 env := NewIntegrationTestEnv(t) 91 defer CloseIntegrationTestEnv(env, nil) 92 93 // Create ft and nft 94 tokenID, err := createFungibleToken(&env) 95 require.NoError(t, err) 96 nftID, err := createNft(&env) 97 require.NoError(t, err) 98 99 // Mint some NFTs 100 txResponse, err := NewTokenMintTransaction(). 101 SetTokenID(nftID). 102 SetMetadatas(mintMetadata). 103 Execute(env.Client) 104 require.NoError(t, err) 105 106 receipt, err := txResponse.SetValidateStatus(true).GetReceipt(env.Client) 107 require.NoError(t, err) 108 109 nftSerials := receipt.SerialNumbers 110 111 // Create receiver 112 receiver, _, err := createAccount(&env) 113 require.NoError(t, err) 114 115 // Airdrop the tokens 116 airdropTx, err := NewTokenAirdropTransaction(). 117 AddNftTransfer(nftID.Nft(nftSerials[0]), env.OperatorID, receiver). 118 AddNftTransfer(nftID.Nft(nftSerials[1]), env.OperatorID, receiver). 119 AddTokenTransfer(tokenID, receiver, 100). 120 AddTokenTransfer(tokenID, env.OperatorID, -100). 121 Execute(env.Client) 122 require.NoError(t, err) 123 124 record, err := airdropTx.SetValidateStatus(true).GetRecord(env.Client) 125 require.NoError(t, err) 126 127 // verify the pending airdrop record 128 assert.Equal(t, 3, len(record.PendingAirdropRecords)) 129 assert.Equal(t, uint64(100), record.PendingAirdropRecords[0].pendingAirdropAmount) 130 assert.Nil(t, record.PendingAirdropRecords[0].pendingAirdropId.nftID) 131 assert.Equal(t, tokenID, *record.PendingAirdropRecords[0].pendingAirdropId.tokenID) 132 133 assert.Equal(t, uint64(0), record.PendingAirdropRecords[1].pendingAirdropAmount) 134 assert.Nil(t, record.PendingAirdropRecords[1].pendingAirdropId.tokenID) 135 assert.Equal(t, nftID.Nft(nftSerials[0]), *record.PendingAirdropRecords[1].pendingAirdropId.nftID) 136 137 assert.Equal(t, uint64(0), record.PendingAirdropRecords[2].pendingAirdropAmount) 138 assert.Nil(t, record.PendingAirdropRecords[2].pendingAirdropId.tokenID) 139 assert.Equal(t, nftID.Nft(nftSerials[1]), *record.PendingAirdropRecords[2].pendingAirdropId.nftID) 140 141 // Verify the receiver does not hold the tokens via query 142 receiverAccountBalance, err := NewAccountBalanceQuery(). 143 SetAccountID(receiver). 144 Execute(env.Client) 145 require.NoError(t, err) 146 assert.Equal(t, uint64(0), receiverAccountBalance.Tokens.Get(tokenID)) 147 assert.Equal(t, uint64(0), receiverAccountBalance.Tokens.Get(nftID)) 148 149 // Verify the operator does hold the tokens 150 operatorBalance, err := NewAccountBalanceQuery(). 151 SetAccountID(env.OperatorID). 152 Execute(env.Client) 153 require.NoError(t, err) 154 assert.Equal(t, uint64(1_000_000), operatorBalance.Tokens.Get(tokenID)) 155 assert.Equal(t, uint64(10), operatorBalance.Tokens.Get(nftID)) 156 } 157 158 func TestIntegrationTokenAirdropTransactionCreatesHollowAccount(t *testing.T) { 159 env := NewIntegrationTestEnv(t) 160 defer CloseIntegrationTestEnv(env, nil) 161 162 // Create ft and nft 163 tokenID, err := createFungibleToken(&env) 164 require.NoError(t, err) 165 nftID, err := createNft(&env) 166 require.NoError(t, err) 167 168 // Mint some NFTs 169 txResponse, err := NewTokenMintTransaction(). 170 SetTokenID(nftID). 171 SetMetadatas(mintMetadata). 172 Execute(env.Client) 173 require.NoError(t, err) 174 175 receipt, err := txResponse.SetValidateStatus(true).GetReceipt(env.Client) 176 require.NoError(t, err) 177 178 nftSerials := receipt.SerialNumbers 179 180 // Create a ECDSA private key 181 privateKey, err := PrivateKeyGenerateEcdsa() 182 if err != nil { 183 println(err.Error()) 184 } 185 // Extract the ECDSA public key public key 186 publicKey := privateKey.PublicKey() 187 188 aliasAccountId := publicKey.ToAccountID(0, 0) 189 190 // should lazy-create and transfer the tokens 191 airdropTx, err := NewTokenAirdropTransaction(). 192 AddNftTransfer(nftID.Nft(nftSerials[0]), env.OperatorID, *aliasAccountId). 193 AddNftTransfer(nftID.Nft(nftSerials[1]), env.OperatorID, *aliasAccountId). 194 AddTokenTransfer(tokenID, *aliasAccountId, 100). 195 AddTokenTransfer(tokenID, env.OperatorID, -100). 196 Execute(env.Client) 197 require.NoError(t, err) 198 199 _, err = airdropTx.SetValidateStatus(true).GetRecord(env.Client) 200 require.NoError(t, err) 201 202 // Verify the receiver holds the tokens via query 203 receiverAccountBalance, err := NewAccountBalanceQuery(). 204 SetAccountID(*aliasAccountId). 205 Execute(env.Client) 206 require.NoError(t, err) 207 assert.Equal(t, uint64(100), receiverAccountBalance.Tokens.Get(tokenID)) 208 assert.Equal(t, uint64(2), receiverAccountBalance.Tokens.Get(nftID)) 209 210 // Verify the operator does not hold the tokens 211 operatorBalance, err := NewAccountBalanceQuery(). 212 SetAccountID(env.OperatorID). 213 Execute(env.Client) 214 require.NoError(t, err) 215 assert.Equal(t, uint64(1_000_000-100), operatorBalance.Tokens.Get(tokenID)) 216 assert.Equal(t, uint64(8), operatorBalance.Tokens.Get(nftID)) 217 } 218 219 func TestIntegrationTokenAirdropTransactionWithCustomFees(t *testing.T) { 220 env := NewIntegrationTestEnv(t) 221 defer CloseIntegrationTestEnv(env, nil) 222 223 // Create receiver with unlimited auto associations and receiverSig = false 224 receiver, _, err := createAccount(&env, func(tx *AccountCreateTransaction) { 225 tx.SetMaxAutomaticTokenAssociations(-1) 226 }) 227 require.NoError(t, err) 228 229 // create fungible token with custom fee another token 230 customFeeTokenID, err := createFungibleToken(&env) 231 require.NoError(t, err) 232 233 // make the custom fee to be paid by the sender and the fee collector to be the operator account 234 fee := NewCustomFixedFee(). 235 SetFeeCollectorAccountID(env.OperatorID). 236 SetDenominatingTokenID(customFeeTokenID). 237 SetAmount(1). 238 SetAllCollectorsAreExempt(true) 239 240 txResponse, err := NewTokenCreateTransaction(). 241 SetTokenName("Test Fungible Token"). 242 SetTokenSymbol("TFT"). 243 SetTokenMemo("I was created for integration tests"). 244 SetDecimals(3). 245 SetInitialSupply(1_000_000). 246 SetMaxSupply(1_000_000). 247 SetTreasuryAccountID(env.OperatorID). 248 SetSupplyType(TokenSupplyTypeFinite). 249 SetAdminKey(env.OperatorKey). 250 SetFreezeKey(env.OperatorKey). 251 SetSupplyKey(env.OperatorKey). 252 SetMetadataKey(env.OperatorKey). 253 SetPauseKey(env.OperatorKey). 254 SetCustomFees([]Fee{fee}). 255 Execute(env.Client) 256 257 require.NoError(t, err) 258 259 receipt, err := txResponse.SetValidateStatus(true).GetReceipt(env.Client) 260 tokenID := receipt.TokenID 261 262 // create sender account with unlimited associations and send some tokens to it 263 sender, senderKey, err := createAccount(&env, func(tx *AccountCreateTransaction) { 264 tx.SetMaxAutomaticTokenAssociations(-1) 265 }) 266 require.NoError(t, err) 267 268 // associate the token to the sender 269 frozenTxn, err := NewTokenAssociateTransaction(). 270 SetAccountID(sender). 271 AddTokenID(*tokenID). 272 FreezeWith(env.Client) 273 require.NoError(t, err) 274 txResponse, err = frozenTxn.Sign(senderKey).Execute(env.Client) 275 require.NoError(t, err) 276 _, err = txResponse.SetValidateStatus(true).GetReceipt(env.Client) 277 require.NoError(t, err) 278 279 // send tokens to the sender 280 txResponse, err = NewTransferTransaction(). 281 AddTokenTransfer(customFeeTokenID, sender, 100). 282 AddTokenTransfer(customFeeTokenID, env.OperatorID, -100). 283 AddTokenTransfer(*tokenID, sender, 100). 284 AddTokenTransfer(*tokenID, env.OperatorID, -100). 285 Execute(env.Client) 286 require.NoError(t, err) 287 _, err = txResponse.SetValidateStatus(true).GetReceipt(env.Client) 288 require.NoError(t, err) 289 290 // airdrop the tokens from the sender to the receiver 291 frozenTx, err := NewTokenAirdropTransaction(). 292 AddTokenTransfer(*tokenID, receiver, 100). 293 AddTokenTransfer(*tokenID, sender, -100). 294 FreezeWith(env.Client) 295 require.NoError(t, err) 296 airdropTx, err := frozenTx. 297 Sign(senderKey). 298 Execute(env.Client) 299 require.NoError(t, err) 300 _, err = airdropTx.SetValidateStatus(true).GetRecord(env.Client) 301 require.NoError(t, err) 302 303 // verify the custom fee has been paid by the sender to the collector 304 receiverAccountBalance, err := NewAccountBalanceQuery(). 305 SetAccountID(receiver). 306 Execute(env.Client) 307 require.NoError(t, err) 308 assert.Equal(t, uint64(100), receiverAccountBalance.Tokens.Get(*tokenID)) 309 310 senderAccountBalance, err := NewAccountBalanceQuery(). 311 SetAccountID(sender). 312 Execute(env.Client) 313 require.NoError(t, err) 314 assert.Equal(t, uint64(0), senderAccountBalance.Tokens.Get(*tokenID)) 315 assert.Equal(t, uint64(99), senderAccountBalance.Tokens.Get(customFeeTokenID)) 316 317 operatorBalance, err := NewAccountBalanceQuery(). 318 SetAccountID(env.OperatorID). 319 Execute(env.Client) 320 require.NoError(t, err) 321 assert.Equal(t, uint64(1_000_000-100), operatorBalance.Tokens.Get(*tokenID)) 322 assert.Equal(t, uint64(1_000_000-100+1), operatorBalance.Tokens.Get(customFeeTokenID)) 323 } 324 func TestIntegrationTokenAirdropTransactionWithReceiverSigTrue(t *testing.T) { 325 env := NewIntegrationTestEnv(t) 326 defer CloseIntegrationTestEnv(env, nil) 327 328 // create fungible token 329 tokenID, err := createFungibleToken(&env) 330 require.NoError(t, err) 331 332 // create nft 333 nftID, err := createNft(&env) 334 require.NoError(t, err) 335 336 // Mint some NFTs 337 txResponse, err := NewTokenMintTransaction(). 338 SetTokenID(nftID). 339 SetMetadatas(mintMetadata). 340 Execute(env.Client) 341 require.NoError(t, err) 342 343 receipt, err := txResponse.SetValidateStatus(true).GetReceipt(env.Client) 344 require.NoError(t, err) 345 346 nftSerials := receipt.SerialNumbers 347 348 // create receiver with unlimited auto associations and receiverSig = true 349 newKey, err := PrivateKeyGenerateEd25519() 350 require.NoError(t, err) 351 352 accountCreateFrozen, err := NewAccountCreateTransaction(). 353 SetKey(newKey). 354 SetInitialBalance(NewHbar(3)). 355 SetReceiverSignatureRequired(true). 356 SetMaxAutomaticTokenAssociations(-1). 357 FreezeWith(env.Client) 358 require.NoError(t, err) 359 accountCreate, err := accountCreateFrozen.Sign(newKey).Execute(env.Client) 360 require.NoError(t, err) 361 receipt, err = accountCreate.SetValidateStatus(true).GetReceipt(env.Client) 362 require.NoError(t, err) 363 receiver := *receipt.AccountID 364 365 // airdrop the tokens 366 airdropTx, err := NewTokenAirdropTransaction(). 367 AddNftTransfer(nftID.Nft(nftSerials[0]), env.OperatorID, receiver). 368 AddNftTransfer(nftID.Nft(nftSerials[1]), env.OperatorID, receiver). 369 AddTokenTransfer(tokenID, receiver, 100). 370 AddTokenTransfer(tokenID, env.OperatorID, -100). 371 Execute(env.Client) 372 require.NoError(t, err) 373 _, err = airdropTx.SetValidateStatus(true).GetReceipt(env.Client) 374 require.NoError(t, err) 375 } 376 377 func TestIntegrationTokenAirdropTransactionWithNoBalanceFT(t *testing.T) { 378 env := NewIntegrationTestEnv(t) 379 defer CloseIntegrationTestEnv(env, nil) 380 381 // create fungible token 382 tokenID, _ := createFungibleToken(&env) 383 384 // create spender and approve to it some tokens 385 spender, spenderKey, err := createAccount(&env, func(tx *AccountCreateTransaction) { 386 tx.SetMaxAutomaticTokenAssociations(-1) 387 }) 388 require.NoError(t, err) 389 390 // create sender 391 sender, senderKey, err := createAccount(&env, func(tx *AccountCreateTransaction) { 392 tx.SetMaxAutomaticTokenAssociations(-1) 393 }) 394 require.NoError(t, err) 395 396 // transfer ft to sender 397 txResponse, err := NewTransferTransaction(). 398 AddTokenTransfer(tokenID, sender, 100). 399 AddTokenTransfer(tokenID, env.OperatorID, -100). 400 Execute(env.Client) 401 require.NoError(t, err) 402 _, err = txResponse.SetValidateStatus(true).GetReceipt(env.Client) 403 require.NoError(t, err) 404 405 // approve allowance to the spender 406 frozenTx, err := NewAccountAllowanceApproveTransaction(). 407 ApproveTokenAllowance(tokenID, sender, spender, 100). 408 FreezeWith(env.Client) 409 require.NoError(t, err) 410 txResponse, err = frozenTx.Sign(senderKey).Execute(env.Client) 411 require.NoError(t, err) 412 _, err = txResponse.SetValidateStatus(true).GetReceipt(env.Client) 413 require.NoError(t, err) 414 415 // airdrop the tokens from the sender to the spender via approval 416 frozenTxn, err := NewTokenAirdropTransaction(). 417 AddTokenTransfer(tokenID, spender, 100). 418 AddApprovedTokenTransfer(tokenID, sender, -100, true). 419 SetTransactionID(TransactionIDGenerate(spender)). 420 FreezeWith(env.Client) 421 require.NoError(t, err) 422 _, err = frozenTxn. 423 Sign(spenderKey). 424 Execute(env.Client) 425 assert.ErrorContains(t, err, "NOT_SUPPORTED") 426 } 427 428 func TestIntegrationTokenAirdropTransactionWithNoBalanceNFT(t *testing.T) { 429 env := NewIntegrationTestEnv(t) 430 defer CloseIntegrationTestEnv(env, nil) 431 432 // create nft 433 nftID, err := createNft(&env) 434 require.NoError(t, err) 435 436 // Mint some NFTs 437 txResponse, err := NewTokenMintTransaction(). 438 SetTokenID(nftID). 439 SetMetadatas(mintMetadata). 440 Execute(env.Client) 441 require.NoError(t, err) 442 443 receipt, err := txResponse.SetValidateStatus(true).GetReceipt(env.Client) 444 require.NoError(t, err) 445 446 nftSerials := receipt.SerialNumbers 447 448 // create spender and approve to it some tokens 449 spender, spenderKey, err := createAccount(&env, func(tx *AccountCreateTransaction) { 450 tx.SetMaxAutomaticTokenAssociations(-1) 451 }) 452 require.NoError(t, err) 453 454 // create sender 455 sender, senderKey, err := createAccount(&env, func(tx *AccountCreateTransaction) { 456 tx.SetMaxAutomaticTokenAssociations(-1) 457 }) 458 require.NoError(t, err) 459 460 // transfer ft to sender 461 txResponse, err = NewTransferTransaction(). 462 AddNftTransfer(nftID.Nft(nftSerials[0]), env.OperatorID, sender). 463 AddNftTransfer(nftID.Nft(nftSerials[1]), env.OperatorID, sender). 464 Execute(env.Client) 465 require.NoError(t, err) 466 _, err = txResponse.SetValidateStatus(true).GetReceipt(env.Client) 467 require.NoError(t, err) 468 469 // approve allowance to the spender 470 frozenTx, err := NewAccountAllowanceApproveTransaction(). 471 ApproveTokenNftAllowance(nftID.Nft(nftSerials[0]), sender, spender). 472 ApproveTokenNftAllowance(nftID.Nft(nftSerials[1]), sender, spender). 473 FreezeWith(env.Client) 474 require.NoError(t, err) 475 txResponse, err = frozenTx.Sign(senderKey).Execute(env.Client) 476 require.NoError(t, err) 477 _, err = txResponse.SetValidateStatus(true).GetReceipt(env.Client) 478 require.NoError(t, err) 479 480 // airdrop the tokens from the sender to the spender via approval 481 frozenTxn, err := NewTokenAirdropTransaction(). 482 AddApprovedNftTransfer(nftID.Nft(nftSerials[0]), sender, spender, true). 483 AddApprovedNftTransfer(nftID.Nft(nftSerials[1]), sender, spender, true). 484 SetTransactionID(TransactionIDGenerate(spender)). 485 FreezeWith(env.Client) 486 require.NoError(t, err) 487 _, err = frozenTxn. 488 Sign(spenderKey). 489 Execute(env.Client) 490 assert.ErrorContains(t, err, "NOT_SUPPORTED") 491 } 492 493 func TestIntegrationTokenAirdropTransactionWithInvalidBody(t *testing.T) { 494 env := NewIntegrationTestEnv(t) 495 defer CloseIntegrationTestEnv(env, nil) 496 497 // create fungible token 498 tokenID, _ := createFungibleToken(&env) 499 500 // create receiver 501 receiver, _, err := createAccount(&env, func(tx *AccountCreateTransaction) { 502 tx.SetMaxAutomaticTokenAssociations(-1) 503 }) 504 require.NoError(t, err) 505 506 _, err = NewTokenAirdropTransaction(). 507 Execute(env.Client) 508 require.ErrorContains(t, err, "EMPTY_TOKEN_TRANSFER_BODY") 509 510 _, err = NewTokenAirdropTransaction(). 511 AddTokenTransfer(tokenID, receiver, 100). 512 AddTokenTransfer(tokenID, receiver, 100). 513 Execute(env.Client) 514 require.ErrorContains(t, err, "INVALID_TRANSACTION_BODY") 515 }