Send an Algorand Transaction Using Rust!
Algorand currently officially supports SDKs for JavaScript, Python, Java and Go. A community SDK is available for Rust and I am personally developing an alternative one.
In this tutorial I want to walk you through the dependencies of the project and introducing you to some basic concepts of Algorand’s SDKs that will allow you to send a transaction written only in Rust code.
Requirements
- Basic Rust Language knowledge
- Familiarity with the installation of Docker Compose and Algorand’s Sandbox
Steps
1. Installing Rust Language
Let’s start by installing Rust. If you are running Linux or macOS it is going to be as easy as running the following command:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
but feel free to follow the installation guide.
2. Installing Algorand Sandbox
Now it’s time to get started with Algorand, we will need a node running on our machine. The easiest way to do this is using the Algorand Sandbox. The main requirement is Docker Compose, once you have it installed, using the sandbox is straightforward, just run the following commands:
git clone https://github.com/algorand/sandbox.git
cd sandbox
./sandbox up
As described in the readme, the sandbox creates the following endpoints:
algod
:- address:
http://localhost:4001
- token:
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
kmd
:- address:
http://localhost:4002
- token:
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
indexer
:- address:
http://localhost:8980
for a detailed description of Algorand node’s artifacts refer to the official documentation. Here we can briefly say that algod
(Algorand Daemon) is at the core of an Algorand node, it handles the blockchain, process the messages with other nodes, runs the consensus protocol, and writes blocks to the disk. Finally, as you can see, exposes an API that developers can use to interact with it.
kmd
is Algorand’s key management daemon, which is the process responsible for handling client’s private keys. It can create or import spending keys, sign transactions, and interact with key storage mechanisms. Being a separate process, a user can run it on a different machine effectively isolating the keys from the network. Also, this process exposes an API that developers can call in their applications.
Finally, the indexer
is an optional component that allows searching the blockchain for transactions, assets, accounts, and blocks with various criteria.
The sandbox also creates its own private network sandnet-v1
with a list of accounts owning some ALGO, which will be very useful in the next section. In order to see this list just run:
./sandbox goal account list
3. Create a New Project
Now it’s time to send a transaction on the private Algorand network kindly offered by our sandbox.
If your sandbox is running and you found the list of accounts with a positive balance in your wallet we are ready to go.
First, create a new project with:
cargo new my-first-transaction
4. Declare Dependencies
Then open the file Cargo.toml
in the root folder of your new project and add the following dependency:
[dependencies]
algonaut = {git = "https://github.com/manuelmauro/algonaut", rev = "11808fa3650712bbd903560306400730ecc1d7b5"}
This is the single dependency that we will need for this simple example, now let’s head to /src/main.rs
and write some code.
5. Use Statements and the Main Function
First we need to include some use
statements:
use algonaut::core::{Address, MicroAlgos};
use algonaut::transaction::{BaseTransaction, Payment, Transaction, TransactionType};
use algonaut::{Algod, Kmd};
use std::error::Error;
and let’s start writing our main function.
fn main() -> Result<(), Box<dyn Error>> {
let kmd = Kmd::new()
.bind("http://localhost:4002")
.auth("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
.client_v1()?;
In this tutorial we will use a kmd
client in order to showcase its usage, but this is not needed in general. In fact, most of the Algorand SDKs also allow creating Algorand accounts without the need to manage wallets.
To create a client for the kmd
daemon, we need some information about our sandbox that we discovered earlier, namely the address and the token.
Also, our main
function will return a Result
to easily manage some unwrapping using the ?
operator.
6. Locate Sandbox’s Default Wallet
let list_response = kmd.list_wallets()?;
let wallet_id = match list_response
.wallets
.into_iter()
.find(|wallet| wallet.name == "unencrypted-default-wallet")
{
Some(wallet) => wallet.id,
None => return Err("Wallet not found".into()),
};
println!("Wallet: {}", wallet_id);
let init_response = kmd.init_wallet_handle(&wallet_id, "")?;
let wallet_handle_token = init_response.wallet_handle_token;
Now it’s time to locate our default wallet and obtain a handle to it. In this case, our wallet has no password, so we just use an empty string.
7. Build Payment Transaction
let algod = Algod::new()
.bind("http://localhost:4001")
.auth("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
.client_v1()?;
let transaction_params = algod.transaction_params()?;
let genesis_id = transaction_params.genesis_id;
let genesis_hash = transaction_params.genesis_hash;
It’s time to start building our transaction. We will need an algod
client and some parameters identifying our private network. We can retrieve them with algod.transaction_params()
.
let public_key = "INSERT-HERE-YOUR-ADDRESS";
let to_address = Address::from_string(public_key.as_ref())?;
let from_address = Address::from_string(public_key.as_ref())?;
println!("Receiver: {:#?}", to_address);
Time to describe the sender and the receiver! We will use the same address for simplicity. Here you must insert one of the addresses with a positive balance in your wallet (you can use this command: ./sandbox goal account list
).
let base = BaseTransaction {
sender: from_address,
first_valid: transaction_params.last_round,
last_valid: transaction_params.last_round + 1000,
note: Vec::new(),
genesis_id,
genesis_hash,
};
println!("Base: {:#?}", base);
That’s enough information for our base transaction struct. Let’s build it.
let payment = Payment {
amount: MicroAlgos(100_000),
receiver: to_address,
close_remainder_to: None,
};
println!("Payment: {:#?}", payment);
But we said that we would send some ALGO! So, let’s prepare the Payment
part of our transaction. Note that there are many different kinds of transactions that we can send on Algorand, thus the subdivision in Base
plus Payment
(you can read more here).
let transaction =
Transaction::new_flat_fee(base, MicroAlgos(1_000), TransactionType::Payment(payment));
println!("Transaction: {:#?}", transaction);
Now that we have the two components of our transaction we can build it! Also, notice that here we can specify the fees that we are willing to pay. In this example, we will use 1000 MicroAlgos.
8. Sign the Transaction
let sign_response = kmd.sign_transaction(&wallet_handle_token, "", &transaction)?;
println!("Signed: {:#?}", sign_response);
Up to this point, anyone could have built such a transaction. This means that it is time to sign it! To do so, we just invoke kmd
‘s sign_transaction
function, passing our wallet’s handle, its password, and the transaction itself.
9. Broadcast Transaction
let send_response = algod.raw_transaction(&sign_response.signed_transaction)?;
println!("Transaction ID: {}", send_response.tx_id);
Ok(())
}
All that is left is to broadcast the transaction to the network using algod
.
10. Run the Code
Now it is time to run the code. To do so open a terminal in the root folder of your project and run
cargo run
After some logs you should be able to read something like this (with a different hash)
Transaction ID: ZBIVUOJS2PBMKQHHYAWKWYWWH5WFNI7CSBD3AV22VJLR6AMUBROA
Congratulations, you sent your first Algorand transaction using Rust!
Troubleshooting
- Check if your sandbox is running.
- Check that the address of an account with enough funds is correctly reported in your code.
11. Conclusion
If you think that it feels a bit too convoluted to send a transaction… I completely agree! This is because algonaut
is just at the beginning. I am adopting an example-driven development approach to improve its API. Please contribute with issues, pull requests, examples, and let’s discuss how we can make algonaut
better on Algorand’s discord channel!
The RustConf artwork is distributed under a Creative Commons Attribute-NonCommercial-NoDerivatives license. This license permits you to reproduce the work as-is for noncommercial purposes, with attribution, but does not permit creating or distributing derived works.
The RustConf artwork is owned by Tilde.