Skip to content

[CLI][Move] support public structs/enums as txn args in CLI#18591

Open
rahxephon89 wants to merge 2 commits intoteng/public-struct-txn-argsfrom
teng/txn-args-CLI
Open

[CLI][Move] support public structs/enums as txn args in CLI#18591
rahxephon89 wants to merge 2 commits intoteng/public-struct-txn-argsfrom
teng/txn-args-CLI

Conversation

@rahxephon89
Copy link
Contributor

@rahxephon89 rahxephon89 commented Feb 4, 2026

Description

This PR:

  1. adds support of public structs/enums as txn args for Aptos CLI;
  2. adds support of running local net for cli e2e tests without using a docker.

How Has This Been Tested?

Newly added cli e2e tests.

Key Areas to Review

Type of Change

  • New feature
  • Bug fix
  • Breaking change
  • Performance improvement
  • Refactoring
  • Dependency update
  • Documentation update
  • Tests

Which Components or Systems Does This Change Impact?

  • Validator Node
  • Full Node (API, Indexer, etc.)
  • Move/Aptos Virtual Machine
  • Aptos Framework
  • Aptos CLI/SDK
  • Developer Infrastructure
  • Move Compiler
  • Other (specify)

Checklist

  • I have read and followed the CONTRIBUTING doc
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I identified and added all stakeholders and component owners affected by this change as reviewers
  • I tested both happy and unhappy path of the functionality
  • I have made corresponding changes to the documentation

Note

Medium Risk
Touches CLI transaction argument parsing and encoding, adding async REST-dependent logic that could break move run/view/simulate for JSON inputs if type detection or BCS encoding is incorrect. Local-testnet orchestration changes also affect e2e execution flow and process lifecycle management.

Overview
Adds public struct/enum transaction argument support to the Aptos CLI when using --json-file, including enum variant/object syntax and Option<T> in both legacy vector ([] / [v]) and new variant ({"None":{}} / {"Some":{"0":v}}) formats. This introduces an async parsing path that fetches on-chain module bytecode/ABI to validate types and BCS-encode nested structs/enums, implemented via a new StructArgParser with module caching and depth limits.

Updates aptos move run, move simulate, and move view to use the async parser only when JSON input is present, and hard-errors if struct/enum types are encountered in the previous synchronous conversion path. Adds a new Move test package and Python e2e cases to exercise struct/enum/Option argument handling, and extends the e2e harness with --use-local-testnet (auto-start or manual) to run tests without Docker. Also introduces shared MODULE_SEPARATOR/std-type constants in move-core-types and adds async-recursion to the CLI crate.

Written by Cursor Bugbot for commit 0904383. This will update automatically on new commits. Configure here.

Copy link
Contributor Author

rahxephon89 commented Feb 4, 2026

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@rahxephon89 rahxephon89 changed the title cli support [experiment] support public structs/enums as txn args in CLI Feb 4, 2026
@rahxephon89 rahxephon89 force-pushed the teng/public-struct-txn-args branch 2 times, most recently from fc1c26d to d31e4d0 Compare February 5, 2026 01:06
@rahxephon89 rahxephon89 force-pushed the teng/txn-args-CLI branch 2 times, most recently from 155b20c to 398a435 Compare February 5, 2026 04:25
@rahxephon89 rahxephon89 force-pushed the teng/public-struct-txn-args branch 2 times, most recently from f37ad1b to b30d55a Compare February 5, 2026 05:31
@rahxephon89 rahxephon89 force-pushed the teng/txn-args-CLI branch 2 times, most recently from 170c1ef to e22d876 Compare February 5, 2026 07:00
@rahxephon89 rahxephon89 force-pushed the teng/public-struct-txn-args branch 2 times, most recently from 8f27993 to dd7a3d2 Compare February 5, 2026 18:45
@rahxephon89 rahxephon89 force-pushed the teng/txn-args-CLI branch 2 times, most recently from bfc10dc to ff0ca1f Compare February 5, 2026 19:01
@rahxephon89 rahxephon89 changed the title [experiment] support public structs/enums as txn args in CLI [CLI][Move] support public structs/enums as txn args in CLI Feb 5, 2026
@rahxephon89 rahxephon89 marked this pull request as ready for review February 5, 2026 19:07
@rahxephon89 rahxephon89 force-pushed the teng/public-struct-txn-args branch from dd7a3d2 to 966306b Compare February 5, 2026 22:35
@rahxephon89 rahxephon89 force-pushed the teng/public-struct-txn-args branch from 966306b to a6c536f Compare February 6, 2026 03:09
fn check_depth(depth: u8, type_name: &str) -> CliTypedResult<()> {
if depth > MAX_NESTING_DEPTH {
return Err(CliError::CommandArgumentError(format!(
"{} nesting depth {} exceeds maximum allowed depth of {}",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"{} nesting depth {} exceeds maximum allowed depth of {}",
"`{}` nesting depth {} exceeds maximum allowed depth of {}",

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}

/// Construct a struct argument by parsing fields and encoding to BCS.
pub async fn construct_struct_argument(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am curious about the necessity of getting struct def from rest API for verifying the args. It seems to bring in extra complexities.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we cannot get the abi, we have to deserialize the module to get enough information

) -> CliTypedResult<Vec<u8>> {
// Check nesting depth limit
Self::check_depth(depth, "Struct")?;
let module = self.verify_struct_exists(struct_tag).await?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is repeated work by verify_struct_exists and the code below (e.g., potential deserialization of the bytecode module). Maybe creating a separate data struct for onchain modules, where we can cache and reuse the analysis results along the way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}

/// Convert MoveType to TypeTag.
fn convert_move_type_to_type_tag(move_type: &MoveType) -> CliTypedResult<TypeTag> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's worth checking if we already have APIs to do this and other convertions defined in this file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code refactored

}

/// Parse a value based on its Move type and encode to BCS.
fn parse_value_by_type<'a>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we simply make it a async fn so that we can simplify the return type?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, some abstraction would help this function to be more readable and maintainable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

)));
}

match move_type {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have existing APIs doing this thing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code refactored

})?;
bcs::to_bytes(&v).map_err(|e| CliError::BCS("bool", e))
},
MoveType::U8 => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parsing here and below has an inconsistent error handling with MoveType::Bool.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you mean the error type is different?

// 1. Array format (legacy): [] or [value]
// 2. Enum format (new): {"None": {}} or {"Some": {"0": value}}

if value.is_array() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remember to have seen similar code in several other places? Maybe making a function for it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code refactored

}
}

match qualified_name.as_str() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have a more systematic and generic way to handle these special cases? I suppose they will grow as time goes by.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this corresponds to allowed structs so I will keep it as it is and add TODO once we have a more general way to handle them

let s = if value.is_string() {
value.as_str().unwrap()
} else if value.is_number() {
&value.to_string()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use value.as_str().unwrap() above and &value.to_string() here. Is it intended?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code refactored

@junxzm1990
Copy link
Contributor

@rahxephon89 I did a first look at the PR. The code makes sense but can benefit from some clean-up and refactoring. I will take another look afterward.

@rahxephon89 rahxephon89 force-pushed the teng/public-struct-txn-args branch from a6c536f to 810bb48 Compare February 7, 2026 03:25
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 4 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants