Skip to content

Commit

Permalink
Merge branch 'ian/update-sdk-docs' (#388)
Browse files Browse the repository at this point in the history
update sdk docs for v0.43.0
  • Loading branch information
brentstone authored Sep 12, 2024
2 parents 8aa60a4 + a485a2d commit dc4b2b2
Show file tree
Hide file tree
Showing 14 changed files with 536 additions and 539 deletions.
57 changes: 7 additions & 50 deletions packages/docs/pages/integrating-with-namada/sdk.mdx
Original file line number Diff line number Diff line change
@@ -1,56 +1,13 @@
import Expandable from '../../components/Expandable';
import { Callout } from 'nextra-theme-docs'

# The Namada SDK

The Namada software development kit (SDK) can be found in the `namada` repo under the path [`namada/crates](https://github.com/anoma/namada/tree/main/crates/sdk). The SDK is written in Rust and can be used to interact with the Namada blockchain, by constructing transactions, signing them, and submitting them to the network.
The Namada software development kit (SDK) can be found in the `namada` repo under the path [`namada/crates/sdk`](https://github.com/anoma/namada/tree/main/crates/sdk).
The SDK is written in Rust and can be used to interact with the Namada blockchain by constructing transactions, signing them, and submitting them to the network.

## Quick Start
This section explains some basic operations using the SDK with example code.

A good starting point to see the SDK in use is to check-out the [namada interface](https://github.com/anoma/namada/tree/main/crates/sdk) repo. This repo contains a simple web application that uses the SDK to interact with the Namada blockchain. However, it is important to note the added complexity arising from the application integrating javascript using [wasm-bindgen](https://rustwasm.github.io/docs/wasm-bindgen/), which is not necessary.

## Installation

The Namada SDK can be installed by creating a new Rust project and adding the following to the `Cargo.toml` file:


<Expandable>

```toml
[package]
name = "namada-sdk-example"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
clap = { version = "4.4.2", features = ["derive", "env"] }
rand = {version = "0.8", default-features = false}
rand_core = {version = "0.6", default-features = false}
serde = { version = "1.0.188", features = ["derive"] }
serde_json = "1.0.107"
namada_sdk = { git = "https://github.com/anoma/namada", rev = "v0.31.8", default-features = false, features = ["tendermint-rpc", "std", "async-send", "download-params", "rand"] }
tendermint-config = "0.34.0"
tendermint-rpc = { version = "0.34.0", features = ["http-client"]}
tokio = {version = "1.8.2", default-features = false}
tempfile = "3.8.0"
async-trait = "0.1.74"
markdown-gen = "1.2.1"
reqwest = "0.11.22"
minio = "0.1.0"
itertools = "0.12.0"

[build-dependencies]
vergen = { version = "8.0.0", features = ["build", "git", "gitcl"] }
```

</Expandable>


Once the sdk is installed, you can use it to interact with the Namada blockchain.

## Table of contents

- [Setting up a client](./sdk/setting-up-a-client.mdx)
- [Setting up a wallet](./sdk/setting-up-a-wallet.mdx)
- [Constructing transfers](./sdk/constructing-transfers.mdx)
<Callout type="info">
Read the following sections in order, as they may refer to variables, imports or actions taken in previous sections.
</Callout>
9 changes: 6 additions & 3 deletions packages/docs/pages/integrating-with-namada/sdk/_meta.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
{
"setting-up-a-client" : "Setting up a client",
"setup": "Project setup",
"context": "Chain context",
"query": "Querying",
"setting-up-a-wallet" : "Setting up a wallet",
"generating-accounts": "Generating accounts",
"constructing-transfers" : "Constructing transfers",
"shielded-sync": "Shielded sync",
"shielded-transfers": "Shielded transfers",
"proof-of-stake" : "Proof of stake transactions",
"governance" : "Governance",
"interface-integration" : "Integrating with the interface"
"governance" : "Governance"
}
Original file line number Diff line number Diff line change
@@ -1,81 +1,46 @@
# Constructing transfers

Now that we have the wallet and client set up, we can create the environment necessary for constructing transfers.
This can be a bit tricky, but the below boilerplate code should do the trick:
The following imports will be needed in order to generate transfers:
The SDK provides methods for constructing, signing, and submitting transactions to the chain. These are accessed through the [Chain Context](./context.mdx) object.

```rust
use namada_sdk::args::InputAmount;
```

After the user has [generated an account](./generating-accounts.mdx) and created the appropriate struct, it is not too difficult to construct and submit transfer transactions.
### Preparation

```rust
let mut namada = NamadaImpl::new(&http_client, &mut wallet, &mut shielded_ctx, &NullIo)
.await
.expect("unable to construct Namada object")
.chain_id(ChainId::from_str("public-testnet-14.5d79b6958580").unwrap());
// Transfer the given amount of native tokens from the source account to the
// destination account.
async fn gen_transfer(
namada: &impl Namada,
source: &Account,
destination: &Account,
amount: InputAmount,
) -> std::result::Result<ProcessTxResponse, namada_sdk::error::Error> {
let mut transfer_tx_builder = namada
.new_transfer(
TransferSource::Address(Address::from(&source.public_key)),
TransferTarget::Address(Address::from(&destination.public_key)),
namada.native_token(),
amount,
)
.signing_keys(vec![source.public_key.clone()]);
let (mut transfer_tx, signing_data, _epoch) = transfer_tx_builder
.build(namada)
.await
.expect("unable to build transfer");
namada
.sign(&mut transfer_tx, args: &transfer_tx_builder.tx, signing_data, with: default_sign, user_data: ())
.await
.expect("unable to sign reveal pk tx");
namada.submit(transfer_tx, &transfer_tx_builder.tx).await
}
```
Let's assume we have two accounts—Alice and Bob—[added to the wallet](./setting-up-a-wallet.mdx) as in the previous section
and that Alice's account already contains sufficient funds.

Other transactions can be constructed in a similar fashion.

## Shielded transfers

In order to make the transfer shielded, we need to the only difference is to use shielded addresses and keys instead of transparent ones.

It is important to use the shielded extended `SpendingKey` as the source.
### Transparent Transfers
The following code will build a `transparent-transfer` tx that sends 1 NAM from Alice to Bob, then sign it and submit
it to the chain.

```rust
use std::str::FromStr;
use namada_sdk::{
masp::{
PaymentAddress,
TransferSource,
TransferTarget,
},
masp_primitives::zip32::ExtendedSpendingKey,
key::common::PublicKey,
use namada_sdk::args::TxTransparentTransferData;
use namada_sdk::args::InputAmount;
use namada_sdk::signing::default_sign;

// Prepare the tx arguments
let data = TxTransparentTransferData {
source: sdk.wallet().await.find_address("alice".to_string()).expect("Invalid alias").into_owned(),
target: sdk.wallet().await.find_address("bob".to_string()).expect("Invalid alias").into_owned(),
token: sdk.native_token(),
amount: InputAmount::from_str("1").expect("Invalid amount"),
};

// Make sure to replace "secret-ex" with an actual Namada extended spending key
let source = ExtendedSpendingKey::from_str("secret-ex").unwrap();
// Make sure to replace "payment-addr-ex" with an actual Namada payment address
let destination = PaymentAddress::from_str("payment-addr-ex").unwrap();
// Make sure to replace "public-key" with an actual Namada public key
let fee_payer = PublicKey::from_str("public-key");
let amount = /* Needed amount here */;
let mut transfer_tx_builder = namada
.new_transfer(
TransferSource::ExtendedSpendingKey(source.into()),
TransferTarget::PaymentAddress(destination),
namada.native_token(),
amount,
)
.signing_keys(vec![fee_payer]);
```
// Build the tx
let mut transfer_tx_builder = sdk
.new_transparent_transfer(vec![data])
.signing_keys(vec![alice_acct.public_key.clone()]);
let (mut transfer_tx, signing_data) = transfer_tx_builder
.build(&sdk)
.await
.expect("unable to build transfer");

// Sign the tx
sdk.sign(&mut transfer_tx, &transfer_tx_builder.tx, signing_data, default_sign, ())
.await
.expect("unable to sign transparent-transfer tx");

// Submit the signed tx to the ledger for execution
match sdk.submit(transfer_tx, &transfer_tx_builder.tx).await {
Ok(res) => println!("Tx result: {:?}", res),
Err(e) => println!("Tx error: {:?}", e)
}
```
85 changes: 85 additions & 0 deletions packages/docs/pages/integrating-with-namada/sdk/context.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { Callout } from 'nextra-theme-docs';

# Initializing the Chain Context

To begin working with the SDK, the first step is to initialize a 'Chain Context'. This is an instance of the
[`NamadaImpl struct`](https://github.com/anoma/namada/blob/main/crates/sdk/src/lib.rs#L659), which allows us to access
wallet storage, shielded context storage, and the RPC endpoint of our full node.

The Chain Context object also provides methods for common operations like querying the chain, constructing, signing,
and submitting transactions.

To create the Chain Context, we'll need the RPC url of a synced full node.

### Code

First, import our dependencies:
```rust
use tokio;
use std::str::FromStr;
use namada_sdk::{args::TxBuilder, io::NullIo, masp::fs::FsShieldedUtils, wallet::fs::FsWalletUtils, NamadaImpl, chain::ChainId};
use tendermint_rpc::{HttpClient, Url};
```

Next, we'll create the required helper objects.

Instantiate an http client for interacting with the full node:
```rust
let url = Url::from_str("http://127.0.0.1:26657").expect("Invalid RPC address");
let http_client = HttpClient::new(url).unwrap();
```

Set up wallet storage for our addresses and keys:
```rust
let wallet = FsWalletUtils::new("./sdk-wallet".into());
```

Set up shielded context storage for the current state of the shielded pool:
```rust
let shielded_ctx = FsShieldedUtils::new("./masp".into());
```

Create an IO object to capture the input/output streams. Since we don't need to
capture anything, we'll use `NullIo`
```rust
let null_io = NullIo;
```

Now we're ready to instantiate our Chain Context:
```rust
let sdk = NamadaImpl::new(http_client, wallet, shielded_ctx, null_io)
.await
.expect("unable to initialize Namada context")
.chain_id(ChainId::from_str("local-net.b8f955720ab").unwrap());
```

### Full Example:

```rust
use tokio;
use std::str::FromStr;
use namada_sdk::{args::TxBuilder, io::NullIo, masp::fs::FsShieldedUtils, rpc, wallet::fs::FsWalletUtils, Namada, NamadaImpl, chain::ChainId};
use tendermint_rpc::{HttpClient, Url};

#[tokio::main]
async fn main() {

let url = Url::from_str("http://127.0.0.1:26657").expect("Invalid RPC address");
let http_client = HttpClient::new(url).unwrap();

let wallet = FsWalletUtils::new("./sdk-wallet".into());
let shielded_ctx = FsShieldedUtils::new("./masp".into());
let null_io = NullIo;

let sdk = NamadaImpl::new(http_client, wallet, shielded_ctx, null_io)
.await
.expect("unable to initialize Namada context")
.chain_id(ChainId::from_str("local-net.b8f955720ab").unwrap());
}
```
The paths provided when creating `wallet` and `shielded_ctx` are the directories where the shielded context data and `wallet.toml` will be
saved to/read from.

<Callout type="info">
All following pages in this section assume you have already created a Chain Context with the name `sdk`.
</Callout>
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
# Generating accounts

## Representing accounts
Representing accounts using the Namada SDK is straightforward. An account on Namada is defined by its public key(s) and private key(s) (plural for multisignatures). The public key(s) is/are used to identify the account and the private key is used to sign transactions. In the below snippet, we represent the account using the public key and private key.
### Representing accounts
Representing accounts using the Namada SDK is straightforward. An account on Namada is defined by its public key(s) and private key(s)
(plural for multi-signature accounts). The public key(s) is/are used to identify the account and the private key is used to sign transactions.
In the below snippet, we create a struct to represent the account using the public key and private key.

```rust
use namada_sdk::key::common::{PublicKey, SecretKey};
struct SimpleAccount {
public_key: PublicKey,
private_key: SecretKey
public_key: PublicKey,
private_key: SecretKey
}
```

Expand All @@ -16,60 +18,48 @@ For a multisignature account, we can represent this through a vector of keys.
```rust
use namada_sdk::key::common::{PublicKey, SecretKey};
struct MultisigAccount {
public_keys: Vec<PublicKey>,
private_keys: Vec<SecretKey>
public_keys: Vec<PublicKey>,
private_keys: Vec<SecretKey>
}
```

Multisignature accounts, because they are initialized by an on-chain transaction, will always have their public key revealed to the ledger. However, when keypairs are generated in an offline fashion, the user must submit a transaction in order to reveal their public key. Because of this, it is helpful to add the field `revealed` to the account struct.

```rust
use namada_sdk::key::common::{PublicKey, SecretKey};
struct Account {
public_key: PublicKey,
private_key: SecretKey,
revealed: bool
struct SimpleAccount {
public_key: PublicKey,
private_key: SecretKey,
revealed: bool,
}
```

## Revealing the public key of an implicit account
### Revealing the public key of an implicit account

In order to reveal the public key of an implicit account, the user must submit a transaction to the ledger.

```rust
use namada_sdk::io::NullIo;
use namada_sdk::NamadaImpl;
use namada_sdk::chain::ChainId;

use namada_sdk::signing::default_sign;

// Define the namada implementation (assuming we have a wallet, http_client, and shielded_ctx)
let mut namada = NamadaImpl::new(&http_client, &mut wallet, &mut shielded_ctx, &NullIo)
.await
.expect("unable to construct Namada object")
.chain_id(ChainId::from_str("shielded-expedition.88f17d1d14").unwrap());
// Build the reveal pk transaction using the sdk object
let reveal_tx_builder = sdk
.new_reveal_pk(account.public_key.clone())
.signing_keys(vec![account.public_key.clone()]);
let (mut reveal_tx, signing_data) = reveal_tx_builder
.build(&sdk)
.await
.expect("unable to build reveal pk tx");

// Generate an account (assuming sk is a SecretKey)
let account = Account {
public_key: sk.to_public(),
private_key: sk,
revealed: false,
};

// Build the reveal pk transaction using the NamadaImpl object
let reveal_tx_builder = namada
.new_reveal_pk(account.public_key.clone())
.signing_keys(vec![account.private_key.clone()]);
let (mut reveal_tx, signing_data, _) = reveal_tx_builder
.build(namada)
.await
.expect("unable to build reveal pk tx");
// Sign the transaction
namada
.sign(&mut reveal_tx, &reveal_tx_builder.tx, signing_data)
.await
.expect("unable to sign reveal pk tx");
sdk.sign(&mut reveal_tx, &reveal_tx_builder.tx, signing_data, default_sign, ())
.await
.expect("unable to sign reveal pk tx");

// Submit the signed tx to the ledger for execution
namada.submit(reveal_tx.clone(), reveal_tx_builder)
// Assumes account already has funds to cover gas costs
match sdk.submit(reveal_tx.clone(), &reveal_tx_builder.tx).await {
Ok(res) => println!("Tx result: {:?}", res),
Err(e) => println!("Tx error: {:?}", e)
}
```

Once the public key is revealed, the account can be used to sign transactions.
Loading

0 comments on commit dc4b2b2

Please sign in to comment.