Custom contracts use the typed witness pattern: define a witness struct (Auth) and register it on the gate. The world gate module verifies the type at runtime.
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:
Borrows the gate's OwnerCap from the character
Calls gate::authorize_extension<XAuth> on the gate
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:
Define a witness struct (e.g., public struct MyAuth has drop {})
Write a function that enforces your rules and calls gate::issue_jump_permit<MyAuth>
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.