Skip to content

Commit 71c0e71

Browse files
authored
Make Rust first-transaction example in dev docs site use the Rust SDK (aptos-labs#2870)
* Update first transaction tutorial with TS SDK * Make Rust first-transaction example in dev docs site use the Rust SDK
1 parent c8cee31 commit 71c0e71

File tree

9 files changed

+380
-15
lines changed

9 files changed

+380
-15
lines changed

Cargo.lock

+5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
title: "Rust SDK"
3+
slug: "rust-sdk"
4+
---
5+
6+
# Aptos Rust SDK
7+
8+
Aptos provides an official Rust SDK. The Rust SDK is tested carefully, though it isn't as popular as the [Typescript SDK](/sdks/typescript-sdk).
9+
10+
For now the best way to use the Rust SDK is to add a dependency on the git repo directly, like this:
11+
```toml
12+
aptos-sdk = { git = "https://github.com/aptos-labs/aptos-core", branch = "devnet" }
13+
```
14+
15+
The source code is available in the [aptos-core GitHub repository](https://github.com/aptos-labs/aptos-core/tree/main/sdk).

developer-docs-site/docs/tutorials/first-transaction-sdk.md

+85-14
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ This tutorial introduces the Aptos SDKs and how to generate, submit, and verify
1515

1616
* [Official Aptos Typescript SDK][typescript-sdk]
1717
* [Official Aptos Python SDK][python-sdk]
18-
* Official Aptos Rust SDK*TBA*
18+
* [Official Aptos Rust SDK][rust-sdk]
1919

2020
## Step 2: Run the Example
2121

@@ -63,7 +63,15 @@ git clone [email protected]:aptos-labs/aptos-core.git ~/aptos-core
6363
</TabItem>
6464
<TabItem value="rust" label="Rust">
6565

66-
In progress.
66+
Navigate to the Rust SDK directory:
67+
```sh
68+
cd ~/aptos-core/sdk
69+
```
70+
71+
Run the `transfer-coin` example:
72+
```sh
73+
cargo run --example transfer-coin
74+
```
6775
</TabItem>
6876
</Tabs>
6977

@@ -96,7 +104,7 @@ The above output demonstrates that the `transfer-coin` example executes the foll
96104
* The funding and creation of Alice's account from a faucet.
97105
* The creation of Bob's account from a faucet.
98106
* The transferring of 1000 coins from Alice to Bob.
99-
* The 4 coins of gas paid for by Alice to make that tansfer.
107+
* The 4 coins of gas paid for by Alice to make that transfer.
100108
* Another transfer of 1000 coins from Alice to Bob.
101109
* The additional 4 coins of gas paid for by Alice to make that transfer.
102110

@@ -106,9 +114,26 @@ Next, see below a walk-through of the Python SDK functions that are used to acco
106114

107115
The `transfer-coin` example code uses helper functions to interact with the [REST API][rest_spec]. This section reviews each of the calls and gives insights into functionality.
108116

109-
:::tip See the example full listing
110-
See the [`transfer-coin`](https://github.com/aptos-labs/aptos-core/blob/main/ecosystem/python/sdk/examples/transfer-coin.py) for the complete code as you follow the below steps.
117+
<Tabs groupId="sdk-examples">
118+
<TabItem value="typescript" label="Typescript">
119+
120+
:::tip See the full example
121+
See [`transfer-coin`](https://github.com/aptos-labs/aptos-core/blob/main/ecosystem/typescript/sdk/examples/typescript/transfer_coin.ts) for the complete code as you follow the below steps.
111122
:::
123+
</TabItem>
124+
<TabItem value="python" label="Python">
125+
126+
:::tip See the full example
127+
See [`transfer-coin`](https://github.com/aptos-labs/aptos-core/blob/main/ecosystem/python/sdk/examples/transfer-coin.py) for the complete code as you follow the below steps.
128+
:::
129+
</TabItem>
130+
<TabItem value="rust" label="Rust">
131+
132+
:::tip See the full example
133+
See [`transfer-coin`](https://github.com/aptos-labs/aptos-core/blob/main/sdk/examples/transfer-coin.rs) for the complete code as you follow the below steps.
134+
:::
135+
</TabItem>
136+
</Tabs>
112137

113138
### Step 4.1: Initializing the Clients
114139

@@ -148,15 +173,27 @@ The [`common.py`](https://github.com/aptos-labs/aptos-core/tree/main/ecosystem/p
148173
</TabItem>
149174
<TabItem value="rust" label="Rust">
150175

151-
In progress.
176+
```rust
177+
:!: static/sdks/rust/examples/transfer-coin.rs section_1a
178+
```
179+
180+
Using the API client we can create a `CoinClient`, which we use for common coin operations such as transferring coins and checking balances.
181+
```rust
182+
:!: static/sdks/rust/examples/transfer-coin.rs section_1b
183+
```
184+
185+
In the example we initialize the URL values as such:
186+
```rust
187+
:!: static/sdks/rust/examples/transfer-coin.rs section_1c
188+
```
152189
</TabItem>
153190
</Tabs>
154191

155192
:::tip
156193

157194
By default the URLs for both the services point to Aptos devnet services. However, they can be configured with the following environment variables:
158-
- `APTOS_NODE_URL` and
159-
- `APTOS_FAUCET_URL`.
195+
- `APTOS_NODE_URL`
196+
- `APTOS_FAUCET_URL`
160197
:::
161198

162199
### Step 4.2: Creating local accounts
@@ -178,7 +215,9 @@ The next step is to create two accounts locally. [Accounts][account_basics] repr
178215
</TabItem>
179216
<TabItem value="rust" label="Rust">
180217

181-
In progress.
218+
```rust
219+
:!: static/sdks/rust/examples/transfer-coin.rs section_2
220+
```
182221
</TabItem>
183222
</Tabs>
184223

@@ -201,7 +240,9 @@ In Aptos, each account must have an on-chain representation in order to support
201240
</TabItem>
202241
<TabItem value="rust" label="Rust">
203242

204-
In progress.
243+
```rust
244+
:!: static/sdks/rust/examples/transfer-coin.rs section_3
245+
```
205246
</TabItem>
206247
</Tabs>
207248

@@ -239,7 +280,16 @@ def account_balance(self, account_address: str) -> int:
239280
</TabItem>
240281
<TabItem value="rust" label="Rust">
241282

242-
In progress.
283+
```rust
284+
:!: static/sdks/rust/examples/transfer-coin.rs section_4
285+
```
286+
287+
Behind the scenes, the SDK queries the CoinStore resource for the AptosCoin and reads the current stored value:
288+
```rust
289+
let balance = self
290+
.get_account_resource(address, "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>")
291+
.await?;
292+
```
243293
</TabItem>
244294
</Tabs>
245295

@@ -292,7 +342,23 @@ Breaking the above down into pieces:
292342
</TabItem>
293343
<TabItem value="rust" label="Rust">
294344

295-
In progress.
345+
```rust
346+
:!: static/sdks/rust/examples/transfer-coin.rs section_5
347+
```
348+
349+
Behind the scenes the Rust SDK generates, signs, and submits a transaction:
350+
```rust
351+
:!: static/sdks/rust/src/coin_client.rs section_1
352+
```
353+
354+
Breaking the above down into pieces:
355+
1. First, we fetch the chain ID, necessary for building the transaction payload.
356+
1. `transfer` internally is a `EntryFunction` in the [Coin Move module](https://github.com/aptos-labs/aptos-core/blob/main/aptos-move/framework/aptos-framework/sources/coin.move#L412), i.e. an entry function in Move that is directly callable.
357+
1. The Move function is stored on the coin module: `0x1::coin`.
358+
1. Because the Coin module can be used by other coins, the transfer must explicitly use a `TypeTag` to define which coin to transfer.
359+
1. The transaction arguments, such as `to_account` and `amount`, must be encoded as BCS to use with the `TransactionBuilder`.
360+
361+
296362
</TabItem>
297363
</Tabs>
298364

@@ -319,11 +385,16 @@ The transaction hash can be used to query the status of a transaction:
319385
</TabItem>
320386
<TabItem value="rust" label="Rust">
321387

322-
In progress.
388+
The transaction hash can be used to query the status of a transaction:
389+
390+
```rust
391+
:!: static/sdks/rust/examples/transfer-coin.rs section_6
392+
```
323393
</TabItem>
324394
</Tabs>
325395

326396
[account_basics]: /concepts/basics-accounts
327-
[python-sdk]: /sdks/python-sdk
328397
[typescript-sdk]: /sdks/typescript-sdk
398+
[python-sdk]: /sdks/python-sdk
399+
[rust-sdk]: /sdks/rust-sdk
329400
[rest_spec]: https://fullnode.devnet.aptoslabs.com/v1/spec#/

developer-docs-site/sidebars.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,13 @@ const sidebars = {
148148
label: "SDKs",
149149
collapsible: true,
150150
collapsed: true,
151-
items: ["sdks/typescript-sdk", "sdks/aptos-sdk-overview", "sdks/transactions-with-ts-sdk", "sdks/python-sdk"],
151+
items: [
152+
"sdks/aptos-sdk-overview",
153+
"sdks/typescript-sdk",
154+
"sdks/transactions-with-ts-sdk",
155+
"sdks/python-sdk",
156+
"sdks/rust-sdk",
157+
],
152158
},
153159
{
154160
type: "category",

developer-docs-site/static/sdks/rust

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../../sdk/

sdk/Cargo.toml

+8
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ publish = false
1010
edition = "2018"
1111

1212
[dependencies]
13+
anyhow = "1.0.57"
1314
aptos-crypto = { path = "../crates/aptos-crypto" }
1415
aptos-rest-client = { path = "../crates/aptos-rest-client" }
1516
aptos-types = { path = "../types" }
@@ -18,3 +19,10 @@ cached-packages = { path = "../aptos-move/framework/cached-packages" }
1819
move-deps = { path = "../aptos-move/move-deps", features = ["address32"] }
1920
rand_core = "0.5.1"
2021
serde = { version = "1.0.137", features = ["derive"] }
22+
23+
# Used by the examples.
24+
[dev-dependencies]
25+
once_cell = "1.13.0"
26+
rand = "0.7.3"
27+
tokio = { version = "1.18.2", features = ["macros", "rt-multi-thread"] }
28+
url = "2.2.2"

sdk/examples/transfer-coin.rs

+139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// Copyright (c) Aptos
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use anyhow::{Context, Result};
5+
use aptos_sdk::coin_client::CoinClient;
6+
use aptos_sdk::rest_client::{Client, FaucetClient};
7+
use aptos_sdk::types::LocalAccount;
8+
use once_cell::sync::Lazy;
9+
use std::str::FromStr;
10+
use url::Url;
11+
12+
// :!:>section_1c
13+
static NODE_URL: Lazy<Url> = Lazy::new(|| {
14+
Url::from_str(
15+
std::env::var("APTOS_NODE_URL")
16+
.as_ref()
17+
.map(|s| s.as_str())
18+
.unwrap_or("https://fullnode.devnet.aptoslabs.com"),
19+
)
20+
.unwrap()
21+
});
22+
23+
static FAUCET_URL: Lazy<Url> = Lazy::new(|| {
24+
Url::from_str(
25+
std::env::var("APTOS_FAUCET_URL")
26+
.as_ref()
27+
.map(|s| s.as_str())
28+
.unwrap_or("https://faucet.devnet.aptoslabs.com"),
29+
)
30+
.unwrap()
31+
});
32+
// <:!:section_1c
33+
34+
#[tokio::main]
35+
async fn main() -> Result<()> {
36+
// :!:>section_1a
37+
let rest_client = Client::new(NODE_URL.clone());
38+
let faucet_client = FaucetClient::new(FAUCET_URL.clone(), NODE_URL.clone()); // <:!:section_1a
39+
40+
// :!:>section_1b
41+
let coin_client = CoinClient::new(&rest_client); // <:!:section_1b
42+
43+
// Create two accounts locally, Alice and Bob.
44+
// :!:>section_2
45+
let mut alice = LocalAccount::generate(&mut rand::rngs::OsRng);
46+
let bob = LocalAccount::generate(&mut rand::rngs::OsRng); // <:!:section_2
47+
48+
// Print account addresses.
49+
println!("\n=== Addresses ===");
50+
println!("Alice: {}", alice.address().to_hex_literal());
51+
println!("Bob: {}", bob.address().to_hex_literal());
52+
53+
// Create the accounts on chain, but only fund Alice.
54+
// :!:>section_3
55+
faucet_client
56+
.fund(alice.address(), 20_000)
57+
.await
58+
.context("Failed to fund Alice's account")?;
59+
faucet_client
60+
.create_account(bob.address())
61+
.await
62+
.context("Failed to fund Bob's account")?; // <:!:section_3
63+
64+
// Print initial balances.
65+
println!("\n=== Initial Balances ===");
66+
println!(
67+
"Alice: {:?}",
68+
coin_client
69+
.get_account_balance(&alice.address())
70+
.await
71+
.context("Failed to get Alice's account balance")?
72+
);
73+
println!(
74+
"Bob: {:?}",
75+
coin_client
76+
.get_account_balance(&bob.address())
77+
.await
78+
.context("Failed to get Bob's account balance")?
79+
);
80+
81+
// Have Alice send Bob some coins.
82+
let txn_hash = coin_client
83+
.transfer(&mut alice, bob.address(), 1_000, None)
84+
.await
85+
.context("Failed to submit transaction to transfer coins")?;
86+
rest_client
87+
.wait_for_transaction(&txn_hash)
88+
.await
89+
.context("Failed when waiting for the transfer transaction")?;
90+
91+
// Print intermediate balances.
92+
println!("\n=== Intermediate Balances ===");
93+
// :!:>section_4
94+
println!(
95+
"Alice: {:?}",
96+
coin_client
97+
.get_account_balance(&alice.address())
98+
.await
99+
.context("Failed to get Alice's account balance the second time")?
100+
);
101+
println!(
102+
"Bob: {:?}",
103+
coin_client
104+
.get_account_balance(&bob.address())
105+
.await
106+
.context("Failed to get Bob's account balance the second time")?
107+
); // <:!:section_4
108+
109+
// Have Alice send Bob some more coins.
110+
// :!:>section_5
111+
let txn_hash = coin_client
112+
.transfer(&mut alice, bob.address(), 1_000, None)
113+
.await
114+
.context("Failed to submit transaction to transfer coins")?; // <:!:section_5
115+
// :!:>section_6
116+
rest_client
117+
.wait_for_transaction(&txn_hash)
118+
.await
119+
.context("Failed when waiting for the transfer transaction")?; // <:!:section_6
120+
121+
// Print final balances.
122+
println!("\n=== Final Balances ===");
123+
println!(
124+
"Alice: {:?}",
125+
coin_client
126+
.get_account_balance(&alice.address())
127+
.await
128+
.context("Failed to get Alice's account balance the second time")?
129+
);
130+
println!(
131+
"Bob: {:?}",
132+
coin_client
133+
.get_account_balance(&bob.address())
134+
.await
135+
.context("Failed to get Bob's account balance the second time")?
136+
);
137+
138+
Ok(())
139+
}

0 commit comments

Comments
 (0)