gate.move
Overview
The promise of a truly decentralized metaverse lies in “digital physics”—a set of immutable, composable rules that enable emergent gameplay and player-driven innovation. In the EVE Frontier world contracts, the Smart Gate system is a primary example of this philosophy. Far more than a simple teleportation point, the Gate is a programmable structure that balances core game integrity with unprecedented player agency.
This guide provides a comprehensive technical breakdown of Gate architecture, logic, and extensibility for developers building on the Frontier platform.
Learning Objectives
By the end of this article, you will be able to:
- Explain the three-layer architecture of the Frontier world contracts.
- Describe the lifecycle of a Gate, from anchoring to linking.
- Analyze the “Typed Witness Pattern” used for securing extensions.
- Implement interactions with Gates using the developer toolkit and TypeScript SDK.
1. Architecture and Role
The Frontier world contracts utilize a three-layer model to prioritize composition over inheritance.
- Layer 1: Composable Primitives: Focused modules handling “physics” logic like spatial positioning (
location.move), resource consumption (fuel.move), and state management (status.move). - Layer 2: Game-Defined Assemblies: Structures like the
Gatethat compose these primitives into functional units. - Layer 3: Player Extensions: Custom smart contracts that register with assemblies to modify their behavior.
2. Operational Lifecycle and Physics
A Gate’s lifecycle begins with Anchoring, where it is initialized in the ObjectRegistry and linked to a NetworkNode for power.
The Gate Lifecycle
sequenceDiagram
participant Admin
participant Registry as ObjectRegistry
participant NWN as NetworkNode
participant Gate
participant Char as Character
Admin->>Registry: anchor()
Admin->>NWN: connect_assembly(gate_id)
Admin->>Gate: Create Gate Object
Admin->>Char: Transfer OwnerCap
Note over Gate: Status: Anchored/Offline
Char->>Gate: online(owner_cap, nwn)
Gate->>NWN: reserve_energy()
Note over Gate: Status: Online
Core Physics: Linking and Energy
Gates function by linking to another gate to create a transport link. This process is governed by strict “digital physics”:
- Ownership: Both gates must be owned by the same character.
- Distance: Gates must be at least 20KM apart.
- Location Proofs: Linking requires a
distance_proof(a signature from a trusted server) to verify the gates are within the maximum distance allowed for that gate type. - Energy Integration: A gate cannot go online unless its connected
NetworkNodehas reserved energy for it. If a network node is updated or unanchored, the system uses a “Hot Potato” pattern (e.g.,UpdateEnergySources) to ensure all connected gates are updated atomically in the same transaction block.
3. The Moddability Pattern: Type-Based Authorization
The true power of Frontier lies in Moddability. Owners can define custom jump rules through extension contracts using the Typed Witness Pattern.
Authorization Flow
- Extension Development: A developer deploys a contract defining a witness type (e.g.,
XAuth). - Registration: The gate owner calls
authorize_extension<XAuth>, adding theTypeNameto the gate’sextensionfield. - Enforcement: Once an extension is configured, the standard
jumpfunction will abort. Travelers (via the game client) must usejump_with_permit.
graph TD
A[User wants to Jump] --> B{Extension Configured?}
B -- No --> C[Call jump]
C --> D[Check Online & Linked]
D --> E((Success))
B -- Yes --> F[Call jump_with_permit]
F --> G[Check Online & Linked]
G --> H[Validate JumpPermit]
H --> I[Check Auth Witness Type]
I --> J[Check Expiry & Route Hash]
J --> K[Delete Permit]
K --> E
4. Logic Deep Dive: Jumps and Permits
There are two primary ways to traverse a gate:
A. Default Jump
Allowed only when no extension logic is configured. It validates that both gates are online and linked before emitting a JumpEvent.
B. Jump with Permit
Required when an extension is authorized. The extension logic (Layer 3) issues a JumpPermit object to the player.
- Route Hashing: Permits are bound to a specific source/destination pair via a
route_hash. - Direction Agnostic: The hash is computed from concatenated IDs (A+B) using
blake2b256, ensuring one permit works for both A→B and B→A. - Single-Use: The
JumpPermitobject is deleted upon a successful jump to prevent replay attacks.
5. Developer’s Toolkit: Bulk Queries and Discovery
External tools (like route planners) can interact with this system by querying Sui’s shared objects and indexed events.
Building a Route Graph
To map the traversable network, a developer should query for Gate objects and filter by:
status.is_online(): Only active gates can be used.linked_gate_id: This defines the edge in the graph.extension: If aTypeNameis present, the edge has restricted access.
Identifying Traversable Paths for Players
A planner determines if a restricted gate is “open” for a specific player by querying the player’s address for JumpPermit objects:
- Query Objects: Call
suix_getOwnedObjectsfor typeworld::gate::JumpPermit. - Validate Metadata:
- Character ID:
permit.character_idmust match the player’sCharacterID. - Route Hash: Match against the hash of the intended source and destination.
- Expiry:
permit.validity_periodmust be greater than currentclocktime.
- Character ID:
6. Security and Obfuscation
To support “fog of war” mechanics, Frontier stores locations as cryptographic hashes. This allows for the verification of proximity without revealing exact coordinates on-chain. developers must use the verify_distance function within the world::location primitive to prove that two entities are legally interacting based on their private coordinates.
7. Developer’s Implementation Guide: TypeScript/JavaScript
Interacting with Gates requires a mix of standard Sui SDK patterns and Frontier-specific utilities for managing game state and character identity.
1. Environment Initialization
Before interacting with the world, developers must initialize a context that includes the SuiClient, the user’s Keypair, and the Frontier network configuration.
import { initializeContext, getEnvConfig } from "../utils/helper";
const env = getEnvConfig(); // Loads PRIVATE_KEY and SUI_NETWORK from .env
const { client, keypair, config, address } = initializeContext(
env.network,
env.playerExportedKey,
); // Sets up the client and identifies the package IDs
2. Addressing Objects: Deterministic Derivation
EVE Frontier uses a deterministic registry system to manage object IDs. Instead of hardcoding hex strings, developers should derive object IDs using the ObjectRegistry ID and the in-game ITEM_ID.
import { deriveObjectId } from "../utils/derive-object-id";
import { NWN_ITEM_ID, ASSEMBLY_ITEM_ID } from "../utils/constants";
// Derive the on-chain Object ID for a specific structure
const networkNodeId = deriveObjectId(
config.objectRegistry,
NWN_ITEM_ID,
config.packageId,
);
const gateId = deriveObjectId(
config.objectRegistry,
ASSEMBLY_ITEM_ID,
config.packageId,
);3. Pattern: Borrow, Use, Return (OwnerCaps)
Most Gate operations (linking, going online) require an OwnerCap. In Frontier, these are typically stored within the player’s Character object. Developers must use a “Borrow/Return” pattern within a single transaction block to authenticate.
import { Transaction } from "@mysten/sui/transactions";
import { MODULES } from "../utils/config";
const tx = new Transaction();
// 1. Borrow the OwnerCap from the Character object
const [ownerCap] = tx.moveCall({
target: `${config.packageId}::${MODULES.CHARACTER}::borrow_owner_cap`,
typeArguments: [`${config.packageId}::${MODULES.GATE}::Gate`],
arguments: [tx.object(characterId), tx.object(gateOwnerCapId)],
});
// 2. Use the OwnerCap for the Gate operation
tx.moveCall({
target: `${config.packageId}::${MODULES.GATE}::online`,
arguments: [
tx.object(gateId),
tx.object(networkNodeId),
tx.object(config.energyConfig),
ownerCap,
],
});
// 3. Return the OwnerCap back to the Character object
tx.moveCall({
target: `${config.packageId}::${MODULES.CHARACTER}::return_owner_cap`,
typeArguments: [`${config.packageId}::${MODULES.GATE}::Gate`],
arguments: [tx.object(characterId), ownerCap],
});4. Executing a Jump Transaction
When a user performs a jump, the client must construct the transaction based on whether an extension is active.
Standard Jump (Public)
tx.moveCall({
target: `${config.packageId}::${MODULES.GATE}::jump`,
arguments: [
tx.object(sourceGateId),
tx.object(destinationGateId),
tx.object(characterId),
],
});Jump with Permit (Restricted)
For restricted gates, the developer must first fetch the JumpPermit object from the user’s inventory and pass it as an argument.
// Fetch permit from user inventory via RPC
const permitObject = await client.getOwnedObjects({
owner: playerAddress,
filter: { StructType: `${config.packageId}::gate::JumpPermit` },
});
tx.moveCall({
target: `${config.packageId}::${MODULES.GATE}::jump_with_permit`,
arguments: [
tx.object(sourceGateId),
tx.object(destinationGateId),
tx.object(characterId),
tx.object(permitObject.data[0].data.objectId), // The single-use permit
tx.object(CLOCK_OBJECT_ID), // Required for expiry check
],
});5. Sponsored Transactions
Frontier supports sponsored transactions, allowing admins to cover gas costs for players. This requires collecting signatures from both the player and the sponsor before execution.
import { executeSponsoredTransaction } from "../utils/transaction";
await executeSponsoredTransaction(
tx,
client,
playerKeypair,
adminKeypair,
playerAddress,
adminAddress,
);Conclusion
The Smart Gate system represents a paradigm shift in game infrastructure. By separating core physics from player-defined rules, Frontier creates a living, programmable world. Whether building a simple transport route or a complex, tribe-restricted toll network, developers have the tools to define the very horizon of the EVE Frontier.
By combining the Typed Witness Pattern for logic with the Borrow/Return pattern for authentication, EVE Frontier provides developers with a robust framework for building complex space infrastructure. These TypeScript patterns allow your applications to navigate the “digital physics” of the world while maintaining strict security and character-based identity.