Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: cleanup before release 0.2 #94

Merged
merged 14 commits into from
Mar 29, 2022
50 changes: 28 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
# NEAR Workspaces (Rust Edition)
A set of functions provided to automate workflows and write tests, such as the ability to deploy and run NEAR contracts, along with several other functions to aid in development and maintenance.
<div align="center">

This software is in very early alpha (use at your own risk). Only local sandboxed environments and testnet are available currently to run this library on.
<h1><code>NEAR Workspaces (Rust Edition)</code></h1>

<p>
<strong>Rust library for automating workflows and writing tests for NEAR smart contracts. This software is in early alpha (use at your own risk)</strong>
</p>

<p>
<a href="https://crates.io/crates/workspaces"><img src="https://img.shields.io/crates/v/workspaces.svg?style=flat-square" alt="Crates.io version" /></a>
<a href="https://crates.io/crates/workspaces"><img src="https://img.shields.io/crates/d/workspaces.svg?style=flat-square" alt="Download" /></a>
<a href="https://docs.rs/workspaces"><img src="https://docs.rs/workspaces/badge.svg" alt="Reference Documentation" /></a>
</p>
</div>

## Requirements
- rust v1.56 and up
- MacOS (x86) or Linux (x86) for sandbox tests. Testnet is available regardless
- MacOS (x86), M1 (thru rosetta) or Linux (x86) for sandbox tests. Testnet is available regardless
ChaoticTempest marked this conversation as resolved.
Show resolved Hide resolved

## Include it in our project
### M1 MacOS Setup
To be able to use this library on an M1 Mac, we would need to setup rosetta plus our cross compile target:
```
[dependencies]
workspaces = "0.1"
softwareupdate --install-rosetta
rustup default stable-x86_64-apple-darwin
```
Remember to grab the current revision since things are likely to change until published.

## Testing
A simple test to get us going and familiar with the features:
Expand All @@ -26,31 +36,27 @@ use workspaces::prelude::*;
async fn test_deploy_and_view() -> anyhow::Result<()> {
let worker = workspaces::sandbox();

let contract = worker.dev_deploy(include_bytes!("path/to/file.wasm").to_vec())
let contract = worker.dev_deploy(include_bytes!("path/to/file.wasm"))
.await
.expect("could not dev-deploy contract");

let result: String = contract.view(
&worker,
"function_name",
serde_json::json!({
let result: String = contract.call(&worker, "function_name")
.args_json(serde_json::json!({
"some_arg": "some_value",
})
.to_string()
.into_bytes(),
)
.await?
.json()?;
}))?
.view()
.await?
.json()?;

assert_eq!(result, "OUR_EXPECTED_RESULT");
Ok(())
}
```

## Examples
Some examples can be found `examples/src/*.rs` to run it standalone.
Some examples can be found in `examples/src/*.rs` to run it standalone.

To run the NFT example, run:
To run the NFT example, execute:
```
cargo run --example nft
```
Expand Down Expand Up @@ -93,6 +99,6 @@ async fn call_my_func(worker: Worker<impl Network>, contract: &Contract) -> anyh
// Create a helper function that deploys a specific contract
// NOTE: `dev_deploy` is only available on `DevNetwork`s such sandbox and testnet.
async fn deploy_my_contract(worker: Worker<impl DevNetwork>) -> anyhow::Result<Contract> {
worker.dev_deploy(std::fs::read(CONTRACT_FILE)?).await
worker.dev_deploy(&std::fs::read(CONTRACT_FILE)?).await
}
```
2 changes: 1 addition & 1 deletion examples/src/ref_finance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ async fn create_ref(
.transact()
.await?;

// NOTE: We are not pulling down the contract's data here, so we'll need ot initalize
// NOTE: We are not pulling down the contract's data here, so we'll need to initalize
// our own set of metadata. This is because the contract's data is too big for the rpc
// service to pull down (i.e. greater than 50mb).

Expand Down
3 changes: 2 additions & 1 deletion workspaces/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ pub use network::transaction::Function;
pub use network::{Account, AccountDetails, Block, Contract, DevNetwork, Network};
pub use types::{AccessKey, AccountId, BlockHeight, CryptoHash, InMemorySigner};
pub use worker::{
mainnet, mainnet_archival, sandbox, testnet, with_mainnet, with_sandbox, with_testnet, Worker,
mainnet, mainnet_archival, sandbox, testnet, testnet_archival, with_mainnet,
with_mainnet_archival, with_sandbox, with_testnet, with_testnet_archival, Worker,
};
5 changes: 5 additions & 0 deletions workspaces/src/network/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ impl Account {
Self { id, signer }
}

/// Grab the current account identifier
pub fn id(&self) -> &AccountId {
&self.id
}
Expand Down Expand Up @@ -146,10 +147,14 @@ impl Contract {
Self { account }
}

/// Grab the current contract identifier
ChaoticTempest marked this conversation as resolved.
Show resolved Hide resolved
pub fn id(&self) -> &AccountId {
&self.account.id
}

/// Casts the current [`Contract`] into an [`Account`] type. This does
/// nothing on chain/network, and is merely allowing `Account::*` functions
/// to be used from this `Contract`.
pub fn as_account(&self) -> &Account {
&self.account
}
Expand Down
13 changes: 13 additions & 0 deletions workspaces/src/network/testnet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use crate::{Contract, CryptoHash};

const RPC_URL: &str = "https://rpc.testnet.near.org";
const HELPER_URL: &str = "https://helper.testnet.near.org";
const ARCHIVAL_URL: &str = "https://archival-rpc.testnet.near.org";

pub struct Testnet {
client: Client,
Expand All @@ -36,6 +37,18 @@ impl Testnet {
},
}
}

pub(crate) fn archival() -> Self {
Self {
client: Client::new(ARCHIVAL_URL.into()),
info: Info {
name: "testnet-archival".into(),
root_id: AccountId::from_str("testnet").unwrap(),
keystore_path: PathBuf::from(".near-credentials/testnet/"),
rpc_url: ARCHIVAL_URL.into(),
},
}
}
}

impl AllowDevAccountCreation for Testnet {}
Expand Down
25 changes: 25 additions & 0 deletions workspaces/src/network/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ use crate::types::{AccessKey, AccountId, Balance, Gas, InMemorySigner, PublicKey
use crate::worker::Worker;
use crate::Account;

const MAX_GAS: Gas = 300_000_000_000_000;

/// A set of arguments we can provide to a transaction, containing
/// the function name, arguments, the amount of gas to use and deposit.
#[derive(Debug, Clone)]
Expand All @@ -26,6 +28,8 @@ pub struct Function<'a> {
}

impl<'a> Function<'a> {
/// Initialize a new instance of [`Function`], tied to a specific function on a
/// contract that lives directly on a contract we've specified in [`Transaction`].
pub fn new(name: &'a str) -> Self {
Self {
name,
Expand All @@ -35,30 +39,46 @@ impl<'a> Function<'a> {
}
}

/// Provide the arguments for the call. These args are serialized bytes from either
/// a JSON or Borsh serializable set of arguments. To use the more specific versions
/// with better quality of life, use `args_json` or `args_borsh`.
pub fn args(mut self, args: Vec<u8>) -> Self {
self.args = args;
self
}

/// Similiar to `args`, specify an argument that is JSON serializable and can be
/// accepted by the equivalent contract. Recommend to use something like
/// `serde_json::json!` macro to easily serialize the arguments.
pub fn args_json<U: serde::Serialize>(mut self, args: U) -> anyhow::Result<Self> {
self.args = serde_json::to_vec(&args)?;
Ok(self)
}

/// Similiar to `args`, specify an argument that is borsh serializable and can be
/// accepted by the equivalent contract.
pub fn args_borsh<U: borsh::BorshSerialize>(mut self, args: U) -> anyhow::Result<Self> {
self.args = args.try_to_vec()?;
Ok(self)
}

/// Specify the amount of tokens to be deposited where `deposit` is the amount of
/// tokens in yocto near.
pub fn deposit(mut self, deposit: Balance) -> Self {
self.deposit = deposit;
self
}

/// Specify the amount of gas to be used where `gas` is the amount of gas in yocto near.
ChaoticTempest marked this conversation as resolved.
Show resolved Hide resolved
pub fn gas(mut self, gas: Gas) -> Self {
self.gas = gas;
self
}

/// Use the maximum amount of gas possible to perform this function call into the contract.
pub fn max_gas(self) -> Self {
self.gas(MAX_GAS)
}
}

impl From<Function<'_>> for Action {
Expand Down Expand Up @@ -234,6 +254,11 @@ impl<'a, 'b, T: Network> CallTransaction<'a, 'b, T> {
self
}

/// Use the maximum amount of gas possible to perform this transaction.
pub fn max_gas(self) -> Self {
self.gas(MAX_GAS)
}

/// Finally, send the transaction to the network. This will consume the `CallTransaction`
/// object and return us the execution details, along with any errors if the transaction
/// failed in any process along the way.
Expand Down
34 changes: 34 additions & 0 deletions workspaces/src/worker/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,36 @@ where
}
}

/// Spin up a new sandbox instance, and grab a [`Worker`] that interacts with it.
pub fn sandbox() -> Worker<Sandbox> {
Worker::new(Sandbox::new())
}

/// Connect to the [testnet](https://explorer.testnet.near.org/) network, and grab
/// a [`Worker`] that can interact with it.
pub fn testnet() -> Worker<Testnet> {
Worker::new(Testnet::new())
}

/// Connect to the [testnet archival](https://near-nodes.io/intro/node-types#archival-node)
/// network, and grab a [`Worker`] that can interact with it.
pub fn testnet_archival() -> Worker<Testnet> {
Worker::new(Testnet::archival())
}

/// Connect to the [mainnet](https://explorer.near.org/) network, and grab
/// a [`Worker`] that can interact with it.
pub fn mainnet() -> Worker<Mainnet> {
Worker::new(Mainnet::new())
}

/// Connect to the [mainnet archival](https://near-nodes.io/intro/node-types#archival-node)
/// network, and grab a [`Worker`] that can interact with it.
pub fn mainnet_archival() -> Worker<Mainnet> {
Worker::new(Mainnet::archival())
}

/// Run a locally scoped task with a [`sandbox`] instanced [`Worker`] is supplied.
pub async fn with_sandbox<F, T>(task: F) -> T::Output
where
F: Fn(Worker<Sandbox>) -> T,
Expand All @@ -43,6 +57,7 @@ where
task(sandbox()).await
}

/// Run a locally scoped task with a [`testnet`] instanced [`Worker`] is supplied.
pub async fn with_testnet<F, T>(task: F) -> T::Output
where
F: Fn(Worker<Testnet>) -> T,
Expand All @@ -51,10 +66,29 @@ where
task(testnet()).await
}

/// Run a locally scoped task with a [`testnet_archival`] instanced [`Worker`] is supplied.
pub async fn with_testnet_archival<F, T>(task: F) -> T::Output
where
F: Fn(Worker<Testnet>) -> T,
T: core::future::Future,
{
task(testnet_archival()).await
}

/// Run a locally scoped task with a [`mainnet`] instanced [`Worker`] is supplied.
pub async fn with_mainnet<F, T>(task: F) -> T::Output
where
F: Fn(Worker<Mainnet>) -> T,
T: core::future::Future,
{
task(mainnet()).await
}

/// Run a locally scoped task with a [`mainnet_archival`] instanced [`Worker`] is supplied.
pub async fn with_mainnet_archival<F, T>(task: F) -> T::Output
where
F: Fn(Worker<Mainnet>) -> T,
T: core::future::Future,
{
task(mainnet_archival()).await
}