Join the AlphaWave competition
Developer toolsSDKsRust SDK

Usage

Understand the basics for connecting to Recall and using buckets.


The most basic usage will likely involve connecting to the network, creating an bucket, and then adding or getting objects. This page walks through each step and provides examples to help you get started.

Signer & provider setup

To create a bucket, you'll need to use the JsonRpcProvider from the recall_provider crate plus various recall_sdk types.

Here's a simple example. The signer.init_sequence() call is optional, but without it, you'll run into issues with out of sync sequences (nonces). It's best to call init_sequence unless you're firing many transactions at once.

use std::collections::HashMap;
 
use recall_provider::json_rpc::JsonRpcProvider;
use recall_sdk::{
    machine::{bucket::Bucket, Machine},
    network::Network,
};
 
#[tokio::main]
async fn main() {
    let network = Network::Testnet.init();
    let provider =
        JsonRpcProvider::new_http(network.rpc_url()?, None, Some(network.object_api_url()?))?;
    let file = async_tempfile::TempFile::new().await?;
 
    signer.init_sequence(&provider).await?;
}

Creating a bucket

To create a bucket, you'll need to use the Bucket::new method, which takes a few arguments:

  • The RPC provider.
  • The signer.
  • An optional owner address, which defaults to the signer's address.
  • A HashMap of metadata to assign to the bucket (use Default::default() to create an empty map).
  • Lastly, optional gas parameters (these can be imported from recall_provider::message::GasParams).
#[tokio::main]
async fn main() {
    // Existing code...
 
    let (bucket, tx) = Bucket::new(
        &provider,
        &mut signer,
        None,
        HashMap::new(),
        Default::default(),
    ).await?;
}

Note the second value returned is the onchain transaction that created the bucket.

Adding an object

We can extend the example above to add an object to the bucket. The add method is used to add an object to the bucket given a provider, signer, the object's desired key (e.g., my/key), and options that control its behavior. For example, you can add other metadata to an object or specify how to handle overwriting an existing object.

use std::collections::HashMap;
 
use recall_sdk::{
    machine::{
        bucket::{AddOptions, Bucket}, 
        Machine,
    },
    network::Network,
};
use tokio::io::{AsyncSeekExt, AsyncWriteExt}; 
 
#[tokio::main]
async fn main() {
    // Existing code...
 
    let file = async_tempfile::TempFile::new().await?;
    file.write_all("hello world".as_bytes()).await?;
    file.flush().await?;
    file.rewind().await?;
 
    let key = "my/key";
    let mut metadata = HashMap::new();
    metadata.insert("foo".to_string(), "bar".to_string());
    let options = AddOptions {
        overwrite: true,
        metadata,
        ..Default::default()
    };
    let tx = bucket
        .add_from_path(&provider, &mut signer, key, file.file_path(), options)
        .await?;
}

Querying objects

To list an object, you can use the query method. This method comes with available query options to refine your results:

  • prefix: The prefix of the keys you want to list.
  • delimiter: The delimiter to use when listing keys.
  • start_key: The key to start listing keys from.
  • limit: The maximum number of keys to list.
  • height: The block height to list objects for (defaults to the latest committed height).
use recall_sdk::{
    machine::{
        objectstore::{AddOptions, ObjectStore, QueryOptions}, 
        Machine,
    },
    network::Network,
};
 
#[tokio::main]
async fn main() {
    // Existing code...
 
    let options = QueryOptions {
        prefix: "my/".to_string(),
        delimiter: "/".to_string(),
        start_key: None,
        limit: 1,
        height: Default::default(),
    };
    let list = bucket.query(&provider, options).await?;
}

This will give you a list of all objects and any other common prefixes down the tree.

{
  "objects": [
    {
      "key": "hello/world",
      "value": {
        "hash": "rzghyg4z3p6vbz5jkgc75lk64fci7kieul65o6hk6xznx7lctkmq",
        "size": 6,
        "metadata": {
          "new": "foo",
          "content-type": "application/octet-stream"
        }
      }
    }
  ],
  "common_prefixes": [],
  "next_key": null
}

Getting an object

You can use the get method to retrieve data and download the actual contents. A few options are available to refine your results:

  • bytes: Range request bytes as a dash-separated string (e.g., "0-99" gets the first 100 bytes).
  • height: The height of the bucket to list keys from (defaults to the latest committed height).
  • show_progress: Visually show the download progress (useful with CLI tools).
use recall_sdk::{
    machine::{
        objectstore::{AddOptions, GetOptions, ObjectStore, QueryOptions}, 
        Machine,
    },
    network::Network,
};
 
#[tokio::main]
async fn main() {
    // Existing code...
 
    let options = GetOptions {
        range: Some("0-99".to_string()), // Get the first 100 bytes
        ..Default::default()
    };
 
    let file = async_tempfile::TempFile::new().await?;
    {
        let open_file = file.open_rw().await?;
        bucket.get(&provider, &key, open_file, options).await?;
    }
    // Read the first 10 bytes of your downloaded 100 bytes
    let mut read_file = tokio::fs::File::open(&file.file_path()).await?;
    let mut contents = vec![0; 10];
    read_file.write_all(&mut contents).await?;
}

Deleting an object

Lastly, since buckets are a fully mutable store, you can delete an object using the delete method. It takes similar parameters as adding an object. The default options are generally used, which let you specify the broadcast mode and gas parameters.

#[tokio::main]
async fn main() {
    // Existing code...
 
    let tx = bucket
        .delete(&provider, &mut signer, &key, Default::default())
        .await?;
}

The response will be similar to the add example above.

Network interactions

You can review the recall_sdk::network module for more information or check out the network configuration docs. Utility methods exist on the recall_sdk::network::Network struct to fetch network values dynamically, so there's not necessarily a need to hardcode them yourself anywhere.

On this page