Skip to content

Instantly share code, notes, and snippets.

@areshand
Created July 22, 2025 23:31
Show Gist options
  • Select an option

  • Save areshand/507d4248237489d2acef5d0c26cd202b to your computer and use it in GitHub Desktop.

Select an option

Save areshand/507d4248237489d2acef5d0c26cd202b to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
import subprocess
import json
import sys
# Feature flag definitions from types/src/on_chain_config/aptos_features.rs
FEATURE_FLAGS = {
1: "CODE_DEPENDENCY_CHECK",
2: "TREAT_FRIEND_AS_PRIVATE",
3: "SHA_512_AND_RIPEMD_160_NATIVES",
4: "APTOS_STD_CHAIN_ID_NATIVES",
5: "VM_BINARY_FORMAT_V6",
6: "_DEPRECATED_COLLECT_AND_DISTRIBUTE_GAS_FEES",
7: "MULTI_ED25519_PK_VALIDATE_V2_NATIVES",
8: "BLAKE2B_256_NATIVE",
9: "RESOURCE_GROUPS",
10: "MULTISIG_ACCOUNTS",
11: "DELEGATION_POOLS",
12: "CRYPTOGRAPHY_ALGEBRA_NATIVES",
13: "BLS12_381_STRUCTURES",
14: "ED25519_PUBKEY_VALIDATE_RETURN_FALSE_WRONG_LENGTH",
15: "STRUCT_CONSTRUCTORS",
16: "PERIODICAL_REWARD_RATE_DECREASE",
17: "PARTIAL_GOVERNANCE_VOTING",
18: "SIGNATURE_CHECKER_V2",
19: "STORAGE_SLOT_METADATA",
20: "CHARGE_INVARIANT_VIOLATION",
21: "DELEGATION_POOL_PARTIAL_GOVERNANCE_VOTING",
22: "GAS_PAYER_ENABLED",
23: "APTOS_UNIQUE_IDENTIFIERS",
24: "BULLETPROOFS_NATIVES",
25: "SIGNER_NATIVE_FORMAT_FIX",
26: "MODULE_EVENT",
27: "EMIT_FEE_STATEMENT",
28: "STORAGE_DELETION_REFUND",
29: "SIGNATURE_CHECKER_V2_SCRIPT_FIX",
30: "AGGREGATOR_V2_API",
31: "SAFER_RESOURCE_GROUPS",
32: "SAFER_METADATA",
33: "SINGLE_SENDER_AUTHENTICATOR",
34: "SPONSORED_AUTOMATIC_ACCOUNT_V1_CREATION",
35: "FEE_PAYER_ACCOUNT_OPTIONAL",
36: "AGGREGATOR_V2_DELAYED_FIELDS",
37: "CONCURRENT_TOKEN_V2",
38: "LIMIT_MAX_IDENTIFIER_LENGTH",
39: "OPERATOR_BENEFICIARY_CHANGE",
40: "VM_BINARY_FORMAT_V7",
41: "RESOURCE_GROUPS_SPLIT_IN_VM_CHANGE_SET",
42: "COMMISSION_CHANGE_DELEGATION_POOL",
43: "BN254_STRUCTURES",
44: "WEBAUTHN_SIGNATURE",
45: "_DEPRECATED_RECONFIGURE_WITH_DKG",
46: "KEYLESS_ACCOUNTS",
47: "KEYLESS_BUT_ZKLESS_ACCOUNTS",
48: "_DEPRECATED_REMOVE_DETAILED_ERROR_FROM_HASH",
49: "JWK_CONSENSUS",
50: "CONCURRENT_FUNGIBLE_ASSETS",
51: "REFUNDABLE_BYTES",
52: "OBJECT_CODE_DEPLOYMENT",
53: "MAX_OBJECT_NESTING_CHECK",
54: "KEYLESS_ACCOUNTS_WITH_PASSKEYS",
55: "MULTISIG_V2_ENHANCEMENT",
56: "DELEGATION_POOL_ALLOWLISTING",
57: "MODULE_EVENT_MIGRATION",
58: "_REJECT_UNSTABLE_BYTECODE",
59: "TRANSACTION_CONTEXT_EXTENSION",
60: "COIN_TO_FUNGIBLE_ASSET_MIGRATION",
61: "PRIMARY_APT_FUNGIBLE_STORE_AT_USER_ADDRESS",
62: "OBJECT_NATIVE_DERIVED_ADDRESS",
63: "DISPATCHABLE_FUNGIBLE_ASSET",
64: "NEW_ACCOUNTS_DEFAULT_TO_FA_APT_STORE",
65: "OPERATIONS_DEFAULT_TO_FA_APT_STORE",
66: "AGGREGATOR_V2_IS_AT_LEAST_API",
67: "CONCURRENT_FUNGIBLE_BALANCE",
68: "DEFAULT_TO_CONCURRENT_FUNGIBLE_BALANCE",
69: "_LIMIT_VM_TYPE_SIZE",
70: "ABORT_IF_MULTISIG_PAYLOAD_MISMATCH",
71: "_DISALLOW_USER_NATIVES",
72: "ALLOW_SERIALIZED_SCRIPT_ARGS",
73: "_USE_COMPATIBILITY_CHECKER_V2",
74: "ENABLE_ENUM_TYPES",
75: "ENABLE_RESOURCE_ACCESS_CONTROL",
76: "_REJECT_UNSTABLE_BYTECODE_FOR_SCRIPT",
77: "FEDERATED_KEYLESS",
78: "TRANSACTION_SIMULATION_ENHANCEMENT",
79: "COLLECTION_OWNER",
80: "NATIVE_MEMORY_OPERATIONS",
81: "_ENABLE_LOADER_V2",
82: "_DISALLOW_INIT_MODULE_TO_PUBLISH_MODULES",
83: "ENABLE_CALL_TREE_AND_INSTRUCTION_VM_CACHE",
84: "PERMISSIONED_SIGNER",
85: "ACCOUNT_ABSTRACTION",
86: "VM_BINARY_FORMAT_V8",
87: "BULLETPROOFS_BATCH_NATIVES",
88: "DERIVABLE_ACCOUNT_ABSTRACTION",
89: "ENABLE_FUNCTION_VALUES",
90: "NEW_ACCOUNTS_DEFAULT_TO_FA_STORE",
91: "DEFAULT_ACCOUNT_RESOURCE",
92: "JWK_CONSENSUS_PER_KEY_MODE",
93: "TRANSACTION_PAYLOAD_V2",
94: "ORDERLESS_TRANSACTIONS",
95: "ENABLE_LAZY_LOADING",
96: "CALCULATE_TRANSACTION_FEE_FOR_DISTRIBUTION",
97: "DISTRIBUTE_TRANSACTION_FEE",
}
def fetch_features(node_url="http://localhost:8080"):
"""Fetch features from Aptos node using curl"""
try:
# Ensure URL doesn't end with /v1 if it's already included
if node_url.endswith('/v1'):
api_url = f'{node_url}/accounts/0x1/resource/0x1::features::Features'
else:
api_url = f'{node_url}/v1/accounts/0x1/resource/0x1::features::Features'
result = subprocess.run([
'curl', '-s', api_url
], capture_output=True, text=True)
if result.returncode != 0:
raise Exception(f"Curl failed with return code {result.returncode}: {result.stderr}")
if not result.stdout.strip():
raise Exception("Empty response from server")
# Debug: print raw response if it's not valid JSON
try:
data = json.loads(result.stdout)
except json.JSONDecodeError as e:
print(f"Raw response: {result.stdout[:200]}...")
raise Exception(f"Invalid JSON response: {e}")
if 'data' not in data or 'features' not in data['data']:
raise Exception(f"Unexpected response format: {data}")
return data['data']['features']
except Exception as e:
print(f"Error fetching features from {node_url}: {e}")
return None
def is_feature_enabled(features_hex, flag_number):
"""
Check if a feature flag is enabled using the exact same logic as Rust code.
This matches the is_enabled function in types/src/on_chain_config/aptos_features.rs
"""
if features_hex.startswith('0x'):
features_hex = features_hex[2:]
features_bytes = bytes.fromhex(features_hex)
# Rust logic: byte_index = (val / 8), bit_mask = 1 << (val % 8)
byte_index = flag_number // 8
bit_mask = 1 << (flag_number % 8)
if byte_index < len(features_bytes):
return bool(features_bytes[byte_index] & bit_mask)
return False
def get_max_vm_version(features_hex):
"""
Determine maximum VM version using same logic as Rust get_max_binary_format_version
"""
if is_feature_enabled(features_hex, 86): # VM_BINARY_FORMAT_V8
return 8
elif is_feature_enabled(features_hex, 40): # VM_BINARY_FORMAT_V7
return 7
elif is_feature_enabled(features_hex, 5): # VM_BINARY_FORMAT_V6
return 6
else:
return 5
def list_enabled_features(features_hex):
"""List all enabled features"""
enabled = []
for flag_num in range(1, 98): # Check all known flags
if is_feature_enabled(features_hex, flag_num):
feature_name = FEATURE_FLAGS.get(flag_num, f"UNKNOWN_FEATURE_{flag_num}")
enabled.append((flag_num, feature_name))
return enabled
def main():
if len(sys.argv) > 1:
if sys.argv[1] == "--help" or sys.argv[1] == "-h":
print("Usage:")
print(" python3 aptos_feature_checker.py [node_url] [feature_number]")
print(" python3 aptos_feature_checker.py # Check localhost VM version")
print(" python3 aptos_feature_checker.py localhost 5 # Check specific feature")
print(" python3 aptos_feature_checker.py localhost --list # List all enabled features")
return
node_url = sys.argv[1] if sys.argv[1] != "localhost" else "http://localhost:8080"
if len(sys.argv) > 2:
if sys.argv[2] == "--list":
# List all enabled features
features_hex = fetch_features(node_url)
if not features_hex:
return
enabled = list_enabled_features(features_hex)
print(f"Features hex: {features_hex}")
print(f"\nEnabled features ({len(enabled)} total):")
print("=" * 50)
for flag_num, feature_name in enabled:
print(f"Feature {flag_num:2d}: {feature_name}")
return
else:
# Check specific feature
try:
feature_number = int(sys.argv[2])
features_hex = fetch_features(node_url)
if not features_hex:
return
is_enabled = is_feature_enabled(features_hex, feature_number)
feature_name = FEATURE_FLAGS.get(feature_number, f"UNKNOWN_FEATURE_{feature_number}")
print(f"Features hex: {features_hex}")
print(f"Feature {feature_number} ({feature_name}): {'ENABLED' if is_enabled else 'DISABLED'}")
return
except ValueError:
print("Error: Feature number must be an integer")
return
else:
node_url = "http://localhost:8080"
# Default: Check VM version
features_hex = fetch_features(node_url)
if not features_hex:
return
print(f"Features hex: {features_hex}")
print()
# Check VM version features
vm_v6_enabled = is_feature_enabled(features_hex, 5)
vm_v7_enabled = is_feature_enabled(features_hex, 40)
vm_v8_enabled = is_feature_enabled(features_hex, 86)
print("VM Version Analysis:")
print("===================")
print(f"VM_BINARY_FORMAT_V6 (feature 5): {'ENABLED' if vm_v6_enabled else 'DISABLED'}")
print(f"VM_BINARY_FORMAT_V7 (feature 40): {'ENABLED' if vm_v7_enabled else 'DISABLED'}")
print(f"VM_BINARY_FORMAT_V8 (feature 86): {'ENABLED' if vm_v8_enabled else 'DISABLED'}")
max_version = get_max_vm_version(features_hex)
print(f"\n🎯 Maximum VM Version: {max_version}")
print(f"\nSupported Features:")
print(f"==================")
if max_version >= 8:
print("✅ VM v8 features: (future features)")
if max_version >= 7:
print("✅ VM v7 features: enums, access specifiers, variant operations")
if max_version >= 6:
print("✅ VM v6 features: u16, u32, u256 types")
print("✅ VM v1-v5 features: basic Move functionality")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment