Skip to content

Commit

Permalink
Make Rust first-transaction example in dev docs site use the Rust SDK (
Browse files Browse the repository at this point in the history
…aptos-labs#2870)

* Update first transaction tutorial with TS SDK

* Make Rust first-transaction example in dev docs site use the Rust SDK
  • Loading branch information
banool authored Aug 25, 2022
1 parent c8cee31 commit 71c0e71
Show file tree
Hide file tree
Showing 9 changed files with 380 additions and 15 deletions.
5 changes: 5 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions developer-docs-site/docs/sdks/rust-sdk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
title: "Rust SDK"
slug: "rust-sdk"
---

# Aptos Rust SDK

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).

For now the best way to use the Rust SDK is to add a dependency on the git repo directly, like this:
```toml
aptos-sdk = { git = "https://github.com/aptos-labs/aptos-core", branch = "devnet" }
```

The source code is available in the [aptos-core GitHub repository](https://github.com/aptos-labs/aptos-core/tree/main/sdk).
99 changes: 85 additions & 14 deletions developer-docs-site/docs/tutorials/first-transaction-sdk.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ This tutorial introduces the Aptos SDKs and how to generate, submit, and verify

* [Official Aptos Typescript SDK][typescript-sdk]
* [Official Aptos Python SDK][python-sdk]
* Official Aptos Rust SDK*TBA*
* [Official Aptos Rust SDK][rust-sdk]

## Step 2: Run the Example

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

In progress.
Navigate to the Rust SDK directory:
```sh
cd ~/aptos-core/sdk
```

Run the `transfer-coin` example:
```sh
cargo run --example transfer-coin
```
</TabItem>
</Tabs>

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

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

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.

:::tip See the example full listing
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.
<Tabs groupId="sdk-examples">
<TabItem value="typescript" label="Typescript">

:::tip See the full example
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.
:::
</TabItem>
<TabItem value="python" label="Python">

:::tip See the full example
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.
:::
</TabItem>
<TabItem value="rust" label="Rust">

:::tip See the full example
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.
:::
</TabItem>
</Tabs>

### Step 4.1: Initializing the Clients

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

In progress.
```rust
:!: static/sdks/rust/examples/transfer-coin.rs section_1a
```

Using the API client we can create a `CoinClient`, which we use for common coin operations such as transferring coins and checking balances.
```rust
:!: static/sdks/rust/examples/transfer-coin.rs section_1b
```

In the example we initialize the URL values as such:
```rust
:!: static/sdks/rust/examples/transfer-coin.rs section_1c
```
</TabItem>
</Tabs>

:::tip

By default the URLs for both the services point to Aptos devnet services. However, they can be configured with the following environment variables:
- `APTOS_NODE_URL` and
- `APTOS_FAUCET_URL`.
- `APTOS_NODE_URL`
- `APTOS_FAUCET_URL`
:::

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

In progress.
```rust
:!: static/sdks/rust/examples/transfer-coin.rs section_2
```
</TabItem>
</Tabs>

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

In progress.
```rust
:!: static/sdks/rust/examples/transfer-coin.rs section_3
```
</TabItem>
</Tabs>

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

In progress.
```rust
:!: static/sdks/rust/examples/transfer-coin.rs section_4
```

Behind the scenes, the SDK queries the CoinStore resource for the AptosCoin and reads the current stored value:
```rust
let balance = self
.get_account_resource(address, "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>")
.await?;
```
</TabItem>
</Tabs>

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

In progress.
```rust
:!: static/sdks/rust/examples/transfer-coin.rs section_5
```

Behind the scenes the Rust SDK generates, signs, and submits a transaction:
```rust
:!: static/sdks/rust/src/coin_client.rs section_1
```

Breaking the above down into pieces:
1. First, we fetch the chain ID, necessary for building the transaction payload.
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.
1. The Move function is stored on the coin module: `0x1::coin`.
1. Because the Coin module can be used by other coins, the transfer must explicitly use a `TypeTag` to define which coin to transfer.
1. The transaction arguments, such as `to_account` and `amount`, must be encoded as BCS to use with the `TransactionBuilder`.


</TabItem>
</Tabs>

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

In progress.
The transaction hash can be used to query the status of a transaction:

```rust
:!: static/sdks/rust/examples/transfer-coin.rs section_6
```
</TabItem>
</Tabs>

[account_basics]: /concepts/basics-accounts
[python-sdk]: /sdks/python-sdk
[typescript-sdk]: /sdks/typescript-sdk
[python-sdk]: /sdks/python-sdk
[rust-sdk]: /sdks/rust-sdk
[rest_spec]: https://fullnode.devnet.aptoslabs.com/v1/spec#/
8 changes: 7 additions & 1 deletion developer-docs-site/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,13 @@ const sidebars = {
label: "SDKs",
collapsible: true,
collapsed: true,
items: ["sdks/typescript-sdk", "sdks/aptos-sdk-overview", "sdks/transactions-with-ts-sdk", "sdks/python-sdk"],
items: [
"sdks/aptos-sdk-overview",
"sdks/typescript-sdk",
"sdks/transactions-with-ts-sdk",
"sdks/python-sdk",
"sdks/rust-sdk",
],
},
{
type: "category",
Expand Down
1 change: 1 addition & 0 deletions developer-docs-site/static/sdks/rust
8 changes: 8 additions & 0 deletions sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ publish = false
edition = "2018"

[dependencies]
anyhow = "1.0.57"
aptos-crypto = { path = "../crates/aptos-crypto" }
aptos-rest-client = { path = "../crates/aptos-rest-client" }
aptos-types = { path = "../types" }
Expand All @@ -18,3 +19,10 @@ cached-packages = { path = "../aptos-move/framework/cached-packages" }
move-deps = { path = "../aptos-move/move-deps", features = ["address32"] }
rand_core = "0.5.1"
serde = { version = "1.0.137", features = ["derive"] }

# Used by the examples.
[dev-dependencies]
once_cell = "1.13.0"
rand = "0.7.3"
tokio = { version = "1.18.2", features = ["macros", "rt-multi-thread"] }
url = "2.2.2"
139 changes: 139 additions & 0 deletions sdk/examples/transfer-coin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use anyhow::{Context, Result};
use aptos_sdk::coin_client::CoinClient;
use aptos_sdk::rest_client::{Client, FaucetClient};
use aptos_sdk::types::LocalAccount;
use once_cell::sync::Lazy;
use std::str::FromStr;
use url::Url;

// :!:>section_1c
static NODE_URL: Lazy<Url> = Lazy::new(|| {
Url::from_str(
std::env::var("APTOS_NODE_URL")
.as_ref()
.map(|s| s.as_str())
.unwrap_or("https://fullnode.devnet.aptoslabs.com"),
)
.unwrap()
});

static FAUCET_URL: Lazy<Url> = Lazy::new(|| {
Url::from_str(
std::env::var("APTOS_FAUCET_URL")
.as_ref()
.map(|s| s.as_str())
.unwrap_or("https://faucet.devnet.aptoslabs.com"),
)
.unwrap()
});
// <:!:section_1c

#[tokio::main]
async fn main() -> Result<()> {
// :!:>section_1a
let rest_client = Client::new(NODE_URL.clone());
let faucet_client = FaucetClient::new(FAUCET_URL.clone(), NODE_URL.clone()); // <:!:section_1a

// :!:>section_1b
let coin_client = CoinClient::new(&rest_client); // <:!:section_1b

// Create two accounts locally, Alice and Bob.
// :!:>section_2
let mut alice = LocalAccount::generate(&mut rand::rngs::OsRng);
let bob = LocalAccount::generate(&mut rand::rngs::OsRng); // <:!:section_2

// Print account addresses.
println!("\n=== Addresses ===");
println!("Alice: {}", alice.address().to_hex_literal());
println!("Bob: {}", bob.address().to_hex_literal());

// Create the accounts on chain, but only fund Alice.
// :!:>section_3
faucet_client
.fund(alice.address(), 20_000)
.await
.context("Failed to fund Alice's account")?;
faucet_client
.create_account(bob.address())
.await
.context("Failed to fund Bob's account")?; // <:!:section_3

// Print initial balances.
println!("\n=== Initial Balances ===");
println!(
"Alice: {:?}",
coin_client
.get_account_balance(&alice.address())
.await
.context("Failed to get Alice's account balance")?
);
println!(
"Bob: {:?}",
coin_client
.get_account_balance(&bob.address())
.await
.context("Failed to get Bob's account balance")?
);

// Have Alice send Bob some coins.
let txn_hash = coin_client
.transfer(&mut alice, bob.address(), 1_000, None)
.await
.context("Failed to submit transaction to transfer coins")?;
rest_client
.wait_for_transaction(&txn_hash)
.await
.context("Failed when waiting for the transfer transaction")?;

// Print intermediate balances.
println!("\n=== Intermediate Balances ===");
// :!:>section_4
println!(
"Alice: {:?}",
coin_client
.get_account_balance(&alice.address())
.await
.context("Failed to get Alice's account balance the second time")?
);
println!(
"Bob: {:?}",
coin_client
.get_account_balance(&bob.address())
.await
.context("Failed to get Bob's account balance the second time")?
); // <:!:section_4

// Have Alice send Bob some more coins.
// :!:>section_5
let txn_hash = coin_client
.transfer(&mut alice, bob.address(), 1_000, None)
.await
.context("Failed to submit transaction to transfer coins")?; // <:!:section_5
// :!:>section_6
rest_client
.wait_for_transaction(&txn_hash)
.await
.context("Failed when waiting for the transfer transaction")?; // <:!:section_6

// Print final balances.
println!("\n=== Final Balances ===");
println!(
"Alice: {:?}",
coin_client
.get_account_balance(&alice.address())
.await
.context("Failed to get Alice's account balance the second time")?
);
println!(
"Bob: {:?}",
coin_client
.get_account_balance(&bob.address())
.await
.context("Failed to get Bob's account balance the second time")?
);

Ok(())
}
Loading

0 comments on commit 71c0e71

Please sign in to comment.