Skip to content

Commit 26dcfa9

Browse files
committed
feat: Add Revenue Generating example and some assertion fixes
Signed-off-by: gsstoykov <georgi.stoykov@limechain.tech>
1 parent 6c08f8b commit 26dcfa9

File tree

6 files changed

+279
-46
lines changed

6 files changed

+279
-46
lines changed

src/sdk/examples/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ set(MULTI_APP_TRANSFER_EXAMPLE_NAME ${PROJECT_NAME}-multi-app-transfer-example)
4444
set(MULTI_SIG_OFFLINE_EXAMPLE_NAME ${PROJECT_NAME}-multi-sig-offline-example)
4545
set(NFT_ADD_REMOVE_ALLOWANCES_EXAMPLE_NAME ${PROJECT_NAME}-nft-add-remove-allowances-example)
4646
set(PRNG_EXAMPLE_NAME ${PROJECT_NAME}-prng-example)
47+
set(REVENUE_GENERATING_TOPICS_EXAMPLE_NAME ${PROJECT_NAME}-revenue-generating-topics-example)
4748
set(SCHEDULE_EXAMPLE_NAME ${PROJECT_NAME}-schedule-example)
4849
set(SCHEDULE_IDENTICAL_TRANSACTION_EXAMPLE_NAME ${PROJECT_NAME}-schedule-identical-transaction-example)
4950
set(SCHEDULE_MULTI_SIG_TRANSACTION_EXAMPLE_NAME ${PROJECT_NAME}-schedule-multisig-transaction-example)
@@ -101,6 +102,7 @@ add_executable(${MULTI_APP_TRANSFER_EXAMPLE_NAME} MultiAppTransferExample.cc)
101102
add_executable(${MULTI_SIG_OFFLINE_EXAMPLE_NAME} MultiSigOfflineExample.cc)
102103
add_executable(${NFT_ADD_REMOVE_ALLOWANCES_EXAMPLE_NAME} NftAddRemoveAllowancesExample.cc)
103104
add_executable(${PRNG_EXAMPLE_NAME} PrngExample.cc)
105+
add_executable(${REVENUE_GENERATING_TOPICS_EXAMPLE_NAME} RevenueGeneratingTopicsExample.cc)
104106
add_executable(${SCHEDULE_EXAMPLE_NAME} ScheduleExample.cc)
105107
add_executable(${SCHEDULE_IDENTICAL_TRANSACTION_EXAMPLE_NAME} ScheduleIdenticalTransactionExample.cc)
106108
add_executable(${SCHEDULE_MULTI_SIG_TRANSACTION_EXAMPLE_NAME} ScheduleMultiSigTransactionExample.cc)
@@ -180,6 +182,7 @@ target_link_libraries(${MULTI_APP_TRANSFER_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
180182
target_link_libraries(${MULTI_SIG_OFFLINE_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
181183
target_link_libraries(${NFT_ADD_REMOVE_ALLOWANCES_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
182184
target_link_libraries(${PRNG_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
185+
target_link_libraries(${REVENUE_GENERATING_TOPICS_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
183186
target_link_libraries(${SCHEDULE_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
184187
target_link_libraries(${SCHEDULE_IDENTICAL_TRANSACTION_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
185188
target_link_libraries(${SCHEDULE_MULTI_SIG_TRANSACTION_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
@@ -239,6 +242,7 @@ install(TARGETS
239242
${MULTI_SIG_OFFLINE_EXAMPLE_NAME}
240243
${NFT_ADD_REMOVE_ALLOWANCES_EXAMPLE_NAME}
241244
${PRNG_EXAMPLE_NAME}
245+
${REVENUE_GENERATING_TOPICS_EXAMPLE_NAME}
242246
${SCHEDULE_EXAMPLE_NAME}
243247
${SCHEDULE_IDENTICAL_TRANSACTION_EXAMPLE_NAME}
244248
${SCHEDULE_MULTI_SIG_TRANSACTION_EXAMPLE_NAME}
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
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+
}

src/sdk/main/src/AccountBalance.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ std::string AccountBalance::toString() const
4949
nlohmann::json json;
5050
json["mBalance"] = mBalance.toString();
5151

52+
for (const auto& pair : mTokens)
53+
{
54+
json[pair.first.toString()] = pair.second;
55+
}
56+
5257
return json.dump();
5358
}
5459

src/sdk/tests/integration/TopicCreateTransactionIntegrationTests.cc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,12 @@ TEST_F(TopicCreateTransactionIntegrationTests, RevenueGeneratingTopicCanCreate)
8888
std::vector<std::shared_ptr<Key>> exemptKeys;
8989
for (int i = 0; i < 2; i++)
9090
{
91-
EXPECT_NO_THROW(exemptKeys.push_back(ED25519PrivateKey::generatePrivateKey()));
91+
ASSERT_NO_THROW(exemptKeys.push_back(ED25519PrivateKey::generatePrivateKey()));
9292
}
9393
const std::string topicMemo = "memo";
9494

9595
TokenId tokenId;
96-
EXPECT_NO_THROW(tokenId = TokenCreateTransaction()
96+
ASSERT_NO_THROW(tokenId = TokenCreateTransaction()
9797
.setTokenName("ffff")
9898
.setTokenSymbol("F")
9999
.setTreasuryAccountId(AccountId(2ULL))
@@ -102,7 +102,7 @@ TEST_F(TopicCreateTransactionIntegrationTests, RevenueGeneratingTopicCanCreate)
102102
.mTokenId.value());
103103

104104
CustomFixedFee customFixedFee;
105-
EXPECT_NO_THROW(customFixedFee =
105+
ASSERT_NO_THROW(customFixedFee =
106106
CustomFixedFee().setAmount(2).setDenominatingTokenId(tokenId).setFeeCollectorAccountId(
107107
getTestClient().getOperatorAccountId().value()));
108108

@@ -138,7 +138,7 @@ TEST_F(TopicCreateTransactionIntegrationTests, RevenueGeneratingTopicCannotCreat
138138
std::vector<std::shared_ptr<Key>> exemptKeys;
139139
for (int i = 0; i < 11; i++)
140140
{
141-
EXPECT_NO_THROW(exemptKeys.push_back(ED25519PrivateKey::generatePrivateKey()));
141+
ASSERT_NO_THROW(exemptKeys.push_back(ED25519PrivateKey::generatePrivateKey()));
142142
}
143143

144144
// When / Then

0 commit comments

Comments
 (0)