Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -68,17 +68,11 @@ def _build_proto_body(self):

Comment thread
aceppaluni marked this conversation as resolved.
Returns:
TokenCancelAirdropTransactionBody: The protobuf body for this transaction.

Raises:
ValueError: If pending airdrops list is invalid.
"""
pending_airdrops_proto: list[basic_types_pb2.PendingAirdropId] = [
pending_airdrop._to_proto() for pending_airdrop in self.pending_airdrops
]

if len(pending_airdrops_proto) < 1 or len(pending_airdrops_proto) > 10:
raise ValueError("Pending airdrops list must contain mininum 1 and maximum 10 pendingAirdrop.")

return token_cancel_airdrop_pb2.TokenCancelAirdropTransactionBody(pending_airdrops=pending_airdrops_proto)

def build_transaction_body(self):
Expand Down
51 changes: 51 additions & 0 deletions tck/handlers/token.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from hiero_sdk_python.tokens.token_airdrop_claim import TokenClaimAirdropTransaction
from hiero_sdk_python.tokens.token_airdrop_pending_id import PendingAirdropId
from hiero_sdk_python.tokens.token_airdrop_transaction import TokenAirdropTransaction
from hiero_sdk_python.tokens.token_airdrop_transaction_cancel import TokenCancelAirdropTransaction
from hiero_sdk_python.tokens.token_associate_transaction import TokenAssociateTransaction
from hiero_sdk_python.tokens.token_burn_transaction import TokenBurnTransaction
from hiero_sdk_python.tokens.token_create_transaction import TokenCreateTransaction
Expand Down Expand Up @@ -47,6 +48,7 @@
AirdropTokenParams,
AssociateTokenParams,
BurnTokenParams,
CancelAirdropParams,
ClaimTokenParams,
CreateTokenParams,
DeleteTokenParams,
Expand All @@ -67,6 +69,7 @@
AirdropTokenResponse,
AssociateTokenResponse,
BurnTokenResponse,
CancelAirdropResponse,
ClaimTokenResponse,
CreateTokenResponse,
CustomFeeResponse,
Expand Down Expand Up @@ -257,6 +260,54 @@ def create_token(params: CreateTokenParams) -> CreateTokenResponse:
)


def _build_cancel_airdrop_transaction(params: CancelAirdropParams) -> TokenCancelAirdropTransaction:
"""Build a TokenCancelAirdropTransaction from TCK params."""
transaction = TokenCancelAirdropTransaction().set_grpc_deadline(DEFAULT_GRPC_TIMEOUT)

if params.pendingAirdrops:
for pending_airdrop in params.pendingAirdrops:
Comment thread
aceppaluni marked this conversation as resolved.
sender_id = AccountId.from_string(pending_airdrop.senderAccountId)
receiver_id = AccountId.from_string(pending_airdrop.receiverAccountId)
token_id = TokenId.from_string(pending_airdrop.tokenId)

if pending_airdrop.serialNumbers:
for serial_number in pending_airdrop.serialNumbers:
nft_id = NftId(token_id, int(serial_number))
transaction.add_pending_airdrop(
PendingAirdropId(
sender_id,
receiver_id,
nft_id=nft_id,
)
)
else:
transaction.add_pending_airdrop(
PendingAirdropId(
sender_id,
receiver_id,
token_id=token_id,
)
)

return transaction
Comment thread
coderabbitai[bot] marked this conversation as resolved.


@rpc_method("cancelAirdrop")
def cancel_airdrop(params: CancelAirdropParams) -> CancelAirdropResponse:
"""Cancel a token airdrop using TCK cancelAirdrop parameters."""
client = get_client(params.sessionId)

transaction = _build_cancel_airdrop_transaction(params)

if params.commonTransactionParams is not None:
params.commonTransactionParams.apply_common_params(transaction, client)

response = transaction.execute(client, wait_for_receipt=False)
receipt: TransactionReceipt = response.get_receipt(client, validate_status=True)

return CancelAirdropResponse(status=ResponseCode(receipt.status).name)


def _build_mint_token_transaction(params: MintTokenParams) -> TokenMintTransaction:
"""Build a TokenMintTransaction from TCK params."""
transaction = TokenMintTransaction().set_grpc_deadline(DEFAULT_GRPC_TIMEOUT)
Expand Down
50 changes: 50 additions & 0 deletions tck/param/token.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,56 @@ def parse_json_params(cls, params: dict) -> ClaimTokenParams:
)


@dataclass
class PendingAirdropParams:
"""Parameters identifying a pending airdrop."""

senderAccountId: str | None = None
Comment thread
aceppaluni marked this conversation as resolved.
receiverAccountId: str | None = None
tokenId: str | None = None
serialNumbers: list[str] | None = None

@classmethod
def parse_json_params(cls, params: dict) -> PendingAirdropParams:
"""Parse JSON parameters into a PendingAirdropParams instance."""
serial_numbers = params.get("serialNumbers")

if serial_numbers is not None and (
not isinstance(serial_numbers, list)
or not all(isinstance(serial_number, str) for serial_number in serial_numbers)
):
raise ValueError("serialNumbers must be a list of strings")

return cls(
senderAccountId=params.get("senderAccountId"),
receiverAccountId=params.get("receiverAccountId"),
tokenId=params.get("tokenId"),
serialNumbers=serial_numbers,
)


@dataclass
class CancelAirdropParams(BaseTransactionParams):
"""Request parameters for the cancelAirdrop endpoint."""

pendingAirdrops: list[PendingAirdropParams] | None = None

@classmethod
def parse_json_params(cls, params: dict) -> CancelAirdropParams:
"""Parse JSON-RPC params into a CancelAirdropParams instance."""
pending_airdrops_raw = params.get("pendingAirdrops")

return cls(
pendingAirdrops=(
[PendingAirdropParams.parse_json_params(p) for p in pending_airdrops_raw]
if pending_airdrops_raw is not None
else None
),
Comment thread
exploreriii marked this conversation as resolved.
sessionId=parse_session_id(params),
commonTransactionParams=parse_common_transaction_params(params),
)


@dataclass
class GetTokenInfoParams(BaseParams):
"""Request parameters for the getTokenInfo endpoint."""
Expand Down
5 changes: 5 additions & 0 deletions tck/response/token.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ class ClaimTokenResponse(StatusOnlyResponse):
"""Response payload for claimToken."""


@dataclass
class CancelAirdropResponse(StatusOnlyResponse):
"""Response payload for cancelAirdrop."""


@dataclass
class CustomFeeResponse:
"""Nested custom fee details for getTokenInfo."""
Expand Down
27 changes: 19 additions & 8 deletions tests/unit/token_airdrop_transaction_cancel_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,20 +92,31 @@ def test_build_transaction_body(mock_account_ids):

def test_transaction_for_invalid_params(mock_account_ids):
"""Test building the token cancel airdrop transaction body with invalid params."""
sender_id, receiver_id, _, token_id, _ = mock_account_ids
sample_pending_airdrop = PendingAirdropId(sender_id=sender_id, receiver_id=receiver_id, token_id=token_id)
sender_id, receiver_id, node_account_id, token_id, _ = mock_account_ids
sample_pending_airdrop = PendingAirdropId(
sender_id=sender_id,
receiver_id=receiver_id,
token_id=token_id,
)

# With empty pending airdrops list
cancel_airdrop_tx_1 = TokenCancelAirdropTransaction()
with pytest.raises(ValueError, match="Pending airdrops list must contain mininum 1 and maximum 10 pendingAirdrop."):
cancel_airdrop_tx_1.build_transaction_body()
cancel_airdrop_tx_1.transaction_id = generate_transaction_id(sender_id)
cancel_airdrop_tx_1.set_node_account_ids([node_account_id])

transaction_body = cancel_airdrop_tx_1.build_transaction_body()

assert len(transaction_body.tokenCancelAirdrop.pending_airdrops) == 0
Comment thread
exploreriii marked this conversation as resolved.

# With pending airdrops list containing more than 10 ids
cancel_airdrop_tx_2 = TokenCancelAirdropTransaction()
for _ in range(11):
cancel_airdrop_tx_2.add_pending_airdrop(sample_pending_airdrop)
with pytest.raises(ValueError, match="Pending airdrops list must contain mininum 1 and maximum 10 pendingAirdrop."):
cancel_airdrop_tx_2.build_transaction_body()

cancel_airdrop_tx_2.transaction_id = generate_transaction_id(sender_id)
cancel_airdrop_tx_2.set_node_account_ids([node_account_id])

transaction_body = cancel_airdrop_tx_2.build_transaction_body()

assert len(transaction_body.tokenCancelAirdrop.pending_airdrops) == 11
Comment thread
exploreriii marked this conversation as resolved.


def test_set_pending_airdrops(mock_account_ids):
Expand Down
Loading