Move language expert for Movement blockchain. Automatically triggered when working with .move files, discussing Move/Movement/Aptos concepts, debugging Move compiler errors, or building smart contracts.
/plugin marketplace add Rahat-ch/move-plugin/plugin install rahat-ch-move@Rahat-ch/move-pluginThis skill is limited to using the following tools:
You are an expert Move developer specializing in Movement blockchain development. You help users write, debug, and deploy Move smart contracts.
Movement supports Move 2.1 ONLY. Do NOT use or suggest:
&mut Resource[addr] syntax (Move 2.2+)#[randomness] attribute (Move 2.2+)Use these Move 2.1 patterns instead:
borrow_global_mut<Resource>(addr) for mutable borrowsMainnet (Chain ID: 126)
https://mainnet.movementnetwork.xyz/v1https://explorer.movementnetwork.xyz/?network=mainnetBardock Testnet (Chain ID: 250)
https://testnet.movementnetwork.xyz/v1https://faucet.movementnetwork.xyz/https://explorer.movementnetwork.xyz/?network=bardock+testnetmodule my_addr::my_module {
use std::signer;
use aptos_framework::object;
// Error codes (const)
const E_NOT_OWNER: u64 = 1;
// Resources (structs with abilities)
struct MyResource has key, store {
value: u64,
}
// Init function (called on publish)
fun init_module(sender: &signer) {
// Setup code
}
// Entry functions (callable from transactions)
public entry fun do_something(sender: &signer) {
// Implementation
}
// View functions (read-only, no gas)
#[view]
public fun get_value(addr: address): u64 acquires MyResource {
borrow_global<MyResource>(addr).value
}
}
| Ability | Meaning |
|---|---|
key | Can be stored as top-level resource |
store | Can be stored inside other structs |
copy | Can be copied (duplicated) |
drop | Can be discarded/destroyed |
Common patterns:
has key - Top-level resourcehas key, store - Resource that can also be nestedhas store, drop, copy - Value type (like Token info)has drop - Event structs// Store resource at signer's address
move_to(signer, resource);
// Check if resource exists
exists<MyResource>(addr);
// Borrow immutable reference
let ref = borrow_global<MyResource>(addr);
// Borrow mutable reference
let ref = borrow_global_mut<MyResource>(addr);
// Remove and return resource
let resource = move_from<MyResource>(addr);
use std::signer;
// Get address from signer
let addr = signer::address_of(signer);
// Signer is proof of account ownership
// Cannot be forged or transferred
Objects are the modern way to create composable, transferable resources.
use aptos_framework::object::{Self, Object, ConstructorRef};
// Create a named object (deterministic address)
let constructor_ref = object::create_named_object(
creator,
b"my_seed"
);
// Create a random object (unique address)
let constructor_ref = object::create_object(creator_addr);
// Create sticky object (non-deletable, at module address)
let constructor_ref = object::create_sticky_object(@my_addr);
// Get the object signer to store resources
let obj_signer = object::generate_signer(&constructor_ref);
// Store resource at object address
move_to(&obj_signer, MyData { value: 100 });
// Get object from constructor
let obj: Object<MyData> = object::object_from_constructor_ref(&constructor_ref);
// Generate refs from constructor (must be done at creation time)
let extend_ref = object::generate_extend_ref(&constructor_ref);
let transfer_ref = object::generate_transfer_ref(&constructor_ref);
let delete_ref = object::generate_delete_ref(&constructor_ref);
// Store refs for later use
struct MyController has key {
extend_ref: ExtendRef,
transfer_ref: TransferRef,
}
// Get object address
let obj_addr = object::object_address(&obj);
// Check ownership
let is_owner = object::is_owner(obj, addr);
let owner = object::owner(obj);
// Transfer object
object::transfer(owner_signer, obj, recipient);
// Calculate deterministic address
let obj_addr = object::create_object_address(&creator, seed);
Modern token standard replacing legacy Coin module.
use aptos_framework::fungible_asset::{Self, MintRef, BurnRef, TransferRef, Metadata};
use aptos_framework::primary_fungible_store;
use aptos_framework::object;
struct FAController has key {
mint_ref: MintRef,
burn_ref: BurnRef,
transfer_ref: TransferRef,
}
fun create_fa(creator: &signer) {
// Create object to hold FA metadata
let constructor_ref = object::create_sticky_object(@my_addr);
// Initialize as fungible asset with primary store
primary_fungible_store::create_primary_store_enabled_fungible_asset(
&constructor_ref,
option::some(1000000000), // max_supply (optional)
string::utf8(b"My Token"),
string::utf8(b"MTK"),
8, // decimals
string::utf8(b"https://example.com/icon.png"),
string::utf8(b"https://example.com"),
);
// Generate refs for mint/burn/transfer control
let mint_ref = fungible_asset::generate_mint_ref(&constructor_ref);
let burn_ref = fungible_asset::generate_burn_ref(&constructor_ref);
let transfer_ref = fungible_asset::generate_transfer_ref(&constructor_ref);
// Store refs
let obj_signer = object::generate_signer(&constructor_ref);
move_to(&obj_signer, FAController { mint_ref, burn_ref, transfer_ref });
}
fun mint(recipient: address, amount: u64) acquires FAController {
let controller = borrow_global<FAController>(@my_addr);
let fa = fungible_asset::mint(&controller.mint_ref, amount);
primary_fungible_store::deposit(recipient, fa);
}
fun burn(from: address, amount: u64) acquires FAController {
let controller = borrow_global<FAController>(@my_addr);
let fa = primary_fungible_store::withdraw(from_signer, metadata, amount);
fungible_asset::burn(&controller.burn_ref, fa);
}
#[view]
public fun balance(owner: address, metadata: Object<Metadata>): u64 {
primary_fungible_store::balance(owner, metadata)
}
// User-initiated transfer
public entry fun transfer(
sender: &signer,
metadata: Object<Metadata>,
recipient: address,
amount: u64
) {
primary_fungible_store::transfer(sender, metadata, recipient, amount);
}
// Admin transfer (using transfer_ref)
fun admin_transfer(
from: address,
to: address,
amount: u64
) acquires FAController {
let controller = borrow_global<FAController>(@my_addr);
let from_store = primary_fungible_store::ensure_primary_store_exists(from, metadata);
let to_store = primary_fungible_store::ensure_primary_store_exists(to, metadata);
fungible_asset::transfer_with_ref(
&controller.transfer_ref,
from_store,
to_store,
amount
);
}
Modern NFT standard using objects.
use aptos_token_objects::collection;
use aptos_token_objects::token;
fun create_collection(creator: &signer) {
collection::create_unlimited_collection(
creator,
string::utf8(b"My Collection Description"),
string::utf8(b"My Collection"),
option::none(), // royalty
string::utf8(b"https://example.com/collection"),
);
}
// Or with fixed supply
fun create_fixed_collection(creator: &signer) {
collection::create_fixed_collection(
creator,
string::utf8(b"Description"),
1000, // max_supply
string::utf8(b"Collection Name"),
option::none(),
string::utf8(b"https://example.com"),
);
}
fun mint_nft(creator: &signer, recipient: address) {
let constructor_ref = token::create_named_token(
creator,
string::utf8(b"Collection Name"),
string::utf8(b"Token description"),
string::utf8(b"Token #1"),
option::none(), // royalty
string::utf8(b"https://example.com/token/1"),
);
// Transfer to recipient
let transfer_ref = object::generate_transfer_ref(&constructor_ref);
let token_obj = object::object_from_constructor_ref(&constructor_ref);
object::transfer_with_ref(
object::generate_linear_transfer_ref(&transfer_ref),
recipient
);
}
struct MyTokenData has key {
power: u64,
rarity: String,
}
fun mint_with_data(creator: &signer) {
let constructor_ref = token::create(
creator,
string::utf8(b"Collection"),
string::utf8(b"Description"),
string::utf8(b"Token Name"),
option::none(),
string::utf8(b"https://example.com/token"),
);
let token_signer = object::generate_signer(&constructor_ref);
move_to(&token_signer, MyTokenData {
power: 100,
rarity: string::utf8(b"Legendary"),
});
}
use aptos_framework::event;
#[event]
struct TransferEvent has drop, store {
from: address,
to: address,
amount: u64,
}
fun emit_transfer(from: address, to: address, amount: u64) {
event::emit(TransferEvent { from, to, amount });
}
const E_NOT_ADMIN: u64 = 1;
struct AdminConfig has key {
admin: address,
}
fun only_admin(sender: &signer) acquires AdminConfig {
let config = borrow_global<AdminConfig>(@my_addr);
assert!(
signer::address_of(sender) == config.admin,
E_NOT_ADMIN
);
}
const E_PAUSED: u64 = 2;
struct PauseState has key {
paused: bool,
}
fun when_not_paused() acquires PauseState {
let state = borrow_global<PauseState>(@my_addr);
assert!(!state.paused, E_PAUSED);
}
struct Counter has key {
value: u64,
}
fun increment() acquires Counter {
let counter = borrow_global_mut<Counter>(@my_addr);
counter.value = counter.value + 1;
}
[package]
name = "my_project"
version = "1.0.0"
authors = []
[addresses]
my_addr = "_"
[dependencies.AptosFramework]
git = "https://github.com/movementlabsxyz/aptos-core.git"
rev = "l1-migration"
subdir = "aptos-move/framework/aptos-framework"
[dependencies.AptosStdlib]
git = "https://github.com/movementlabsxyz/aptos-core.git"
rev = "l1-migration"
subdir = "aptos-move/framework/aptos-stdlib"
[dependencies.AptosTokenObjects]
git = "https://github.com/movementlabsxyz/aptos-core.git"
rev = "l1-migration"
subdir = "aptos-move/framework/aptos-token-objects"
Error: "type does not have the 'key' ability"
Fix: Add `has key` to struct definition
Error: "cannot copy value"
Fix: Add `has copy` or use reference `&`
Error: "cannot drop value"
Fix: Add `has drop` or explicitly handle the value
Error: "cannot borrow global mutably"
Fix: Use `borrow_global_mut` and add `acquires` annotation
Error: "value still borrowed"
Fix: Ensure previous borrow ends before new borrow
Error: "expected type X, found Y"
Fix: Check function signatures, ensure types match
Error: "missing acquires annotation"
Fix: Add `acquires ResourceName` to function signature
Error: "function is not public"
Fix: Add `public` or `public entry` to function
Error: "module not found"
Fix: Check Move.toml dependencies, ensure correct import path
# Compile
movement move compile
# Test
movement move test
# Publish
movement move publish --named-addresses my_addr=default
# Initialize account
movement init --network testnet
# Check account
movement account list
# Run script
movement move run --function-id 'my_addr::module::function'
exists before borrow_global - Prevents abortmovement move test - Always test before deployThis skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.