Skip to content

Commit 838a112

Browse files
authored
feat: Added doc on how to create token transaction. (hiero-ledger#1190)
Signed-off-by: Adityarya11 <[email protected]> Signed-off-by: notsogod <[email protected]>
1 parent 4ade123 commit 838a112

File tree

2 files changed

+152
-0
lines changed

2 files changed

+152
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.
5656
- Added GitHub workflow that makes sure newly added test files follow pytest test files naming conventions (#1054)
5757
- Added advanced issue template for contributors `.github/ISSUE_TEMPLATE/06_advanced_issue.yml`.
5858
- Add new tests to `tests/unit/topic_info_query_test.py` (#1124)
59+
- Added `coding_token_transactions.md` for a high level overview training on how token transactions are created in the python sdk.
5960
- Added prompt for codeRabbit on how to review /examples ([#1180](https://github.com/hiero-ledger/hiero-sdk-python/issues/1180))
6061

6162
### Changed
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
# Coding Token Transactions
2+
3+
This guide provides a high-level overview of how token transactions are constructed in the Hiero Python SDK. It is intended for developers adding new transaction types (e.g., `TokenMintTransaction`, `TokenBurnTransaction`) to understand the architectural requirements.
4+
5+
## 1. Inheritance Structure
6+
7+
All token transactions must inherit from the base `Transaction` class.
8+
9+
```python
10+
from hiero_sdk_python.transaction.transaction import Transaction
11+
12+
class TokenAssociateTransaction(Transaction):
13+
"""
14+
Represents a token associate transaction on the Hedera network.
15+
"""
16+
17+
```
18+
19+
By inheriting from `Transaction`, your new class automatically gains essential functionality:
20+
21+
- **Signing:** `sign(private_key)`
22+
- **Freezing:** `freeze()` and `freeze_with(client)`
23+
- **Execution:** `execute(client)`
24+
- **Fee Calculation:** Handling `transaction_fee` and `max_transaction_fee`
25+
- **ID Management:** `transaction_id`, `node_account_id`
26+
27+
Your job in the subclass is to define **what** data is being sent, while the base class handles **how** it is sent.
28+
29+
## 2. Initialization and Defaults
30+
31+
In the Python SDK, we prefer a flexible initialization pattern. Constructors (`__init__`) should generally default arguments to `None`. This allows users to instantiate an empty transaction and configure it later using setters (Method Chaining).
32+
33+
### Pattern:
34+
35+
```python
36+
def __init__(
37+
self,
38+
account_id: Optional[AccountId] = None,
39+
token_ids: Optional[List[TokenId]] = None
40+
) -> None:
41+
super().__init__() # Initialize the base Transaction
42+
self.account_id = account_id
43+
self.token_ids = list(token_ids) if token_ids is not None else []
44+
45+
```
46+
47+
- **Why?** It supports both "all-in-one" instantiation and progressive building.
48+
- **Note:** Always call `super().__init__()` to initialize base fields like the signature map and transaction ID.
49+
50+
## 3. Setters and Method Chaining
51+
52+
To provide a "Pythonic" builder interface, every field should have a corresponding setter method. These methods must:
53+
54+
1. Check that the transaction is not frozen (immutable).
55+
2. Return `self` to allow chaining (e.g., `.set_foo().set_bar()`).
56+
57+
### Pattern:
58+
59+
```python
60+
def set_account_id(self, account_id: AccountId) -> "TokenAssociateTransaction":
61+
"""Sets the account ID for the token association transaction."""
62+
self._require_not_frozen() # Critical check from base class
63+
self.account_id = account_id
64+
return self
65+
66+
```
67+
68+
## 4. Protobuf Conversion
69+
70+
The Hedera network communicates via Protocol Buffers (Protobuf). Your transaction class is responsible for converting its Python fields into a Protobuf message.
71+
72+
This is typically done in three steps:
73+
74+
### Step A: Internal Helper `_build_proto_body()`
75+
76+
Create a helper method that constructs the _specific_ body for your transaction type (e.g., `TokenAssociateTransactionBody`).
77+
78+
```python
79+
def _build_proto_body(self) -> token_associate_pb2.TokenAssociateTransactionBody:
80+
if not self.account_id or not self.token_ids:
81+
raise ValueError("Account ID and token IDs must be set.")
82+
83+
return token_associate_pb2.TokenAssociateTransactionBody(
84+
account=self.account_id._to_proto(),
85+
tokens=[token_id._to_proto() for token_id in self.token_ids]
86+
)
87+
88+
```
89+
90+
### Step B: Implement `build_transaction_body()`
91+
92+
This abstract method from `Transaction` must be implemented. It packages your specific body into the main `TransactionBody`.
93+
94+
```python
95+
def build_transaction_body(self) -> transaction_pb2.TransactionBody:
96+
# 1. Build your specific proto body
97+
token_associate_body = self._build_proto_body()
98+
99+
# 2. Get the base body (contains NodeID, TxID, Fees, Memo)
100+
transaction_body = self.build_base_transaction_body()
101+
102+
# 3. Attach your specific body to the main transaction
103+
transaction_body.tokenAssociate.CopyFrom(token_associate_body)
104+
105+
return transaction_body
106+
107+
```
108+
109+
### Step C: Implement `build_scheduled_body()`
110+
111+
To support scheduled transactions, you must also implement this method. It is nearly identical to step B but uses `SchedulableTransactionBody`.
112+
113+
```python
114+
def build_scheduled_body(self) -> SchedulableTransactionBody:
115+
token_associate_body = self._build_proto_body()
116+
schedulable_body = self.build_base_scheduled_body()
117+
schedulable_body.tokenAssociate.CopyFrom(token_associate_body)
118+
return schedulable_body
119+
120+
```
121+
122+
## 5. Network Routing (`_get_method`)
123+
124+
Finally, you must tell the base class which gRPC method to call on the node. This routes the transaction to the correct service (e.g., Token Service vs. Crypto Service).
125+
126+
```python
127+
def _get_method(self, channel: _Channel) -> _Method:
128+
return _Method(
129+
transaction_func=channel.token.associateTokens, # The gRPC function
130+
query_func=None
131+
)
132+
133+
```
134+
135+
---
136+
137+
## Summary Checklist
138+
139+
When creating a new token transaction, ensure you have:
140+
141+
- [ ] **Inherited** from `Transaction`.
142+
- [ ] **Initialized** fields to `None` in `__init__`.
143+
- [ ] **Implemented Setters** that call `self._require_not_frozen()` and return `self`.
144+
- [ ] **Implemented** `_build_proto_body()` to map Python fields to Protobuf.
145+
- [ ] **Implemented** `build_transaction_body()` to merge your body into the main transaction.
146+
- [ ] **Implemented** `build_scheduled_body()` for scheduling support.
147+
- [ ] **Implemented** `_get_method()` to define the gRPC endpoint.
148+
149+
The base `Transaction` class will handle the heavy lifting of freezing, signing, serialization, and execution.
150+
151+
---

0 commit comments

Comments
 (0)