Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ The format is based on [Keep a Changelog], and this project adheres to
### Added

- Add `sui-move`: core Move-shaped type layer (traits, abilities, decoding).
- Add Move framework primitives under `sui_move::primitives` (e.g. `coin`, `balance`, `vec_map`) with BCS-accurate tags/layouts.
- Add `MoveType` + ability-marker impls for Move builtins (`u8/u16/u32/u64/u128`, `bool`, `Address`, `U256`, `Vec<T>`).
- Add `sui-move-derive` and the `sui-move` `derive` feature for defining Move-shaped structs via macros.
- Add `sui-move-call`: typed Move call descriptions (`CallSpec`) plus typed wrappers for Sui `Input` kinds (pure, immutable/owned, shared, receiving).
- Add `sui-move-ptb`: minimal PTB builder that consumes `CallSpec` and produces `ProgrammableTransaction`.
Expand Down
10 changes: 7 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[workspace]
resolver = "2"
resolver = "3"
members = [
"sui-move",
"sui-move-derive",
Expand All @@ -9,14 +9,18 @@ members = [
"sui-move-codegen",
]

[workspace.package]
edition = "2021"
version = "0.1.0"

[workspace.dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
thiserror = "1.0"
bcs = "0.1.6"
sui-sdk-types = { git = "https://github.com/mystenlabs/sui-rust-sdk", features = ["serde"] }
sui-sdk-types = { git = "https://github.com/mystenlabs/sui-rust-sdk", features = ["rand", "serde"] }
sui-rpc = { git = "https://github.com/mystenlabs/sui-rust-sdk" }
sui-crypto = { git = "https://github.com/mystenlabs/sui-rust-sdk" }
sui-crypto = { git = "https://github.com/mystenlabs/sui-rust-sdk", features = ["ed25519", "pem"] }
proc-macro2 = "1"
quote = "1"
syn = { version = "2", features = ["full"] }
Expand Down
4 changes: 2 additions & 2 deletions sui-move-call/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "sui-move-call"
version = "0.1.0"
edition = "2021"
version.workspace = true
edition.workspace = true
description = "Typed call arguments and call specifications for Move calls on Sui."
readme = "README.md"

Expand Down
24 changes: 22 additions & 2 deletions sui-move-call/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,19 @@ use sui_move::prelude::*;
use sui_move_call::{CallSpec, MoveObject};
use sui_sdk_types::{Address, Digest, ObjectReference, TypeTag};

#[sui_move::move_struct(address = "0x2", module = "object", abilities = "copy, drop, store")]
pub struct ID {
pub bytes: Address,
}

#[sui_move::move_struct(address = "0x2", module = "object", abilities = "store")]
pub struct UID {
pub id: ID,
}

#[sui_move::move_struct(address = "0x1", module = "vault", abilities = "key")]
pub struct Vault {
pub id: sui_move::types::UID,
pub id: UID,
}

pub fn withdraw(vault: &MoveObject<Vault>, amount: u64) -> CallSpec {
Expand Down Expand Up @@ -112,9 +122,19 @@ use std::str::FromStr;
use sui_move_call::{CallArg, CallSpec, ReceivingMoveObject, SharedMoveObject};
use sui_sdk_types::{Address, Digest, ObjectReference};

#[sui_move::move_struct(address = "0x2", module = "object", abilities = "copy, drop, store")]
struct ID {
bytes: Address,
}

#[sui_move::move_struct(address = "0x2", module = "object", abilities = "store")]
struct UID {
id: ID,
}

#[sui_move::move_struct(address = "0x1", module = "demo", abilities = "key")]
struct Thing {
id: sui_move::types::UID,
id: UID,
}

let package = Address::from_str("0x1").unwrap();
Expand Down
48 changes: 44 additions & 4 deletions sui-move-call/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,19 @@ pub use sui_sdk_types::Input as CallArg;
/// use sui_move_call::MoveObject;
/// use sui_sdk_types::{Address, Digest, ObjectReference};
///
/// #[sui_move::move_struct(address = "0x2", module = "object", abilities = "copy, drop, store")]
/// struct ID {
/// bytes: Address,
/// }
///
/// #[sui_move::move_struct(address = "0x2", module = "object", abilities = "store")]
/// struct UID {
/// id: ID,
/// }
///
/// #[sui_move::move_struct(address = "0x1", module = "demo", abilities = "key")]
/// struct Demo {
/// id: sui_move::types::UID,
/// id: UID,
/// }
///
/// let id = Address::from_str("0x1").unwrap();
Expand Down Expand Up @@ -82,9 +92,19 @@ impl<T: MoveStruct + HasKey> MoveObject<T> {
/// use sui_move_call::SharedMoveObject;
/// use sui_sdk_types::Address;
///
/// #[sui_move::move_struct(address = "0x2", module = "object", abilities = "copy, drop, store")]
/// struct ID {
/// bytes: Address,
/// }
///
/// #[sui_move::move_struct(address = "0x2", module = "object", abilities = "store")]
/// struct UID {
/// id: ID,
/// }
///
/// #[sui_move::move_struct(address = "0x1", module = "demo", abilities = "key")]
/// struct SharedThing {
/// id: sui_move::types::UID,
/// id: UID,
/// }
///
/// let object_id = Address::from_str("0x1").unwrap();
Expand Down Expand Up @@ -168,9 +188,19 @@ impl<T: MoveStruct + HasKey> SharedMoveObject<T> {
/// use sui_move_call::ReceivingMoveObject;
/// use sui_sdk_types::{Address, Digest, ObjectReference};
///
/// #[sui_move::move_struct(address = "0x2", module = "object", abilities = "copy, drop, store")]
/// struct ID {
/// bytes: Address,
/// }
///
/// #[sui_move::move_struct(address = "0x2", module = "object", abilities = "store")]
/// struct UID {
/// id: ID,
/// }
///
/// #[sui_move::move_struct(address = "0x1", module = "demo", abilities = "key")]
/// struct ReceivingThing {
/// id: sui_move::types::UID,
/// id: UID,
/// }
///
/// let id = Address::from_str("0x1").unwrap();
Expand Down Expand Up @@ -369,9 +399,19 @@ pub enum CallSpecError {
/// use sui_move_call::{CallSpec, MoveObject};
/// use sui_sdk_types::{Address, Digest, ObjectReference, TypeTag};
///
/// #[sui_move::move_struct(address = "0x2", module = "object", abilities = "copy, drop, store")]
/// struct ID {
/// bytes: Address,
/// }
///
/// #[sui_move::move_struct(address = "0x2", module = "object", abilities = "store")]
/// struct UID {
/// id: ID,
/// }
///
/// #[sui_move::move_struct(address = "0x1", module = "vault", abilities = "key")]
/// struct Vault {
/// id: sui_move::types::UID,
/// id: UID,
/// }
///
/// let package = Address::from_str("0x1").unwrap();
Expand Down
12 changes: 11 additions & 1 deletion sui-move-call/tests/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,19 @@ use sui_move_call::{
};
use sui_sdk_types::{Address, Digest, FundsWithdrawal, ObjectReference, TypeTag, WithdrawFrom};

#[sui_move::move_struct(address = "0x2", module = "object", abilities = "copy, drop, store")]
struct ID {
bytes: Address,
}

#[sui_move::move_struct(address = "0x2", module = "object", abilities = "store")]
struct UID {
id: ID,
}

#[sui_move::move_struct(address = "0x1", module = "demo", abilities = "key")]
struct Demo {
id: sui_move::types::UID,
id: UID,
}

#[test]
Expand Down
7 changes: 5 additions & 2 deletions sui-move-codegen/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "sui-move-codegen"
version = "0.1.0"
edition = "2021"
version.workspace = true
edition.workspace = true
description = "Generate typed Rust bindings (types + CallSpec builders) from Sui Move package metadata."
readme = "README.md"

Expand All @@ -15,3 +15,6 @@ syn = { workspace = true }
prettyplease = { workspace = true }
sui-rpc = { workspace = true }
sui-sdk-types = { workspace = true }

[dev-dependencies]
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
112 changes: 112 additions & 0 deletions sui-move-codegen/examples/localnet_workspace.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
//! Generate a bindings workspace for a Move package (plus its dependency closure) on localnet.
//!
//! By default this connects to `http://127.0.0.1:9000` (override with `--grpc` or `SUI_GRPC`)
//! and writes output under `../target/bindings/<package_id_without_0x>`.
//!
//! Example:
//! - `cargo run -p sui-move-codegen --example localnet_workspace -- --check`
//! - `cargo run -p sui-move-codegen --example localnet_workspace -- 0x4cc... --check`
//! - `cargo run -p sui-move-codegen --example localnet_workspace -- 0x4cc... --external 0x0aaa...=../my-primitives-crate --check`

use std::collections::BTreeMap;
use std::path::PathBuf;
use std::process::Command;

use sui_move_codegen::render::RenderOptions;
use sui_move_codegen::workspace::{generate_bindings_workspace, WorkspaceOptions};
use sui_move_codegen::{Address, Client};

const DEFAULT_PACKAGE: &str = "0x4cc38b7c23bf14d7555503ab38a9748f9544c2c29c6519df412b4f6fb6971640";
const DEFAULT_GRPC: &str = "http://127.0.0.1:9000";

fn usage() -> ! {
eprintln!(
"Usage: localnet_workspace [package_id] [--grpc <url>] [--out <dir>] [--external <pkg>=<crate_dir>]... [--check]\n\n\
Defaults:\n\
- package_id: {DEFAULT_PACKAGE}\n\
- grpc: {DEFAULT_GRPC} (or $SUI_GRPC)\n\
- out: <repo>/target/bindings/<package_id_without_0x>\n"
);
std::process::exit(2);
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut args = std::env::args().skip(1).peekable();

let mut package_id = DEFAULT_PACKAGE.to_string();
let mut grpc = std::env::var("SUI_GRPC").unwrap_or_else(|_| DEFAULT_GRPC.to_string());
let mut out_dir: Option<PathBuf> = None;
let mut externals: BTreeMap<String, PathBuf> = BTreeMap::new();
let mut check = false;

// Optional first positional arg: package id.
if let Some(first) = args.peek() {
if !first.starts_with('-') {
package_id = args.next().unwrap();
}
}

while let Some(arg) = args.next() {
match arg.as_str() {
"--grpc" => grpc = args.next().unwrap_or_else(|| usage()),
"--out" => out_dir = Some(PathBuf::from(args.next().unwrap_or_else(|| usage()))),
"--external" => {
let spec = args.next().unwrap_or_else(|| usage());
let (pkg, path) = spec
.split_once('=')
.ok_or("expected --external <pkg_id>=<crate_dir>")?;
externals.insert(pkg.to_string(), PathBuf::from(path));
}
"--check" => check = true,
"--help" | "-h" => usage(),
other => return Err(format!("unknown argument `{other}`").into()),
}
}

let root_pkg: Address = package_id.parse()?;
let repo_root = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("..")
.canonicalize()?;
let default_out = repo_root
.join("target")
.join("bindings")
.join(package_id.trim_start_matches("0x"));
let out_dir = out_dir.unwrap_or(default_out);

println!("grpc: {grpc}");
println!("package: {package_id}");
println!("out: {}", out_dir.display());

let mut client = Client::new(grpc)?;
let render_opts = RenderOptions::default();
let ws_opts = WorkspaceOptions {
move_binding_root: Some(repo_root),
force_non_flattened: true,
};

generate_bindings_workspace(
&mut client,
root_pkg,
&out_dir,
&render_opts,
externals,
ws_opts,
)
.await?;

println!("generated: {}", out_dir.display());
if check {
let status = Command::new("cargo")
.arg("check")
.current_dir(&out_dir)
.status()?;
if !status.success() {
return Err(format!("cargo check failed with status {status}").into());
}
} else {
println!("next: (optional) cd {} && cargo check", out_dir.display());
}

Ok(())
}
5 changes: 5 additions & 0 deletions sui-move-codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ pub mod ir;
/// Render normalized metadata into Rust source.
pub mod render;

/// High-level helpers for generating bindings across packages.
pub mod workspace;

pub use crate::source::fetch_package;
pub use crate::source::Address;
pub use crate::source::Client;

/// Errors from sourcing or normalizing package metadata.
#[derive(thiserror::Error, Debug)]
Expand Down
Loading
Loading