1+ // SPDX-License-Identifier: Apache-2.0
2+ #include " AccountBalance.h"
3+ #include " AccountBalanceQuery.h"
4+ #include " AccountCreateTransaction.h"
5+ #include " AccountId.h"
6+ #include " Client.h"
7+ #include " CustomFixedFee.h"
8+ #include " ED25519PrivateKey.h"
9+ #include " TokenCreateTransaction.h"
10+ #include " TopicCreateTransaction.h"
11+ #include " TopicId.h"
12+ #include " TopicMessageSubmitTransaction.h"
13+ #include " TopicUpdateTransaction.h"
14+ #include " TransactionReceipt.h"
15+ #include " TransactionResponse.h"
16+ #include " TransferTransaction.h"
17+
18+ #include < dotenv.h>
19+ #include < iostream>
20+
21+ using namespace Hiero ;
22+
23+ int main (int argc, char ** argv)
24+ {
25+ dotenv::init ();
26+ const AccountId operatorAccountId = AccountId::fromString (std::getenv (" OPERATOR_ID" ));
27+ const std::shared_ptr<PrivateKey> operatorPrivateKey = ED25519PrivateKey::fromString (std::getenv (" OPERATOR_KEY" ));
28+
29+ // Get a client for the Hiero testnet, and set the operator account ID and key such that all generated transactions
30+ // will be paid for by this account and be signed by this key.
31+ Client client = Client::forTestnet ();
32+ client.setOperator (operatorAccountId, operatorPrivateKey);
33+
34+ /*
35+ * Step 1:
36+ * Create account - alice
37+ */
38+ std::cout << " Creating account - alice" << std::endl;
39+
40+ std::shared_ptr<PrivateKey> aliceKey = ED25519PrivateKey::generatePrivateKey ();
41+
42+ AccountId alice = AccountCreateTransaction ()
43+ .setKeyWithoutAlias (aliceKey->getPublicKey ())
44+ .setInitialBalance (Hbar (10LL ))
45+ .execute (client)
46+ .getReceipt (client)
47+ .mAccountId .value ();
48+
49+ std::cout << " Alice account id:" << alice.toString () << std::endl;
50+
51+ /*
52+ * Step 2:
53+ * Create a topic with hbar custom fee
54+ */
55+ int64_t feeAmount = 100000000 ; // 1 HBAR equivalent
56+
57+ CustomFixedFee customFixedFee = CustomFixedFee ().setAmount (feeAmount).setFeeCollectorAccountId (operatorAccountId);
58+
59+ TopicId topicId = TopicCreateTransaction ()
60+ .setAdminKey (operatorPrivateKey->getPublicKey ())
61+ .setFeeScheduleKey (operatorPrivateKey->getPublicKey ())
62+ .addCustomFixedFee ({ customFixedFee })
63+ .execute (client)
64+ .getReceipt (client)
65+ .mTopicId .value ();
66+
67+ /*
68+ * Step 3:
69+ * Submit a message to that topic, paid for by alice, specifying max custom fee amount bigger than the topic’s amount.
70+ */
71+ AccountBalance accountBalanceBefore = AccountBalanceQuery ().setAccountId (alice).execute (client);
72+ AccountBalance feeCollectorBalanceBefore = AccountBalanceQuery ().setAccountId (operatorAccountId).execute (client);
73+
74+ CustomFeeLimit limit = CustomFeeLimit ().setPayerId (alice).addCustomFee (CustomFixedFee ().setAmount (feeAmount * 2 ));
75+
76+ client.setOperator (alice, aliceKey);
77+ TopicMessageSubmitTransaction ()
78+ .setTopicId (topicId)
79+ .setMessage (" message" )
80+ .addCustomFeeLimit (limit)
81+ .execute (client)
82+ .getReceipt (client);
83+
84+ std::cout << " Message submitted successfully" << std::endl;
85+
86+ /*
87+ * Step 4:
88+ * Verify alice was debited the fee amount and the fee collector account was credited the amount.
89+ */
90+ client.setOperator (operatorAccountId, operatorPrivateKey);
91+
92+ AccountBalance accountBalanceAfter = AccountBalanceQuery ().setAccountId (alice).execute (client);
93+ AccountBalance feeCollectorBalanceAfter = AccountBalanceQuery ().setAccountId (operatorAccountId).execute (client);
94+
95+ std::cout << " Alice account Hbar balance before: " << accountBalanceBefore.toString () << std::endl;
96+ std::cout << " Alice account Hbar balance after: " << accountBalanceAfter.toString () << std::endl;
97+ std::cout << " Fee collector account Hbar balance before: " << feeCollectorBalanceBefore.toString () << std::endl;
98+ std::cout << " Fee collector account Hbar balance after: " << feeCollectorBalanceAfter.toString () << std::endl;
99+
100+ /*
101+ * Step 5:
102+ * Create a fungible token and transfer some tokens to alice
103+ */
104+ TokenId tokenId = TokenCreateTransaction ()
105+ .setTokenName (" revenueGeneratingToken" )
106+ .setTokenSymbol (" RGT" )
107+ .setInitialSupply (10 )
108+ .setTreasuryAccountId (operatorAccountId)
109+ .execute (client)
110+ .getReceipt (client)
111+ .mTokenId .value ();
112+
113+ std::cout << " Created token with Id: " << tokenId.toString () << std::endl;
114+
115+ TransferTransaction ()
116+ .addTokenTransfer (tokenId, operatorAccountId, -1ULL )
117+ .addTokenTransfer (tokenId, alice, 1ULL )
118+ .execute (client)
119+ .getReceipt (client);
120+
121+ /*
122+ * Step 6:
123+ * Update the topic to have a fee of the token.
124+ */
125+ std::cout << " Updating the topic to have a custom fee of the token" << std::endl;
126+
127+ CustomFixedFee customTokenFixedFee =
128+ CustomFixedFee ().setAmount (1 ).setDenominatingTokenId (tokenId).setFeeCollectorAccountId (operatorAccountId);
129+
130+ TopicUpdateTransaction ()
131+ .setTopicId (topicId)
132+ .setCustomFixedFees ({ customTokenFixedFee })
133+ .execute (client)
134+ .getReceipt (client);
135+
136+ /*
137+ * Step 7:
138+ * Submit another message to that topic, paid by alice, without specifying max custom fee amount.
139+ */
140+ accountBalanceBefore = AccountBalanceQuery ().setAccountId (alice).execute (client);
141+ feeCollectorBalanceBefore = AccountBalanceQuery ().setAccountId (operatorAccountId).execute (client);
142+
143+ std::cout << " Submitting a message as alice to the topic" << std::endl;
144+
145+ client.setOperator (alice, aliceKey);
146+ TopicMessageSubmitTransaction ().setTopicId (topicId).setMessage (" message" ).execute (client).getReceipt (client);
147+
148+ std::cout << " Message submitted successfully" << std::endl;
149+
150+ /*
151+ * Step 8:
152+ * Verify alice was debited the new fee amount and the fee collector account was credited the amount.
153+ */
154+ client.setOperator (operatorAccountId, operatorPrivateKey);
155+
156+ accountBalanceAfter = AccountBalanceQuery ().setAccountId (alice).execute (client);
157+ feeCollectorBalanceAfter = AccountBalanceQuery ().setAccountId (operatorAccountId).execute (client);
158+
159+ std::cout << " Alice account Hbar balance before: " << accountBalanceBefore.toString () << std::endl;
160+ std::cout << " Alice account Hbar balance after: " << accountBalanceAfter.toString () << std::endl;
161+ std::cout << " Fee collector account Hbar balance before: " << feeCollectorBalanceBefore.toString () << std::endl;
162+ std::cout << " Fee collector account Hbar balance after: " << feeCollectorBalanceAfter.toString () << std::endl;
163+
164+ /*
165+ * Step 9:
166+ * Create account - bob
167+ */
168+ std::cout << " Creating account - bob" << std::endl;
169+
170+ std::shared_ptr<PrivateKey> bobKey = ED25519PrivateKey::generatePrivateKey ();
171+
172+ AccountId bob = AccountCreateTransaction ()
173+ .setKeyWithoutAlias (bobKey->getPublicKey ())
174+ .setInitialBalance (Hbar (10LL ))
175+ .setMaxAutomaticTokenAssociations (-1 )
176+ .execute (client)
177+ .getReceipt (client)
178+ .mAccountId .value ();
179+
180+ std::cout << " Bob account id:" << bob.toString () << std::endl;
181+
182+ /*
183+ * Step 10:
184+ * Update the topic’s fee exempt keys and add bob’s public key.
185+ */
186+ std::cout << " Updating the topic fee exempt keys with bob's public key" << std::endl;
187+
188+ TopicUpdateTransaction ()
189+ .setTopicId (topicId)
190+ .addFeeExemptKey (bobKey->getPublicKey ())
191+ .execute (client)
192+ .getReceipt (client);
193+
194+ /*
195+ * Step 11:
196+ * Submit another message to that topic, paid with bob, without specifying max custom fee amount.
197+ */
198+ accountBalanceBefore = AccountBalanceQuery ().setAccountId (bob).execute (client);
199+ feeCollectorBalanceBefore = AccountBalanceQuery ().setAccountId (operatorAccountId).execute (client);
200+
201+ std::cout << " Submitting a message as bob to the topic" << std::endl;
202+
203+ client.setOperator (bob, bobKey);
204+ TopicMessageSubmitTransaction ().setTopicId (topicId).setMessage (" message" ).execute (client).getReceipt (client);
205+
206+ std::cout << " Message submitted successfully" << std::endl;
207+
208+ /*
209+ * Step 12:
210+ * Verify bob was not debited the fee amount.
211+ */
212+ client.setOperator (operatorAccountId, operatorPrivateKey);
213+
214+ accountBalanceAfter = AccountBalanceQuery ().setAccountId (bob).execute (client);
215+ feeCollectorBalanceAfter = AccountBalanceQuery ().setAccountId (operatorAccountId).execute (client);
216+
217+ std::cout << " Bob account Hbar balance before: " << accountBalanceBefore.toString () << std::endl;
218+ std::cout << " Bob account Hbar balance after: " << accountBalanceAfter.toString () << std::endl;
219+
220+ // Clean up:
221+ client.close ();
222+
223+ return 0 ;
224+ }
0 commit comments