Build

End-to-end build guide: write a gate extension, publish it, and test the complete jump flow. For concepts and behaviour, see the Gate README.

Prerequisites

Gate API

Custom contracts use the typed witness pattern: define a witness struct (Auth) and register it on the gate. The world gate modulearrow-up-right verifies the type at runtime.

Authorize an extension:

public fun authorize_extension<Auth: drop>(
    gate: &mut Gate,
    owner_cap: &OwnerCap<Gate>,
)

Issue a jump permit (called by your extension):

public fun issue_jump_permit<Auth: drop>(
    source_gate: &Gate,
    destination_gate: &Gate,
    character: &Character,
    _: Auth,
    expires_at_timestamp_ms: u64,
    ctx: &mut TxContext,
)

Jump with a permit:

JumpPermit (from the world module): contains character_id, route_hash, and expires_at_timestamp_ms. The route_hash is direction-agnostic — a permit for Gate A → Gate B also works for Gate B → Gate A.


Below is the high-level understanding:

1. Understand the example contract

The scaffold includes a working gate extension at move-contracts/smart_gate/. It has three modules:

config.move — shared configuration object and the witness type:

XAuth is the witness type registered on the gate. ExtensionConfig is a shared object where extension modules store their rules as dynamic fields. AdminCap controls who can update those rules.

tribe_permit.move — issues jump permits only to characters in a configured tribe:

The key pattern: your module calls gate::issue_jump_permit<XAuth> with your witness type. The gate verifies XAuth matches the type registered via authorize_extension.

corpse_gate_bounty.move — a more advanced example that combines a storage unit interaction with a gate permit. Players deposit a bounty item and receive a jump permit in return.

2. Build and publish

For local network, provide the world package path:

From the publish output, note the package ID and the ExtensionConfig object ID. Set both in your .env:

3. Configure extension rules

Set the tribe and expiry parameters on the shared ExtensionConfig:

This calls tribe_permit::set_tribe_config with a tribe ID and permit expiry duration. You can modify ts-scripts/smart_gate/configure-rules.ts to change the values.

4. Authorize the extension on both gates

Register your XAuth type on each gate so it switches from default (open) to permit-based:

Under the hood this builds a transaction that:

  1. Borrows the gate's OwnerCap from the character

  2. Calls gate::authorize_extension<XAuth> on the gate

  3. Returns the OwnerCap to the character

After this, the default jump function is disabled — players must use jump_with_permit.

5. Issue a jump permit

Issue a permit to a player character:

This calls tribe_permit::issue_jump_permit, which checks the character's tribe and mints a JumpPermit object owned by the character.

Players can collect/buy permit from a builder dapp.

6. Jump with permit

The player uses their permit to jump through the gate:

A successful jump emits a JumpEvent and consumes the permit. Ideally this flow happens in the game.

Writing your own extension

To create a gate extension from scratch:

  1. Define a witness struct (e.g., public struct MyAuth has drop {})

  2. Write a function that enforces your rules and calls gate::issue_jump_permit<MyAuth>

  3. Publish the contract and authorize it on your gate with gate::authorize_extension<MyAuth>

Here's a minimal toll gate that requires payment before issuing a permit:

Other ideas:

  • Allowlist — check a stored list of approved characters

  • Bounty gate — require depositing an item to earn passage (see corpse_gate_bounty.move in the scaffold)

See the Gate API above for the full function signatures.

Reference

Last updated