UNC Workspaces (Rust Edition)
Rust library for automating workflows and writing tests for UNC smart contracts. This software is not final, and will likely change.
## Release notes
**Release notes and unreleased changes can be found in the [CHANGELOG](CHANGELOG.md)**
## Requirements
- Rust v1.77.1 and up.
- MacOS (x86 and M1/2) or Linux (x86) for sandbox tests.
### WASM compilation not supported
`unc-workspaces-rs`, the library itself, does not currently compile to WASM. Best to put this dependency in `[dev-dependencies]` section of `Cargo.toml` if we were trying to run this library alongside something that already does compile to WASM, such as `unc-sdk-rs`.
## Simple Testing Case
A simple test to get us going and familiar with `unc-workspaces` framework. Here, we will be going through the NFT contract and how we can test it with `unc-workspaces-rs`.
### Setup -- Imports
First, we need to declare some imports for convenience.
```rust
// macro allowing us to convert args into JSON bytes to be read by the contract.
use serde_json::json;
```
We will need to have our pre-compiled WASM contract ahead of time and know its path. Refer to the respective unc-sdk-{rs, js} repos/language for where these paths are located.
In this showcase, we will be pointing to the example's NFT contract:
```rust
const NFT_WASM_FILEPATH: &str = "./examples/res/non_fungible_token.wasm";
```
NOTE: there is an unstable feature that will allow us to compile our projects during testing time as well. Take a look at the feature section [Compiling Contracts During Test Time](#compiling-contracts-during-test-time)
### Setup -- Setting up Sandbox and Deploying NFT Contract
This includes launching our sandbox, loading our wasm file and deploying that wasm file to the sandbox environment.
```rust
#[tokio::test]
async fn test_nft_contract() -> anyhow::Result<()> {
let worker = unc_workspaces::sandbox().await?;
let wasm = std::fs::read(NFT_WASM_FILEPATH)?;
let contract = worker.dev_deploy(&wasm).await?;
```
Where
- `anyhow` - A crate that deals with error handling, making it more robust for developers.
- `worker` - Our gateway towards interacting with our sandbox environment.
- `contract`- The deployed contract on sandbox the developer interacts with.
### Initialize Contract & Test Output
Then we'll go directly into making a call into the contract, and initialize the NFT contract's metadata:
```rust
let outcome = contract
.call("new_default_meta")
.args_json(json!({
"owner_id": contract.id(),
}))
.transact() // note: we use the contract's keys here to sign the transaction
.await?;
// outcome contains data like logs, receipts and transaction outcomes.
println!("new_default_meta outcome: {:#?}", outcome);
```
Afterwards, let's mint an NFT via `nft_mint`. This showcases some extra arguments we can supply, such as deposit and gas:
```rust
use unc_gas::UncGas;
use unc_workspaces::types::UncToken;
let deposit = UncToken::from_unc(100);
let outcome = contract
.call("nft_mint")
.args_json(json!({
"token_id": "0",
"token_owner_id": contract.id(),
"token_metadata": {
"title": "Olympus Mons",
"description": "Tallest mountain in charted solar system",
"copies": 1,
},
}))
.deposit(deposit)
// nft_mint might consume more than default gas, so supply our own gas value:
.gas(UncGas::from_tgas(300))
.transact()
.await?;
println!("nft_mint outcome: {:#?}", outcome);
```
Then later on, we can view our minted NFT's metadata via our `view` call into `nft_metadata`:
```rust
let result: serde_json::Value = contract
.call("nft_metadata")
.view()
.await?
.json()?;
println!("--------------\n{}", result);
println!("Dev Account ID: {}", contract.id());
Ok(())
}
```
### Updating Contract Afterwards
Note that if our contract code changes, `unc-workspaces-rs` does nothing about it since we are utilizing `deploy`/`dev_deploy` to merely send the contract bytes to the network. So if it does change, we will have to recompile the contract as usual, and point `deploy`/`dev_deploy` again to the right WASM files. However, there is a feature that will recompile contract changes for us: refer to the experimental/unstable [`compile_project`](#compiling-contracts-during-test-time) function for telling unc-workspaces to compile a _Rust_ project for us.
## Examples
More standalone examples can be found in `examples/src/*.rs`.
To run the above NFT example, execute:
```sh
cargo run --example nft
```
## Features
### Choosing a network
```rust
#[tokio::main] // or whatever runtime we want
async fn main() -> anyhow::Result<()> {
// Create a sandboxed environment.
// NOTE: Each call will create a new sandboxed environment
let worker = unc_workspaces::sandbox().await?;
// or for testnet:
let worker = unc_workspaces::testnet().await?;
}
```
### Helper Functions
Need to make a helper functions utilizing contracts? Just import it and pass it around:
```rust
use unc_workspaces::Contract;
// Helper function that calls into a contract we give it
async fn call_my_func(contract: &Contract) -> anyhow::Result<()> {
// Call into the function `contract_function` with args:
contract.call("contract_function")
.args_json(serde_json::json!({
"message": msg,
})
.transact()
.await?;
Ok(())
}
```
Or to pass around workers regardless of networks:
```rust
use unc_workspaces::{DevNetwork, Worker};
const CONTRACT_BYTES: &[u8] = include_bytes!("./relative/path/to/file.wasm");
// Create a helper function that deploys a specific contract
// NOTE: `dev_deploy` is only available on `DevNetwork`s such as sandbox and testnet.
async fn deploy_my_contract(worker: Worker