# State, timing, and transaction discipline Move owns game truth. Agents can read that truth from Sui GraphQL (itself an indexed chain service) and submit direct SDK transactions without connecting to the AresRPG server. Alternatively, opt into AresRPG's indexer-backed WebSocket for prepared gameplay projections and realtime services. Choose with the player; start chain-native for a bounded task whose required state you can decode reliably. ## Choose a read path | Tradeoff | Chain-native: Sui GraphQL + direct SDK | Optional AresRPG indexing + direct SDK | | --- | --- | --- | | Setup and authentication | GraphQL reads need no game login; writes need an authorized Sui signer and gas | Allowed WebSocket origin, personal-message proof, baseline, subscriptions and reconnect handling; ordinary wallets work without Google | | Dependencies | Compatible Sui GraphQL read service, gRPC write service, matching pins/content, your read adapter | Same SDK/gRPC write service plus the AresRPG server and its indexer | | State preparation | Read objects, dynamic fields, custody and events; decode and derive gameplay inputs yourself | Prepared account/world/fight rows, market windows, history and rankings | | Freshness and latency | Provider/checkpoint visibility and polling cadence; wait until reads include your certified writes | Push updates avoid repeated scans, but projection lag is extra; reconcile receipts before late packets | | Limits and cost | Provider-specific quotas, page/query limits and pricing; pagination and polling consume requests | Server message/connection limits plus bounded observations; one live connection per address can replace the browser | | Scope | On-chain actions; no realtime presence/chat or courtesy fight relays | Indexed convenience plus off-chain presence/chat and courtesy relays | Neither path promises a latency or quota SLA. Check the chosen provider's current limits, back off on throttling, paginate completely where completeness matters, and cache only with version/freshness checks. Use bounded polling of the objects needed for the next action instead of repeatedly scanning the whole chain. A missing page or unavailable query is unknown state, not an empty inventory. For exact server constraints and login details, see [optional indexing](https://aresrpg.world/agents/indexing.txt). ## Default: observe the chain directly Confirm network, deployment pins and signing address. Use a matching release: source branch `edge` may be ahead of production. A GraphQL reader is separate from the SDK writer. The writer must use `rpc_url` or a compatible gRPC client: the SDK rejects GraphQL-backed writes because capped event pages cannot provide complete business receipts. `SDK({ graphql_url })` is read-only, even if a signer is present. See [the SDK cookbook](https://aresrpg.world/agents/sdk.txt). Build only the read model needed for the approved activity: - Discover personal kiosk capabilities and custody. Characters/items may be locked in kiosks, equipped, held by fights, listed, or reserved by trades. Address-owned objects alone are not a complete roster or spendable inventory. - Fetch exact current objects and dynamic fields with their versions. Validate types against original package IDs; call targets use latest package IDs from the same pins. Keep u64 values as bigint/decimal strings and handle pagination, deletion and unavailable data explicitly. - Read Character world/checkpoint/root/protector and dungeon state, item rolls and equipment, and the selected World/Zone/Fight state as needed. Decode from the current Move owners; a raw object is not a protocol `CharacterRow` or `HydratedFightCheckpoint`. - Derive zone populations from the certified seed/generation/consumption and matching published content with the existing deterministic twin. Hydrate combat checkpoints with their source rows and spells. Do not invent targets, pack indexes or future randomness. - Read the deployment's shared `Version` gate (`current_version` zero means gameplay is frozen) and current chain clock/epoch. Require recent evidence before writing. Poll with a bounded cadence and refresh affected inputs after each certified transaction; GraphQL visibility can trail the SDK's gRPC receipt. The SDK resolves transaction references, not a complete player-state model. `sdk.hydrate` is not fight hydration; the narrow `read_character_checkpoint` returns a position, not the full travel timestamp/pet proof. The examples are composition helpers, not a ready-made chain inventory reader or autonomous game client. If the required decode/discovery is not implemented, stop that activity or deliberately choose the indexed path. Do not silently fall back to WebSocket login. ## Freshness and clocks For chain-native play, obtain time/epoch from fresh chain data and verify the shared `Version` gate and gameplay freeze state, rather than waiting for `packet/server_info` or a socket-ready flag. For the indexed path, use its [readiness and freshness checks](https://aresrpg.world/agents/indexing.txt). Neither a local timer nor GraphQL request completion proves the data includes the latest write. A chain sample carries its age at receipt. Retain the monotonic receipt time and preserve that age. Interpolation can estimate elapsed chain time for planning, but a timer is only a wake-up hint. Stop authorizing actions when the sample becomes stale. Never use `Date.now()` or a changed device clock to unlock a transaction. For conservative scripted timing, wait until a newly observed chain timestamp itself satisfies the deadline. This may wait slightly longer than interpolation but avoids using a prediction as proof. Recheck all inputs on wake: character, world, checkpoint, target, custody, root, fight turn, and budget. ### Travel calculation The chain's integer speed budget is 1,150 / 100,000 blocks per millisecond, or 11.5 blocks per second. With a pet both at the stored checkpoint and now, it is 1,725 / 100,000. This is a validation allowance, not a command to move the browser at that exact speed. Let D² = (target_x − checkpoint_x)² + (target_z − checkpoint_z)². Let L be the smallest integer with L² ≥ D². The earliest travel deadline is: checkpoint_at_ms + ceil(L × 100,000 / speed_integer) This inversion includes the chain's floor on distance budget. An ordinary floating-point distance/speed estimate can unlock too early at rounding boundaries. Use the existing `travel_proof_ready` helper to check the final candidate timestamp. The example planner finds the first eligible millisecond with that helper rather than duplicating the formula. A future checkpoint timestamp represents a root. At the same location, its timestamp is already the earliest release; a new travel leg needs additional time after it. An unresolved protector is an obligation, not a timer worth waiting out. `checkpoint_world` must match the current world. ### Other timing rules | Activity | Authority | | --- | --- | | Gather again or move after harvesting | New character checkpoint root and travel proof | | Refresh depleted zone population | Zone search timestamp + `ZONE_RESEARCH_TTL_MS` | | End Turn / timeout crank / force start | Fight checkpoint + generated `CONTRACT_CONSTANTS` | | Feed a pet | `floor(chain_timestamp_ms / 86,400,000)` greater than its last feed day | | Start/complete Mastery | Current Sui epoch and assignment fields | | Leaderboard reset | Returned UTC calendar-month reset timestamp | ## Submit once and recover honestly Use SDK domain builders wherever possible. They hydrate unknown inputs, resolve custody, simulate the exact transaction before signing, submit once, and project receipts. Original package IDs identify types; latest package IDs are call targets. Read both from the selected pins; never swap them or hand-copy object addresses from an old guide. Random-consuming doors must remain terminal in their SDK composition. Do not attach a generic kiosk close or extra gameplay call after them. Serialize dependent writes using one SDK session; it maintains a previous-digest visibility barrier and receipt-fed object refs. Distinguish these outcomes: | Outcome | Response | | --- | --- | | Proven unsigned refusal or wallet cancellation | No chain execution; correct the cause and re-evaluate the intent | | Certified success with digest | Record it once, fold receipts, reconcile fresh reads/projections | | Certified on-chain failure with digest | Record paid gas and stop; inspect state before any new authorized attempt | | Submission response lost or incomplete | Retain the exact pending digest and recover its receipt; do not submit a replacement | | Read/projection delayed after success | Wait/reconcile; never repeat the successful action to force the UI to catch up | The SDK derives the digest before submission and recovers the exact transaction. A headless writer must supply durable `transaction_storage`; the browser's session storage does not exist in Bun. Preserve the SDK's pending record across process restarts. Do not delete it to unblock a script. Keep a separate durable activity ledger for spending and multi-step intents. Only one process may use a wallet's writer state at a time. Recovery can invalidate the intent that was queued before the outcome became known. Refresh game state and rebuild the plan. An error after a successful first phase does not mean the phase failed. Never blindly retry a whole craft, purchase, fight settlement, claim, or batch because its wrapper threw. ## A bounded automation loop Each routine needs a target, stop conditions, quantity/turn/time limits, spending ceiling, and recovery policy. Persist intent and status before submission. After each result, consume only certified facts, observe fresh state, and decide again. Stop new writes on budget exhaustion, identity change, manual takeover, unexpected custody, stale/frozen state, missing timing evidence, or failed/uncertain execution. Complete necessary recovery only within the retained authority. Surface the smallest decision needed from the player rather than silently switching activities or increasing risk. Use bounded fresh GraphQL reads, or push updates when explicitly using the indexed path, for live facts. Timers schedule inspection, not unconditional transactions. Finite gathering, crafting targets, and one dungeon run are better initial scripts than an unbounded “play forever” loop. ## Source owners - [Gameplay version/freeze gate](https://raw.githubusercontent.com/aresrpg/aresrpg/edge/packages/move/sources/version.move) - [Character checkpoint fields](https://raw.githubusercontent.com/aresrpg/aresrpg/edge/packages/sdk/src/character_checkpoint.ts) - [Wire contract and travel helper](https://raw.githubusercontent.com/aresrpg/aresrpg/edge/packages/protocol/src/packets.ts) - [Clock reducer](https://raw.githubusercontent.com/aresrpg/aresrpg/edge/packages/frontend/src/modules/chain_clock.ts) - [Transaction execution and recovery](https://raw.githubusercontent.com/aresrpg/aresrpg/edge/packages/sdk/src/transaction_execution.ts) - [World travel authority](https://raw.githubusercontent.com/aresrpg/aresrpg/edge/packages/move-math/sources/world_map.move)