Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 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
144 changes: 144 additions & 0 deletions examples/token_update_fee_schedule_fungible.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
"""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


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.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, treasury_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 = [
# Send the custom fee to the token's treasury account
CustomFixedFee(amount=150, fee_collector_account_id=treasury_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 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')}")
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)
# Pass the operator_id as the fee collector (which is also the treasury)
update_custom_fixed_fee(client, token_id, fee_key, operator_id)
query_token_info(client, token_id)

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,
decimals=0,
token_type=TokenType.NON_FUNGIBLE_UNIQUE,
supply_type=SupplyType.FINITE,
max_supply=1000,
custom_fees=[],
)

# A supply_key is REQUIRED for NFTs (to mint)
# A fee_schedule_key is required to update fees
keys = TokenKeys(
supply_key=supply_key,
fee_schedule_key=fee_schedule_key
)

tx = TokenCreateTransaction(token_params=token_params, keys=keys)

# Freeze and execute the transaction (operator auto-signs)
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 = [
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)
)

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:
# Use operator key as both supply and fee key
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)
update_custom_royalty_fee(client, token_id, fee_key, operator_id)
query_token_info(client, token_id)

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