Skip to content

Commit 65ab0d7

Browse files
committed
feat: Allow PublicKey for TokenCreateTransaction keys
Signed-off-by: Adityarya11 <[email protected]>
1 parent 6b38081 commit 65ab0d7

File tree

3 files changed

+52
-39
lines changed

3 files changed

+52
-39
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.
2121
- Added expiration_time, auto_renew_period, auto_renew_account, fee_schedule_key, kyc_key in `TokenCreateTransaction`, `TokenUpdateTransaction` classes
2222
- Added comprehensive Google-style docstrings to the `CustomFee` class and its methods in `custom_fee.py`.
2323
- docs: Add `docs/sdk_developers/project_structure.md` to explain repository layout and import paths.
24+
- feat: Allow `PublicKey` to be used for keys in `TokenCreateTransaction` for non-custodial signing.
2425

2526
### Changed
2627
- chore: validate that token airdrop transactions require an available token service on the channel (#632)

src/hiero_sdk_python/tokens/token_create_transaction.py

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"""
1313

1414
from dataclasses import dataclass, field
15-
from typing import Optional, Any, List
15+
from typing import Optional, Any, List, Union
1616

1717
from hiero_sdk_python.Duration import Duration
1818
from hiero_sdk_python.channels import _Channel
@@ -26,12 +26,15 @@
2626
from hiero_sdk_python.tokens.token_type import TokenType
2727
from hiero_sdk_python.tokens.supply_type import SupplyType
2828
from hiero_sdk_python.account.account_id import AccountId
29+
from hiero_sdk_python.crypto.public_key import PublicKey
2930
from hiero_sdk_python.crypto.private_key import PrivateKey
3031
from hiero_sdk_python.tokens.custom_fee import CustomFee
3132

3233
AUTO_RENEW_PERIOD = Duration(7890000) # around 90 days in seconds
3334
DEFAULT_TRANSACTION_FEE = 3_000_000_000
3435

36+
Key = Union[PublicKey, PrivateKey]
37+
3538
@dataclass
3639
class TokenParams:
3740
"""
@@ -81,14 +84,14 @@ class TokenKeys:
8184
kyc_key: The KYC key for the token to grant KYC to an account.
8285
"""
8386

84-
admin_key: Optional[PrivateKey] = None
85-
supply_key: Optional[PrivateKey] = None
86-
freeze_key: Optional[PrivateKey] = None
87-
wipe_key: Optional[PrivateKey] = None
88-
metadata_key: Optional[PrivateKey] = None
89-
pause_key: Optional[PrivateKey] = None
90-
kyc_key: Optional[PrivateKey] = None
91-
fee_schedule_key: Optional[PrivateKey] = None
87+
admin_key: Optional[Key] = None
88+
supply_key: Optional[Key] = None
89+
freeze_key: Optional[Key] = None
90+
wipe_key: Optional[Key] = None
91+
metadata_key: Optional[Key] = None
92+
pause_key: Optional[Key] = None
93+
kyc_key: Optional[Key] = None
94+
fee_schedule_key: Optional[Key] = None
9295

9396
class TokenCreateValidator:
9497
"""Token, key and freeze checks for creating a token as per the proto"""
@@ -368,43 +371,43 @@ def set_memo(self, memo: str) -> "TokenCreateTransaction":
368371
self._token_params.memo = memo
369372
return self
370373

371-
def set_admin_key(self, key: PrivateKey) -> "TokenCreateTransaction":
374+
def set_admin_key(self, key: Key) -> "TokenCreateTransaction":
372375
""" Sets the admin key for the token, which allows updating and deleting the token."""
373376
self._require_not_frozen()
374377
self._keys.admin_key = key
375378
return self
376379

377-
def set_supply_key(self, key: PrivateKey) -> "TokenCreateTransaction":
380+
def set_supply_key(self, key: Key) -> "TokenCreateTransaction":
378381
""" Sets the supply key for the token, which allows minting and burning tokens."""
379382
self._require_not_frozen()
380383
self._keys.supply_key = key
381384
return self
382385

383-
def set_freeze_key(self, key: PrivateKey) -> "TokenCreateTransaction":
386+
def set_freeze_key(self, key: Key) -> "TokenCreateTransaction":
384387
""" Sets the freeze key for the token, which allows freezing and unfreezing accounts."""
385388
self._require_not_frozen()
386389
self._keys.freeze_key = key
387390
return self
388391

389-
def set_wipe_key(self, key: PrivateKey) -> "TokenCreateTransaction":
392+
def set_wipe_key(self, key: Key) -> "TokenCreateTransaction":
390393
""" Sets the wipe key for the token, which allows wiping tokens from an account."""
391394
self._require_not_frozen()
392395
self._keys.wipe_key = key
393396
return self
394397

395-
def set_metadata_key(self, key: PrivateKey) -> "TokenCreateTransaction":
398+
def set_metadata_key(self, key: Key) -> "TokenCreateTransaction":
396399
""" Sets the metadata key for the token, which allows updating NFT metadata."""
397400
self._require_not_frozen()
398401
self._keys.metadata_key = key
399402
return self
400403

401-
def set_pause_key(self, key: PrivateKey) -> "TokenCreateTransaction":
404+
def set_pause_key(self, key: Key) -> "TokenCreateTransaction":
402405
""" Sets the pause key for the token, which allows pausing and unpausing the token."""
403406
self._require_not_frozen()
404407
self._keys.pause_key = key
405408
return self
406409

407-
def set_kyc_key(self, key: PrivateKey) -> "TokenCreateTransaction":
410+
def set_kyc_key(self, key: Key) -> "TokenCreateTransaction":
408411
""" Sets the KYC key for the token, which allows granting KYC to an account."""
409412
self._require_not_frozen()
410413
self._keys.kyc_key = key
@@ -416,26 +419,35 @@ def set_custom_fees(self, custom_fees: List[CustomFee]) -> "TokenCreateTransacti
416419
self._token_params.custom_fees = custom_fees
417420
return self
418421

419-
def set_fee_schedule_key(self, key: PrivateKey) -> "TokenCreateTransaction":
422+
def set_fee_schedule_key(self, key: Key) -> "TokenCreateTransaction":
420423
"""Sets the fee schedule key for the token."""
421424
self._require_not_frozen()
422425
self._keys.fee_schedule_key = key
423426
return self
424427

425-
def _to_proto_key(self, private_key: Optional[PrivateKey]) -> Optional[basic_types_pb2.Key]:
428+
def _to_proto_key(self, key: Optional[Key]) -> Optional[basic_types_pb2.Key]:
426429
"""
427-
Helper method to convert a private key to protobuf Key format.
430+
Helper method to convert a PrivateKey or PublicKey to protobuf Key format.
428431
429432
Args:
430-
private_key (PrivateKey, Optional): The private key to convert, or None
433+
key (Key, Optional): The private key or public key to convert, or None
431434
432435
Returns:
433-
basic_types_pb2.Key (Optional): The protobuf key or None if private_key is None
436+
basic_types_pb2.Key (Optional): The protobuf key or None if key is None
434437
"""
435-
if not private_key:
438+
if not key:
436439
return None
437440

438-
return private_key.public_key()._to_proto()
441+
# If PrivateKey, get public key first
442+
if isinstance(key, PrivateKey):
443+
return key.public_key()._to_proto()
444+
445+
# If PublicKey, just convert it...
446+
if isinstance(key, PublicKey):
447+
return key._to_proto()
448+
449+
# Handle any other case (though type hinting should prevent this)
450+
raise TypeError("Key must be of type PrivateKey or PublicKey")
439451

440452
def freeze_with(self, client) -> "TokenCreateTransaction":
441453
"""

tests/unit/test_token_create_transaction.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -260,36 +260,36 @@ def test_sign_transaction(mock_account_ids, mock_client):
260260
private_key.sign.return_value = b"signature"
261261
private_key.public_key().to_bytes_raw.return_value = b"public_key"
262262

263-
private_key_admin = MagicMock()
263+
private_key_admin = MagicMock(spec=PrivateKey)
264264
private_key_admin.sign.return_value = b"admin_signature"
265265
private_key_admin.public_key().to_bytes_raw.return_value = b"admin_public_key"
266266
private_key_admin.public_key()._to_proto.return_value = basic_types_pb2.Key(ed25519=b"admin_public_key")
267267

268-
private_key_supply = MagicMock()
268+
private_key_supply = MagicMock(spec=PrivateKey)
269269
private_key_supply.sign.return_value = b"supply_signature"
270270
private_key_supply.public_key()._to_proto.return_value = basic_types_pb2.Key(ed25519=b"supply_public_key")
271271

272-
private_key_freeze = MagicMock()
272+
private_key_freeze = MagicMock(spec=PrivateKey)
273273
private_key_freeze.sign.return_value = b"freeze_signature"
274274
private_key_freeze.public_key()._to_proto.return_value = basic_types_pb2.Key(ed25519=b"freeze_public_key")
275275

276-
private_key_wipe = MagicMock()
276+
private_key_wipe = MagicMock(spec=PrivateKey)
277277
private_key_wipe.sign.return_value = b"wipe_signature"
278278
private_key_wipe.public_key()._to_proto.return_value = basic_types_pb2.Key(ed25519=b"wipe_public_key")
279279

280-
private_key_metadata = MagicMock()
280+
private_key_metadata = MagicMock(spec=PrivateKey)
281281
private_key_metadata.sign.return_value = b"metadata_signature"
282282
private_key_metadata.public_key()._to_proto.return_value = basic_types_pb2.Key(ed25519=b"metadata_public_key")
283283

284-
private_key_pause = MagicMock()
284+
private_key_pause = MagicMock(spec=PrivateKey)
285285
private_key_pause.sign.return_value = b"pause_signature"
286286
private_key_pause.public_key()._to_proto.return_value = basic_types_pb2.Key(ed25519=b"pause_public_key")
287287

288-
private_key_kyc = MagicMock()
288+
private_key_kyc = MagicMock(spec=PrivateKey)
289289
private_key_kyc.sign.return_value = b"kyc_signature"
290290
private_key_kyc.public_key()._to_proto.return_value = basic_types_pb2.Key(ed25519=b"kyc_public_key")
291291

292-
private_key_fee_schedule = MagicMock()
292+
private_key_fee_schedule = MagicMock(spec=PrivateKey)
293293
private_key_fee_schedule.sign.return_value = b"fee_schedule_signature"
294294
private_key_fee_schedule.public_key()._to_proto.return_value = basic_types_pb2.Key(ed25519=b"fee_schedule_public_key")
295295

@@ -722,36 +722,36 @@ def test_build_and_sign_nft_transaction_to_proto(mock_account_ids, mock_client):
722722
private_key_private.sign.return_value = b"private_signature"
723723
private_key_private.public_key().to_bytes_raw.return_value = b"private_public_key"
724724

725-
private_key_admin = MagicMock()
725+
private_key_admin = MagicMock(spec=PrivateKey)
726726
private_key_admin.sign.return_value = b"admin_signature"
727727
private_key_admin.public_key().to_bytes_raw.return_value = b"admin_public_key"
728728
private_key_admin.public_key()._to_proto.return_value = basic_types_pb2.Key(ed25519=b"admin_public_key")
729729

730-
private_key_supply = MagicMock()
730+
private_key_supply = MagicMock(spec=PrivateKey)
731731
private_key_supply.sign.return_value = b"supply_signature"
732732
private_key_supply.public_key()._to_proto.return_value = basic_types_pb2.Key(ed25519=b"supply_public_key")
733733

734-
private_key_freeze = MagicMock()
734+
private_key_freeze = MagicMock(spec=PrivateKey)
735735
private_key_freeze.sign.return_value = b"freeze_signature"
736736
private_key_freeze.public_key()._to_proto.return_value = basic_types_pb2.Key(ed25519=b"freeze_public_key")
737737

738-
private_key_wipe = MagicMock()
738+
private_key_wipe = MagicMock(spec=PrivateKey)
739739
private_key_wipe.sign.return_value = b"wipe_signature"
740740
private_key_wipe.public_key()._to_proto.return_value = basic_types_pb2.Key(ed25519=b"wipe_public_key")
741741

742-
private_key_metadata = MagicMock()
742+
private_key_metadata = MagicMock(spec=PrivateKey)
743743
private_key_metadata.sign.return_value = b"metadata_signature"
744744
private_key_metadata.public_key()._to_proto.return_value = basic_types_pb2.Key(ed25519=b"metadata_public_key")
745745

746-
private_key_pause = MagicMock()
746+
private_key_pause = MagicMock(spec=PrivateKey)
747747
private_key_pause.sign.return_value = b"pause_signature"
748748
private_key_pause.public_key()._to_proto.return_value = basic_types_pb2.Key(ed25519=b"pause_public_key")
749749

750-
private_key_kyc = MagicMock()
750+
private_key_kyc = MagicMock(spec=PrivateKey)
751751
private_key_kyc.sign.return_value = b"kyc_signature"
752752
private_key_kyc.public_key()._to_proto.return_value = basic_types_pb2.Key(ed25519=b"kyc_public_key")
753753

754-
private_key_fee_schedule = MagicMock()
754+
private_key_fee_schedule = MagicMock(spec=PrivateKey)
755755
private_key_fee_schedule.sign.return_value = b"fee_schedule_signature"
756756
private_key_fee_schedule.public_key()._to_proto.return_value = basic_types_pb2.Key(ed25519=b"fee_schedule_public_key")
757757

0 commit comments

Comments
 (0)