Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.
## [Unreleased]

### Added
- Add `TokenFeeScheduleUpdateTransaction` class to support updating custom fee schedules on tokens (#471).
- Add `examples/token_update_fee_schedule_fungible.py` and `examples/token_update_fee_schedule_nft.py` demonstrating the use of `TokenFeeScheduleUpdateTransaction`.
- Update `docs/sdk_users/running_examples.md` to include `TokenFeeScheduleUpdateTransaction`.
- Added `docs/sdk_developers/pylance.md`, a new guide explaining how to set up and use **Pylance** in VS Code for validating imports, file references, and methods before review. (#713)
- docs: Add Google-style docstrings to `TokenId` class and its methods in `token_id.py`.
- added Google-style docstrings to the `TransactionRecord` class including all dataclass fields, `__repr__`, `_from_proto()` & `_to_proto()` methods.
Expand Down
49 changes: 44 additions & 5 deletions docs/sdk_users/running_examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -1034,22 +1034,61 @@ print(nft_info)
### Querying Fungible Token Info

#### Pythonic Syntax:
```
```python
info_query = TokenInfoQuery(token_id=token_id)
info = info_query.execute(client)
print(info)
```

#### Method Chaining:
```
```python
info_query = (
TokenInfoQuery()
.set_token_id(token_id)
)
TokenInfoQuery()
.set_token_id(token_id)
)

info = info_query.execute(client)
print(info)
```

### Updating a Token Fee Schedule

#### Pythonic Syntax:

```python
# Note: Royalty fees are only for NON_FUNGIBLE_UNIQUE tokens.
new_fees = [
CustomFixedFee(amount=100, fee_collector_account_id=collector_account_id),
CustomRoyaltyFee(numerator=5, denominator=10, fee_collector_account_id=collector_account_id)
]

transaction = TokenFeeScheduleUpdateTransaction(
token_id=token_id, # assumed NFT in this example
custom_fees=new_fees
).freeze_with(client)

transaction.sign(fee_schedule_key) # The fee_schedule_key MUST sign
transaction.execute(client)
```

#### Method Chaining:

```python
# Note: Fractional fees are only for FUNGIBLE_COMMON tokens.
new_fees = [
CustomFixedFee(amount=100, fee_collector_account_id=collector_account_id)
]

transaction = (
TokenFeeScheduleUpdateTransaction()
.set_token_id(token_id) # assumed FUNGIBLE in this example
.set_custom_fees(new_fees)
.freeze_with(client)
)

transaction.sign(fee_schedule_key) # The fee_schedule_key MUST sign
transaction.execute(client)
```
## HBAR Transactions

### Transferring HBAR
Expand Down
143 changes: 143 additions & 0 deletions examples/token_update_fee_schedule_fungible.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
"""Example: Update Custom Fees for a Fungible Token"""
import os
import sys
from dotenv import load_dotenv

from hiero_sdk_python import Client, AccountId, PrivateKey, Network
from hiero_sdk_python.tokens.token_create_transaction import TokenCreateTransaction, TokenParams, TokenKeys
from hiero_sdk_python.tokens.token_type import TokenType
from hiero_sdk_python.tokens.supply_type import SupplyType
from hiero_sdk_python.tokens.token_fee_schedule_update_transaction import TokenFeeScheduleUpdateTransaction
from hiero_sdk_python.tokens.custom_fixed_fee import CustomFixedFee
from hiero_sdk_python.response_code import ResponseCode
from hiero_sdk_python.query.token_info_query import TokenInfoQuery # <-- Import TokenInfoQuery


def setup_client():
"""Initialize client and operator credentials from .env."""
load_dotenv()
try:
client = Client(Network(os.getenv("NETWORK", "testnet")))
operator_id = AccountId.from_string(os.getenv("OPERATOR_ID"))
operator_key = PrivateKey.from_string(os.getenv("OPERATOR_KEY"))
client.set_operator(operator_id, operator_key)
print(f" Operator set: {operator_id}\n")
return client, operator_id, operator_key
except Exception as e:
print(f" Error setting up client: {e}")
sys.exit(1)


def create_fungible_token(client, operator_id, fee_schedule_key):
"""Create a fungible token with only a fee schedule key."""
print(" Creating fungible token...")
token_params = TokenParams(
token_name="Fungible Fee Example",
token_symbol="FFE",
treasury_account_id=operator_id,
initial_supply=1000,
decimals=2,
token_type=TokenType.FUNGIBLE_COMMON,
supply_type=SupplyType.FINITE,
max_supply=2000,
custom_fees=[], # No custom fees at creation
)

keys = TokenKeys(
fee_schedule_key=fee_schedule_key
)

tx = TokenCreateTransaction(token_params=token_params, keys=keys)
tx.set_fee_schedule_key(fee_schedule_key)

tx.freeze_with(client)
receipt = tx.execute(client)

if receipt.status != ResponseCode.SUCCESS:
print(f" Token creation failed: {ResponseCode(receipt.status).name}\n")
client.close()
sys.exit(1)

token_id = receipt.token_id
print(f" Token created successfully: {token_id}\n")
return token_id


def update_custom_fixed_fee(client, token_id, fee_schedule_key, collector_account_id):
"""Updates the token's fee schedule with a new fixed fee."""
print(f" Updating custom fixed fee for token {token_id}...")
new_fees = [
CustomFixedFee(amount=150, fee_collector_account_id=collector_account_id)
]
print(f" Defined {len(new_fees)} new custom fees.\n")
tx = (
TokenFeeScheduleUpdateTransaction()
.set_token_id(token_id)
.set_custom_fees(new_fees)
)

tx.freeze_with(client).sign(fee_schedule_key)

try:
receipt = tx.execute(client)
if receipt.status != ResponseCode.SUCCESS:
print(f" Fee schedule update failed: {ResponseCode(receipt.status).name}\n")
else:
print(" Fee schedule updated successfully.\n")
except Exception as e:
print(f" Error during fee schedule update execution: {e}\n")


def query_token_info(client, token_id):
"""Query token info to show the custom fees."""
print(f"\nQuerying token info for {token_id}...\n")
try:
token_info = TokenInfoQuery(token_id=token_id).execute(client)
print("Token Info Retrieved Successfully!\n")

print(f"Name: {getattr(token_info, 'name', 'N/A')}")
print(f"Symbol: {getattr(token_info, 'symbol', 'N/A')}")
print(f"Total Supply: {getattr(token_info, 'total_supply', 'N/A')}")
print(f"Treasury: {getattr(token_info, 'treasury_account_id', 'N/A')}")
print(f"Decimals: {getattr(token_info, 'decimals', 'N/A')}")
print(f"Max Supply: {getattr(token_info, 'max_supply', 'N/A')}")
print()

custom_fees = getattr(token_info, "custom_fees", [])
if custom_fees:
print(f"Found {len(custom_fees)} custom fee(s):")
for i, fee in enumerate(custom_fees, 1):
print(f" Fee #{i}: {type(fee).__name__}")
print(f" Collector: {getattr(fee, 'fee_collector_account_id', 'N/A')}")
# Specific logic for fixed fee
if isinstance(fee, CustomFixedFee):
print(f" Amount: {getattr(fee, 'amount', 'N/A')}")
else:
print("No custom fees defined for this token.\n")

except Exception as e:
print(f"Error querying token info: {e}")


def main():
client, operator_id, operator_key = setup_client()
token_id = None
try:
fee_key = operator_key

token_id = create_fungible_token(client, operator_id, fee_key)

if token_id:
query_token_info(client, token_id) # Query before update
update_custom_fixed_fee(client, token_id, fee_key, operator_id)
query_token_info(client, token_id) # Query after update

except Exception as e:
print(f" Error during token operations: {e}")
finally:
client.close()
print("\n Client closed. Example complete.")


if __name__ == "__main__":
main()
154 changes: 154 additions & 0 deletions examples/token_update_fee_schedule_nft.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
"""Example: Update Custom Fees for an NFT"""
import os
import sys
from dotenv import load_dotenv

from hiero_sdk_python import Client, AccountId, PrivateKey, Network
from hiero_sdk_python.tokens.token_create_transaction import TokenCreateTransaction, TokenParams, TokenKeys
from hiero_sdk_python.tokens.token_type import TokenType
from hiero_sdk_python.tokens.supply_type import SupplyType
from hiero_sdk_python.tokens.token_fee_schedule_update_transaction import TokenFeeScheduleUpdateTransaction
from hiero_sdk_python.tokens.custom_royalty_fee import CustomRoyaltyFee
from hiero_sdk_python.response_code import ResponseCode
from hiero_sdk_python.query.token_info_query import TokenInfoQuery


def setup_client():
"""Initialize client and operator credentials from .env."""
load_dotenv()
try:
client = Client(Network(os.getenv("NETWORK", "testnet")))
operator_id = AccountId.from_string(os.getenv("OPERATOR_ID"))
operator_key = PrivateKey.from_string(os.getenv("OPERATOR_KEY"))
client.set_operator(operator_id, operator_key)
print(f" Operator set: {operator_id}\n")
return client, operator_id, operator_key
except Exception as e:
print(f" Error setting up client: {e}")
sys.exit(1)


def create_nft(client, operator_id, supply_key, fee_schedule_key):
"""Create an NFT with supply and fee schedule keys."""
print(" Creating NFT...")
token_params = TokenParams(
token_name="NFT Fee Example",
token_symbol="NFE",
treasury_account_id=operator_id,
initial_supply=0, # NFTs have 0 initial supply
decimals=0, # NFTs must have 0 decimals
token_type=TokenType.NON_FUNGIBLE_UNIQUE,
supply_type=SupplyType.FINITE,
max_supply=1000,
custom_fees=[], # No custom fees at creation
)

# fee_schedule_key is required to update fees
# supply_key is required to mint
keys = TokenKeys(
supply_key=supply_key,
fee_schedule_key=fee_schedule_key
)

tx = TokenCreateTransaction(token_params=token_params, keys=keys)
tx.set_fee_schedule_key(fee_schedule_key)

tx.freeze_with(client)
receipt = tx.execute(client)

if receipt.status != ResponseCode.SUCCESS:
print(f" Token creation failed: {ResponseCode(receipt.status).name}\n")
client.close()
sys.exit(1)

token_id = receipt.token_id
print(f" Token created successfully: {token_id}\n")
return token_id


def update_custom_royalty_fee(client, token_id, fee_schedule_key, collector_account_id):
"""Updates the token's fee schedule with a new royalty fee."""
print(f" Updating custom royalty fee for token {token_id}...")
new_fees = [
# NFTs can have royalty fees
CustomRoyaltyFee(
numerator=5,
denominator=100, # 5% royalty
fee_collector_account_id=collector_account_id
)
]
print(f" Defined {len(new_fees)} new custom fees.\n")
tx = (
TokenFeeScheduleUpdateTransaction()
.set_token_id(token_id)
.set_custom_fees(new_fees)
)

# The transaction MUST be signed by the fee_schedule_key
tx.freeze_with(client).sign(fee_schedule_key)

try:
receipt = tx.execute(client)
if receipt.status != ResponseCode.SUCCESS:
print(f" Fee schedule update failed: {ResponseCode(receipt.status).name}\n")
else:
print(" Fee schedule updated successfully.\n")
except Exception as e:
print(f" Error during fee schedule update execution: {e}\n")


def query_token_info(client, token_id):
"""Query token info and verify updated custom fees."""
print(f"\nQuerying token info for {token_id}...\n")
try:
token_info = TokenInfoQuery(token_id=token_id).execute(client)
print("Token Info Retrieved Successfully!\n")

print(f"Name: {getattr(token_info, 'name', 'N/A')}")
print(f"Symbol: {getattr(token_info, 'symbol', 'N/A')}")
print(f"Total Supply: {getattr(token_info, 'total_supply', 'N/A')}")
print(f"Treasury: {getattr(token_info, 'treasury_account_id', 'N/A')}")
print(f"Decimals: {getattr(token_info, 'decimals', 'N/A')}")
print(f"Max Supply: {getattr(token_info, 'max_supply', 'N/A')}")
print()

custom_fees = getattr(token_info, "custom_fees", [])
if custom_fees:
print(f"Found {len(custom_fees)} custom fee(s):")
for i, fee in enumerate(custom_fees, 1):
print(f" Fee #{i}: {type(fee).__name__}")
print(f" Collector: {getattr(fee, 'fee_collector_account_id', 'N/A')}")
if isinstance(fee, CustomRoyaltyFee):
print(f" Royalty: {fee.numerator}/{fee.denominator}")
else:
print(f" Amount: {getattr(fee, 'amount', 'N/A')}")
else:
print("No custom fees defined for this token.\n")

except Exception as e:
print(f"Error querying token info: {e}")


def main():
client, operator_id, operator_key = setup_client()
token_id = None
try:
supply_key = operator_key
fee_key = operator_key

token_id = create_nft(client, operator_id, supply_key, fee_key)

if token_id:
query_token_info(client, token_id) # Query before update
update_custom_royalty_fee(client, token_id, fee_key, operator_id)
query_token_info(client, token_id) # Query after update

except Exception as e:
print(f" Error during token operations: {e}")
finally:
client.close()
print("\n Client closed. Example complete.")


if __name__ == "__main__":
main()
Loading