|
15 | 15 | from hiero_sdk_python.hbar import Hbar |
16 | 16 | from hiero_sdk_python.response_code import ResponseCode |
17 | 17 | from hiero_sdk_python.tokens.nft_id import NftId |
18 | | -from hiero_sdk_python.tokens.token_associate_transaction import TokenAssociateTransaction |
| 18 | +from hiero_sdk_python.tokens.token_associate_transaction import ( |
| 19 | + TokenAssociateTransaction, |
| 20 | +) |
19 | 21 | from hiero_sdk_python.tokens.token_mint_transaction import TokenMintTransaction |
20 | 22 | from hiero_sdk_python.transaction.transaction_id import TransactionId |
21 | 23 | from hiero_sdk_python.transaction.transfer_transaction import TransferTransaction |
@@ -61,7 +63,9 @@ def _mint_nft(env, token_id, metadata): |
61 | 63 |
|
62 | 64 |
|
63 | 65 | @pytest.mark.integration |
64 | | -def test_integration_cannot_transfer_on_behalf_of_spender_without_allowance_approval(env): |
| 66 | +def test_integration_cannot_transfer_on_behalf_of_spender_without_allowance_approval( |
| 67 | + env, |
| 68 | +): |
65 | 69 | """Test that a spender cannot transfer NFTs on behalf of account without allowance approval.""" |
66 | 70 | spender_account, receiver_account = _create_spender_and_receiver_accounts(env) |
67 | 71 |
|
@@ -264,7 +268,9 @@ def test_integration_fungible_token_allowance(env): |
264 | 268 |
|
265 | 269 |
|
266 | 270 | @pytest.mark.integration |
267 | | -def test_integration_cant_transfer_on_behalf_of_spender_after_removing_the_allowance_approval(env): |
| 271 | +def test_integration_cant_transfer_on_behalf_of_spender_after_removing_the_allowance_approval( |
| 272 | + env, |
| 273 | +): |
268 | 274 | """Test that a spender cannot transfer NFTs after the allowance approval is removed.""" |
269 | 275 | spender_account, receiver_account = _create_spender_and_receiver_accounts(env) |
270 | 276 |
|
@@ -486,3 +492,58 @@ def test_integration_cannot_send_deleted_token_nft_serials(env): |
486 | 492 | f"Transfer should have failed with SPENDER_DOES_NOT_HAVE_ALLOWANCE" |
487 | 493 | f"status but got: {ResponseCode(transfer_receipt.status).name}" |
488 | 494 | ) |
| 495 | + |
| 496 | + |
| 497 | +@pytest.mark.integration |
| 498 | +def test_integration_can_approve_serial_and_delete_all_serials_in_one_transaction(env): |
| 499 | + """Test that approve-serial + delete-all-serials in one transaction preserves the per-serial allowance.""" |
| 500 | + spender_account, receiver_account = _create_spender_and_receiver_accounts(env) |
| 501 | + |
| 502 | + token_id = create_nft_token(env) |
| 503 | + assert token_id is not None |
| 504 | + |
| 505 | + _associate_token_with_account(env, receiver_account, token_id) |
| 506 | + |
| 507 | + nft_ids = _mint_nft(env, token_id, [b"\x01", b"\x02"]) |
| 508 | + nft1 = nft_ids[0] |
| 509 | + nft2 = nft_ids[1] |
| 510 | + |
| 511 | + # Approve nft1 specifically and delete-all-serials for the same (token, spender) |
| 512 | + # pair in the same transaction. The per-serial approval is preserved because the |
| 513 | + # two operations produce independent NftAllowance entries. |
| 514 | + receipt = ( |
| 515 | + AccountAllowanceApproveTransaction() |
| 516 | + .approve_token_nft_allowance(nft1, env.operator_id, spender_account.id) |
| 517 | + .delete_token_nft_allowance_all_serials(token_id, env.operator_id, spender_account.id) |
| 518 | + .execute(env.client) |
| 519 | + ) |
| 520 | + assert receipt.status == ResponseCode.SUCCESS, ( |
| 521 | + f"Allowance approval failed with status: {ResponseCode(receipt.status).name}" |
| 522 | + ) |
| 523 | + |
| 524 | + # Transfer nft1 (should succeed - per-serial allowance preserved) |
| 525 | + transfer_receipt = ( |
| 526 | + TransferTransaction() |
| 527 | + .set_transaction_id(TransactionId.generate(spender_account.id)) |
| 528 | + .add_approved_nft_transfer(nft1, env.operator_id, receiver_account.id) |
| 529 | + .freeze_with(env.client) |
| 530 | + .sign(spender_account.key) |
| 531 | + .execute(env.client) |
| 532 | + ) |
| 533 | + assert transfer_receipt.status == ResponseCode.SUCCESS, ( |
| 534 | + f"Transfer failed with status: {ResponseCode(transfer_receipt.status).name}" |
| 535 | + ) |
| 536 | + |
| 537 | + # Transfer nft2 (should fail - delete-all-serials revoked any blanket allowance) |
| 538 | + transfer_receipt2 = ( |
| 539 | + TransferTransaction() |
| 540 | + .set_transaction_id(TransactionId.generate(spender_account.id)) |
| 541 | + .add_approved_nft_transfer(nft2, env.operator_id, receiver_account.id) |
| 542 | + .freeze_with(env.client) |
| 543 | + .sign(spender_account.key) |
| 544 | + .execute(env.client) |
| 545 | + ) |
| 546 | + assert transfer_receipt2.status == ResponseCode.SPENDER_DOES_NOT_HAVE_ALLOWANCE, ( |
| 547 | + f"Transfer should have failed with SPENDER_DOES_NOT_HAVE_ALLOWANCE" |
| 548 | + f"status but got: {ResponseCode(transfer_receipt2.status).name}" |
| 549 | + ) |
0 commit comments