Skip to content

Commit 1f4f5cf

Browse files
authored
Merge branch 'hiero-ledger:main' into main
2 parents 277bf9d + 189baa2 commit 1f4f5cf

38 files changed

+2844
-600
lines changed

.github/workflows/examples.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
runs-on: ubuntu-latest
1515
steps:
1616
- name: Harden the runner (Audit all outbound calls)
17-
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
17+
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
1818
with:
1919
egress-policy: audit
2020

.github/workflows/pr-checks.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ jobs:
3131
statuses: write
3232
steps:
3333
- name: Harden the runner (Audit all outbound calls)
34-
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
34+
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
3535
with:
3636
egress-policy: audit
3737

@@ -47,7 +47,7 @@ jobs:
4747
contents: read
4848
steps:
4949
- name: Harden the runner (Audit all outbound calls)
50-
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
50+
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
5151
with:
5252
egress-policy: audit
5353

.github/workflows/publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
id-token: write
1919
steps:
2020
- name: Harden the runner (Audit all outbound calls)
21-
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
21+
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
2222
with:
2323
egress-policy: audit
2424

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222

2323
steps:
2424
- name: Harden the runner (Audit all outbound calls)
25-
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
25+
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
2626
with:
2727
egress-policy: audit
2828

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,34 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.
88

99

1010
### Added
11+
- Add `examples/topic_id.py` to demonstrate `TopicId` opeartions
12+
- Add `examples/topic_message.py` to demonstrate `TopicMessage` and `TopicMessageChunk` with local mock data.
13+
- Added missing validation logic `fee_schedule_key` in integration `token_create_transaction_e2e_test.py` and ``token_update_transaction_e2e_test.py`.
14+
- Add `account_balance_query.py` example to demonstrate how to use the CryptoGetAccountBalanceQuery class.
15+
- Add `examples/token_create_transaction_admin_key.py` demonstrating admin key privileges for token management including token updates, key changes, and deletion (#798)
16+
- Add `examples/account_info.py` to demonstrate `AccountInfo` opeartions
17+
- Added `HbarUnit` class and Extend `Hbar` class to handle floating-point numbers
1118

1219

1320
### Changed
21+
- Refactored token-related example scripts (`token_delete.py`, `token_dissociate.py`, etc.) for improved readability and modularity. [#370]
22+
- upgrade: step security action upgraded from harden-runner-2.13.1 to harden-runner-2.13.1
23+
- chore: Split `examples/account_allowance_nft.py` into separate `account_allowance_approve_transaction_nft.py` and `account_allowance_delete_transaction_nft.py` examples.
24+
- chore: bump protobuf from 6.33.0 to 6.33.1 (#796)
25+
- fix: Allow `max_automatic_token_associations` to be set to -1 (unlimited) in `AccountCreateTransaction` and add field to `AccountInfo`.
26+
- Allow `PrivateKey` to be used for keys in `TopicCreateTransaction` for consistency.
1427

1528

1629
### Fixed
30+
- chore: fixed integration test names without a test prefix or postfix
1731

1832

1933

2034
## [0.1.8] - 2025-11-07
2135

2236
### Added
37+
- `is_unknown` property added to `src/hiero_sdk_python/response_code.py`
38+
- Example `response_code.py`
2339
- Add `TokenFeeScheduleUpdateTransaction` class to support updating custom fee schedules on tokens (#471).
2440
- Add `examples/token_update_fee_schedule_fungible.py` and `examples/token_update_fee_schedule_nft.py` demonstrating the use of `TokenFeeScheduleUpdateTransaction`.
2541
- Update `docs/sdk_users/running_examples.md` to include `TokenFeeScheduleUpdateTransaction`.
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
"""
2+
This example demonstrates approving an NFT allowance and transferring the NFT using it.
3+
4+
- Creates an owner, spender, and receiver account.
5+
- Creates an NFT.
6+
- Mints the NFT to the owner's account.
7+
- Associates the NFT with the receiver's account.
8+
- Approves the spender to spend the owner's NFT.
9+
- Spender (as payer) transfers the NFT from the owner to the receiver.
10+
11+
Usage:
12+
uv run examples/account_allowance_approve_transaction_nft.py
13+
"""
14+
15+
import os
16+
import sys
17+
from dotenv import load_dotenv
18+
19+
from hiero_sdk_python import (
20+
Client,
21+
AccountId,
22+
PrivateKey,
23+
Network,
24+
Hbar,
25+
ResponseCode,
26+
TokenCreateTransaction,
27+
TokenType,
28+
SupplyType,
29+
TokenMintTransaction,
30+
NftId,
31+
TokenAssociateTransaction,
32+
AccountAllowanceApproveTransaction,
33+
TransferTransaction
34+
)
35+
from hiero_sdk_python.account.account_create_transaction import AccountCreateTransaction
36+
37+
load_dotenv()
38+
39+
network_name = os.getenv('NETWORK', 'testnet').lower()
40+
41+
def setup_client():
42+
"""Initialize and set up the client with operator account"""
43+
if os.getenv("OPERATOR_ID") is None or os.getenv("OPERATOR_KEY") is None:
44+
print("Environment variables OPERATOR_ID and OPERATOR_KEY must be set")
45+
sys.exit(1)
46+
47+
network = Network(network_name)
48+
print(f"Connecting to Hedera {network_name} network!")
49+
client = Client(network)
50+
51+
operator_id_str = os.getenv("OPERATOR_ID")
52+
operator_key_str = os.getenv("OPERATOR_KEY")
53+
assert operator_id_str is not None and operator_key_str is not None
54+
55+
operator_id = AccountId.from_string(operator_id_str)
56+
operator_key = PrivateKey.from_string(operator_key_str)
57+
58+
client.set_operator(operator_id, operator_key)
59+
print(f"Client setup for NFT Owner (Operator): {client.operator_account_id}")
60+
return client, operator_id, operator_key
61+
62+
def create_account(client, memo="Test Account"):
63+
"""Create a new Hedera account with an initial balance."""
64+
private_key = PrivateKey.generate_ed25519()
65+
public_key = private_key.public_key()
66+
67+
tx = (
68+
AccountCreateTransaction()
69+
.set_key(public_key)
70+
.set_initial_balance(Hbar(10))
71+
.set_account_memo(memo)
72+
.execute(client)
73+
)
74+
75+
if tx.status != ResponseCode.SUCCESS:
76+
print(f"Account creation failed: {ResponseCode(tx.status).name}")
77+
sys.exit(1)
78+
79+
account_id = tx.account_id
80+
print(f"Created new account ({memo}): {account_id}")
81+
return account_id, private_key
82+
83+
def create_nft_token(client, owner_id, owner_key):
84+
"""Create a new non-fungible token (NFT) with the owner as treasury."""
85+
86+
tx = (
87+
TokenCreateTransaction()
88+
.set_token_name("ApproveTest NFT")
89+
.set_token_symbol("ATNFT")
90+
.set_token_type(TokenType.NON_FUNGIBLE_UNIQUE)
91+
.set_decimals(0)
92+
.set_initial_supply(0)
93+
.set_treasury_account_id(owner_id)
94+
.set_supply_type(SupplyType.INFINITE)
95+
.set_admin_key(owner_key)
96+
.set_supply_key(owner_key)
97+
.freeze_with(client)
98+
.sign(owner_key)
99+
)
100+
101+
receipt = tx.execute(client)
102+
103+
if receipt.status != ResponseCode.SUCCESS:
104+
print(f"Token creation failed: {ResponseCode(receipt.status).name}")
105+
sys.exit(1)
106+
107+
print(f"NFT Owner ({owner_id}) created NFT Token: {receipt.token_id}")
108+
return receipt.token_id
109+
110+
def mint_nft(client, token_id, metadata_list):
111+
"""Mint NFT(s) with metadata."""
112+
tx = (
113+
TokenMintTransaction()
114+
.set_token_id(token_id)
115+
.set_metadata(metadata_list)
116+
.execute(client)
117+
)
118+
119+
if tx.status != ResponseCode.SUCCESS:
120+
print(f"Mint failed: {ResponseCode(tx.status).name}")
121+
sys.exit(1)
122+
123+
serials = tx.serial_numbers
124+
print(f"NFT Owner ({client.operator_account_id}) minted {len(serials)} NFT(s) for Token {token_id}: {serials}")
125+
return [NftId(token_id, s) for s in serials]
126+
127+
def associate_token_with_account(client, account_id, private_key, token_id):
128+
"""Associate a token with an account."""
129+
tx = (
130+
TokenAssociateTransaction()
131+
.set_account_id(account_id)
132+
.add_token_id(token_id)
133+
.freeze_with(client)
134+
.sign(private_key)
135+
.execute(client)
136+
)
137+
138+
if tx.status != ResponseCode.SUCCESS:
139+
print(f"Association failed: {ResponseCode(tx.status).name}")
140+
sys.exit(1)
141+
142+
print(f"Associated token {token_id} with Receiver account {account_id}")
143+
144+
def approve_nft_allowance(client, nft_id, owner_id, spender_id, owner_key):
145+
"""Approve NFT allowance for a spender."""
146+
tx = (
147+
AccountAllowanceApproveTransaction()
148+
.approve_token_nft_allowance_all_serials(
149+
nft_id.token_id, owner_id, spender_id
150+
)
151+
.freeze_with(client)
152+
.sign(owner_key)
153+
.execute(client)
154+
)
155+
156+
if tx.status != ResponseCode.SUCCESS:
157+
print(f"Approval failed: {ResponseCode(tx.status).name}")
158+
sys.exit(1)
159+
160+
print(f"NFT Owner ({owner_id}) approved Spender ({spender_id}) for NFT {nft_id.token_id} (all serials)")
161+
162+
def transfer_nft_using_allowance(spender_client, nft_id, owner_id, receiver_id):
163+
"""Transfer an NFT using approved allowance via the spender client."""
164+
print(f"Spender ({spender_client.operator_account_id}) transferring NFT {nft_id} from Owner ({owner_id})...")
165+
166+
tx = (
167+
TransferTransaction()
168+
.add_approved_nft_transfer(nft_id, owner_id, receiver_id)
169+
.execute(spender_client)
170+
)
171+
172+
if tx.status != ResponseCode.SUCCESS:
173+
print(f"Transfer failed: {ResponseCode(tx.status).name}")
174+
sys.exit(1)
175+
176+
print(f"SUCCESS: Spender ({spender_client.operator_account_id}) transferred NFT {nft_id} to Receiver ({receiver_id})")
177+
178+
def main():
179+
"""End-to-end demonstration."""
180+
owner_client, owner_id, owner_key = setup_client()
181+
182+
try:
183+
# Create spender and receiver accounts
184+
spender_id, spender_key = create_account(owner_client, "Spender")
185+
receiver_id, receiver_key = create_account(owner_client, "Receiver")
186+
187+
# Create and mint NFT
188+
token_id = create_nft_token(owner_client, owner_id, owner_key)
189+
nft_ids = mint_nft(owner_client, token_id, [b"Metadata 1"])
190+
nft_id = nft_ids[0]
191+
192+
# Associate token with receiver
193+
associate_token_with_account(owner_client, receiver_id, receiver_key, token_id)
194+
195+
# Approve allowance
196+
approve_nft_allowance(owner_client, nft_id, owner_id, spender_id, owner_key)
197+
198+
# Create a client for the spender
199+
print("\nSetting up client for the Spender...")
200+
spender_client = Client(owner_client.network)
201+
spender_client.set_operator(spender_id, spender_key)
202+
print(f"Client setup for Spender: {spender_id}")
203+
204+
# Transfer NFT using the allowance
205+
transfer_nft_using_allowance(spender_client, nft_id, owner_id, receiver_id)
206+
207+
except Exception as e:
208+
print(f"Error: {e}")
209+
import traceback
210+
traceback.print_exc()
211+
finally:
212+
owner_client.close()
213+
214+
if __name__ == "__main__":
215+
main()

0 commit comments

Comments
 (0)