Skip to content

Commit a2b26b0

Browse files
authored
Merge pull request #501 from hashgraph/sr/examples
2 parents 1293259 + 1f47db4 commit a2b26b0

File tree

12 files changed

+861
-48
lines changed

12 files changed

+861
-48
lines changed

examples/account_alias.rs

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/*
2+
* ‌
3+
* Hedera Rust SDK
4+
* ​
5+
* Copyright (C) 2022 - 2023 Hedera Hashgraph, LLC
6+
* ​
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* ‍
19+
*/
20+
21+
use clap::Parser;
22+
use hedera::{
23+
AccountBalanceQuery, AccountId, AccountInfoQuery, Client, Hbar, PrivateKey, TransferTransaction
24+
};
25+
26+
#[derive(Parser, Debug)]
27+
struct Args {
28+
#[clap(long, env)]
29+
operator_account_id: AccountId,
30+
31+
#[clap(long, env)]
32+
operator_key: PrivateKey,
33+
34+
#[clap(long, env, default_value = "testnet")]
35+
hedera_network: String,
36+
}
37+
38+
#[tokio::main]
39+
async fn main() -> anyhow::Result<()> {
40+
let _ = dotenvy::dotenv();
41+
let args = Args::parse();
42+
43+
let client = Client::for_name(&args.hedera_network)?;
44+
45+
client.set_operator(args.operator_account_id, args.operator_key.clone());
46+
47+
// Hedera supports a form of auto account creation.
48+
//
49+
// You can "create" an account by generating a private key, and then deriving the public key,
50+
// without any need to interact with the Hedera network. The public key more or less acts as the user's
51+
// account ID. This public key is an account's alias_key: a public key that aliases (or will eventually alias)
52+
// to a Hedera account.
53+
//
54+
// An AccountId takes one of two forms: a normal `AccountId` with no `alias_key` takes the form 0.0.123,
55+
// while an account ID with an `alias_key` takes the form
56+
// 0.0.302a300506032b6570032100114e6abc371b82dab5c15ea149f02d34a012087b163516dd70f44acafabf7777
57+
// Note the prefix of "0.0." indicating the shard and realm. Also note that the aliasKey is stringified
58+
// as a hex-encoded ASN1 DER representation of the key.
59+
//
60+
// An AccountId with an aliasKey can be used just like a normal AccountId for the purposes of queries and
61+
// transactions, however most queries and transactions involving such an AccountId won't work until Hbar has
62+
// been transferred to the alias_key account.
63+
//
64+
// There is no record in the Hedera network of an account associated with a given `alias_key`
65+
// until an amount of Hbar is transferred to the account. The moment that Hbar is transferred to that `alias_key`
66+
// AccountId is the moment that that account actually begins to exist in the Hedera ledger.
67+
68+
println!(r#""Creating" a new account"#);
69+
70+
let private_key = PrivateKey::generate_ed25519();
71+
let public_key = private_key.public_key();
72+
73+
// Assuming that the target shard and realm are known.
74+
// For now they are virtually always 0 and 0.
75+
let alias_account_id = public_key.to_account_id(0, 0);
76+
77+
println!("New account ID: {alias_account_id}");
78+
println!("Just the aliasKey: {:?}", &alias_account_id.alias);
79+
80+
// Note that no queries or transactions have taken place yet.
81+
// This account "creation" process is entirely local.
82+
//
83+
// AccountId::from_str can construct an AccountId with an alias_key.
84+
// It expects a string of the form 0.0.123 in the case of a normal AccountId, or of the form
85+
// 0.0.302a300506032b6570032100114e6abc371b82dab5c15ea149f02d34a012087b163516dd70f44acafabf7777
86+
// in the case of an AccountId with an alias. Note the prefix of "0.0." to indicate the shard and realm.
87+
//
88+
// If the shard and realm are known, you may use PublicKey::from_str().to_account_id() to construct the
89+
// alias_key AccountId.
90+
91+
println!("Transferring some Hbar to the new account");
92+
let _ = TransferTransaction::new()
93+
.hbar_transfer(args.operator_account_id, Hbar::new(-10))
94+
.hbar_transfer(alias_account_id, Hbar::new(10))
95+
.execute(&client)
96+
.await?
97+
.get_receipt(&client)
98+
.await?;
99+
100+
let balance = AccountBalanceQuery::new()
101+
.account_id(alias_account_id)
102+
.execute(&client)
103+
.await?;
104+
105+
println!("Balances of the new account: {balance:?}");
106+
107+
let info = AccountInfoQuery::new()
108+
.account_id(alias_account_id)
109+
.execute(&client)
110+
.await?;
111+
112+
println!("Info about the new account: {info:?}");
113+
114+
// Note that once an account exists in the ledger, it is assigned a normal AccountId, which can be retrieved
115+
// via an AccountInfoQuery.
116+
//
117+
// Users may continue to refer to the account by its alias_key AccountId, but they may also
118+
// now refer to it by its normal AccountId.
119+
120+
println!("the normal account ID: {}", info.account_id);
121+
println!("the alias key: {:?}", info.alias_key);
122+
println!("Example complete!");
123+
124+
Ok(())
125+
}

examples/account_allowance.rs

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
/*
2+
* ‌
3+
* Hedera Rust SDK
4+
* ​
5+
* Copyright (C) 2022 - 2023 Hedera Hashgraph, LLC
6+
* ​
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* ‍
19+
*/
20+
21+
use clap::Parser;
22+
use hedera::{
23+
AccountAllowanceApproveTransaction, AccountBalanceQuery, AccountCreateTransaction, AccountDeleteTransaction, AccountId, Client, Hbar, PrivateKey, TransactionId, TransferTransaction
24+
};
25+
26+
#[derive(Parser, Debug)]
27+
struct Args {
28+
#[clap(long, env)]
29+
operator_account_id: AccountId,
30+
31+
#[clap(long, env)]
32+
operator_key: PrivateKey,
33+
34+
#[clap(long, env, default_value = "testnet")]
35+
hedera_network: String,
36+
}
37+
38+
#[derive(Clone, Debug)]
39+
struct Account {
40+
key: PrivateKey,
41+
id: AccountId,
42+
name: &'static str,
43+
}
44+
45+
async fn create_account(client: &Client, name: &'static str) -> hedera::Result<Account> {
46+
let key = PrivateKey::generate_ed25519();
47+
48+
let reciept = AccountCreateTransaction::new()
49+
.key(key.public_key())
50+
.initial_balance(Hbar::new(5))
51+
.account_memo(format!("[sdk::rust::account_allowance_example::{name}]"))
52+
.execute(client)
53+
.await?
54+
.get_receipt(client)
55+
.await?;
56+
57+
let account_id = reciept
58+
.account_id
59+
.expect("Created account but no account ID in receipt");
60+
61+
Ok(Account {
62+
key,
63+
id: account_id,
64+
name,
65+
})
66+
}
67+
68+
async fn create_accounts(client: &Client) -> anyhow::Result<[Account; 3]> {
69+
println!("Creating accounts");
70+
71+
let (alice, bob, charlie) = tokio::try_join!(
72+
create_account(client, "Alice"),
73+
create_account(client, "Bob"),
74+
create_account(client, "Charlie"),
75+
)?;
76+
77+
let accounts = [alice, bob, charlie];
78+
79+
for account in &accounts {
80+
println!("{}'s ID: {}", account.name, account.id);
81+
}
82+
83+
Ok(accounts)
84+
}
85+
86+
// this needs to be a function because rust doesn't have try blocks.
87+
/// Transfer from `alice` (0) to `charlie` (2) via `bob`'s allowance (1).
88+
async fn transfer(client: &Client, accounts: &[Account; 3], value: Hbar) -> hedera::Result<()> {
89+
let [alice, bob, charlie] = accounts;
90+
// `approved_{hbar,token}_transfer()` means that the transfer has been approved by an allowance
91+
// The allowance spender must be pay the fee for the transaction.
92+
// use `transaction_id()` to set the account ID that will pay the fee for the transaction.
93+
let _ = TransferTransaction::new()
94+
.approved_hbar_transfer(alice.id, -value)
95+
.hbar_transfer(charlie.id, value)
96+
.transaction_id(TransactionId::generate(bob.id))
97+
.freeze_with(client)?
98+
.sign(bob.key.clone())
99+
.execute(client)
100+
.await?
101+
.get_receipt(client)
102+
.await?;
103+
104+
Ok(())
105+
}
106+
107+
async fn demonstrate_allowances(client: &Client, accounts: &[Account; 3]) -> anyhow::Result<()> {
108+
const FIRST_ALLOWANCE_VALUE: Hbar = Hbar::new(2);
109+
const FIRST_TRANSFER_VALUE: Hbar = Hbar::new(1);
110+
const SECOND_ALLOWANCE_VALUE: Hbar = Hbar::new(3);
111+
const SECOND_TRANSFER_VALUE: Hbar = Hbar::new(2);
112+
113+
let [alice, bob, charlie] = accounts;
114+
115+
println!(
116+
"Approving an allowance of {FIRST_ALLOWANCE_VALUE} with owner {} and spender {}",
117+
alice.name, bob.name
118+
);
119+
120+
let _ = AccountAllowanceApproveTransaction::new()
121+
.approve_hbar_allowance(alice.id, bob.id, FIRST_ALLOWANCE_VALUE)
122+
.freeze_with(client)?
123+
.sign(alice.key.clone())
124+
.execute(client)
125+
.await?
126+
.get_receipt(client)
127+
.await?;
128+
129+
print_balances(client, accounts).await?;
130+
131+
println!(
132+
"Transferring {FIRST_TRANSFER_VALUE} from {alice} to {charlie}, but the transaction is signed only by {bob} ({bob} is dipping into their allowance from {alice})",
133+
alice=alice.name,
134+
bob=bob.name,
135+
charlie=charlie.name
136+
);
137+
138+
transfer(client, accounts, FIRST_TRANSFER_VALUE).await?;
139+
140+
let current_balance = FIRST_ALLOWANCE_VALUE - FIRST_TRANSFER_VALUE;
141+
142+
println!(
143+
"Transfer succeeded. {bob} should now have {current_balance} left in their allowance.",
144+
bob = bob.name,
145+
);
146+
147+
print_balances(client, accounts).await?;
148+
149+
println!(
150+
"Attempting to transfer {SECOND_TRANSFER_VALUE} from {alice} to {charlie} using {bob}'s allowance.",
151+
alice=alice.name,
152+
bob=bob.name,
153+
charlie=charlie.name
154+
);
155+
println!(
156+
"This should fail, because there is only {current_balance} left in {bob}'s allowance.",
157+
bob = bob.name
158+
);
159+
160+
match transfer(client, accounts, SECOND_TRANSFER_VALUE).await {
161+
Ok(()) => {
162+
println!("The transfer succeeded. This should not happen.");
163+
}
164+
165+
Err(e) => {
166+
println!("The transfer failed as expected: {e:?}")
167+
}
168+
}
169+
170+
println!(
171+
"Adjusting {bob}'s allowance to {SECOND_ALLOWANCE_VALUE}.",
172+
bob = bob.name
173+
);
174+
175+
let _ = AccountAllowanceApproveTransaction::new()
176+
.approve_hbar_allowance(alice.id, bob.id, SECOND_ALLOWANCE_VALUE)
177+
.freeze_with(client)?
178+
.sign(alice.key.clone())
179+
.execute(client)
180+
.await?
181+
.get_receipt(client)
182+
.await?;
183+
184+
println!(
185+
"Attempting to transfer {SECOND_TRANSFER_VALUE} from {alice} to {charlie} using {bob}'s allowance again.",
186+
alice=alice.name,
187+
bob=bob.name,
188+
charlie=charlie.name
189+
);
190+
println!("This time it should succeed.");
191+
192+
transfer(client, accounts, SECOND_TRANSFER_VALUE).await?;
193+
194+
println!("Transfer succeeded.");
195+
196+
print_balances(client, accounts).await?;
197+
198+
println!("Deleting {bob}'s allowance", bob = bob.name);
199+
200+
let _ = AccountAllowanceApproveTransaction::new()
201+
.approve_hbar_allowance(alice.id, bob.id, Hbar::ZERO)
202+
.freeze_with(client)?
203+
.sign(alice.key.clone())
204+
.execute(client)
205+
.await?
206+
.get_receipt(client)
207+
.await?;
208+
209+
Ok(())
210+
}
211+
212+
async fn clean_up(
213+
client: &Client,
214+
operator_id: AccountId,
215+
accounts: [Account; 3],
216+
) -> anyhow::Result<()> {
217+
println!("Cleaning up...");
218+
219+
for account in accounts {
220+
let _ = AccountDeleteTransaction::new()
221+
.account_id(account.id)
222+
.transfer_account_id(operator_id)
223+
.freeze_with(client)?
224+
.sign(account.key)
225+
.execute(client)
226+
.await?
227+
.get_receipt(client)
228+
.await?;
229+
230+
println!("Deleted `{}` ({})", account.name, account.id);
231+
}
232+
233+
Ok(())
234+
}
235+
236+
#[tokio::main]
237+
async fn main() -> anyhow::Result<()> {
238+
let _ = dotenvy::dotenv();
239+
240+
let args = Args::parse();
241+
242+
let client = Client::for_name(&args.hedera_network)?;
243+
244+
client.set_operator(args.operator_account_id, args.operator_key);
245+
246+
let accounts = create_accounts(&client).await?;
247+
248+
print_balances(&client, &accounts).await?;
249+
250+
demonstrate_allowances(&client, &accounts).await?;
251+
clean_up(&client, args.operator_account_id, accounts).await?;
252+
253+
println!("End of example");
254+
255+
Ok(())
256+
}
257+
258+
async fn print_balances(client: &Client, accounts: &[Account; 3]) -> hedera::Result<()> {
259+
for account in accounts {
260+
let balance = AccountBalanceQuery::new()
261+
.account_id(account.id)
262+
.execute(&client)
263+
.await?
264+
.hbars;
265+
266+
println!("{name}'s balance: {balance}", name = account.name);
267+
}
268+
269+
Ok(())
270+
}
File renamed without changes.

0 commit comments

Comments
 (0)