Post
Share your knowledge.
How can I store and update a history of object actions in Move?
I'm trying to understand this aspect of the Sui Network because I'm either building, debugging, or deploying something that touches this area. I want a detailed explanation of how this mechanism or feature works, along with relevant CLI usage, Move code structure, or architectural concepts. My goal is to gain enough clarity to apply this knowledge in a real project—whether that's a custom smart contract, an NFT system, a wallet integration, or a DeFi tool. The Sui Network has unique features compared to EVM chains, so I'm particularly interested in what sets it apart and how that affects development best practices. It would help to have sample code, command line examples, or typical errors to watch for, especially when using the Sui CLI, SDK, or deploying on localnet/testnet. Ultimately, I want to avoid common mistakes, follow the best security principles, and ensure that the functionality I’m working on behaves as expected under realistic conditions.
- Sui
- NFT Ecosystem
Answers
4To store and update a history of object actions in Move on the Sui Network, you typically use a vector or table within a struct to record past actions. Unlike EVM chains that rely on emitted logs (events) for off-chain indexing, Sui lets you persist action history directly inside objects, enabling rich on-chain state tracking. This is particularly useful for NFTs, DeFi ledgers, or systems where a verifiable, queryable trail of events is essential.
Here's how you can model this in Move. Suppose you're tracking actions on an object (like transfers or upgrades). You define a struct like this:
module my_project::action_log { use std::vector; use std::string;
struct ActionLog has key {
id: UID,
actions: vector<string::String>,
}
public fun new(ctx: &mut TxContext): ActionLog {
ActionLog {
id: object::new(ctx),
actions: vector::empty<string::String>(),
}
}
public fun record_action(log: &mut ActionLog, action: string::String) {
vector::push_back(&mut log.actions, action);
}
}
You create the object during a transaction using:
sui client call --package
Each time something important happens, you call record_action and pass in a human-readable string or encoded data, like "Minted by 0xabc123" or "Transferred to 0xdef456".
If the history becomes large, you may consider chunking it across multiple ActionLog objects or summarizing older actions to save storage. You can also use std::table for mapping timestamps or IDs to messages:
use std::table::{Table, new, add};
struct TimedLog has key { id: UID, log: Table<u64, string::String>, }
Be aware that both vector and table structures grow with use, which increases gas cost. Best practice is to avoid unbounded growth and consider periodic archiving or pagination logic.
A common mistake is trying to mutate a shared object without marking your function as public(entry), or trying to store complex objects without store or drop abilities, which causes compiler errors.
Before updating logs, use the CLI to inspect their current state:
sui client object
And always test your logic on localnet using:
sui start
Then deploy and interact with your module:
sui client publish --path
By storing history on-chain, you gain trustless traceability that can be directly queried from dApps or wallets—no need to rely solely on off-chain indexers.
For more on designing mutable, trackable objects in Move, see: https://docs.sui.io/build/programming-model/objects
Unlike EVM chains that use event logs (which are only available off-chain), Sui lets you store action history directly inside objects. This means you can keep a full, queryable on-chain record — super useful for NFTs, DeFi ledgers, or any system that needs a traceable history.
Let’s say you want to track actions like transfers or upgrades on an object. Here’s a simple Move module for that:
module my_project::action_log {
use std::vector;
use std::string;
struct ActionLog has key {
id: UID,
actions: vector<string::String>,
}
public fun new(ctx: &mut TxContext): ActionLog {
ActionLog {
id: object::new(ctx),
actions: vector::empty<string::String>(),
}
}
public fun record_action(log: &mut ActionLog, action: string::String) {
vector::push_back(&mut log.actions, action);
}
}
Now in your Sui app, whenever something happens — like a mint, transfer, or upgrade — you can log it like:
On Sui, you can directly store a history of actions within an object itself, unlike other blockchains (like EVM) where you mostly rely on external logs to track events. This means you can keep a verifiable record of things that happen to an object right there with the object, which is great for NFTs, financial ledgers, or anything needing a clear history.
module my_project::action_log {
use std::vector;
use std::string;
use sui::object::{Self, UID}; // Make sure to import object and UID
use sui::tx_context::{Self, TxContext}; // Make sure to import tx_context and TxContext
struct ActionLog has key {
id: UID,
actions: vector<string::String>, // This vector stores the history
}
// Function to create a new ActionLog object
public fun new(ctx: &mut TxContext): ActionLog {
ActionLog {
id: object::new(ctx),
actions: vector::empty<string::String>(),
}
}
// Function to add an action to the log
public fun record_action(log: &mut ActionLog, action: string::String) {
vector::push_back(&mut log.actions, action);
}
}
(You'll need to replace <YOUR_PACKAGE_ID> with your actual package ID and adjust the gas budget).
Then, whenever something happens to your main object (like an NFT transfer or an update), you call the record_action function on its linked ActionLog object, passing a description like "Transferred to 0xabc..."
On the Sui Network, if you want to store and track the full history of an object — like NFT transfers, upgrades, or user interactions — you can do it on-chain using Move. This is different from Ethereum and other EVM chains, where action history is usually stored off-chain via events.
In Sui, you can design a struct with a vector or table to keep a log of past actions. For example, to track a list of human-readable events like “Minted by 0xabc” or “Transferred to 0xdef”, just define an ActionLog struct like this:
module my_project::action_log {
use std::vector;
use std::string;
struct ActionLog has key {
id: UID,
actions: vector<string::String>,
}
public fun new(ctx: &mut TxContext): ActionLog {
ActionLog {
id: object::new(ctx),
actions: vector::empty<string::String>(),
}
}
public fun record_action(log: &mut ActionLog, action: string::String) {
vector::push_back(&mut log.actions, action);
}
}
When you run your transaction, you can create a new log object with:
sui client call --package --module action_log --function new --args --gas-budget 10000
And every time an important action happens, just call record_action and pass in a message like "Upgraded skin" or "Leveled up to 10".
Do you know the answer?
Please log in and share it.
Sui is a Layer 1 protocol blockchain designed as the first internet-scale programmable blockchain platform.
- How to Maximize Profit Holding SUI: Sui Staking vs Liquid Staking616
- Why does BCS require exact field order for deserialization when Move structs have named fields?65
- Multiple Source Verification Errors" in Sui Move Module Publications - Automated Error Resolution55
- Sui Move Error - Unable to process transaction No valid gas coins found for the transaction419
- Sui Transaction Failing: Objects Reserved for Another Transaction410