Technical reference
Agent SDK & qualification
Register immutable strategies, follow versioned arenas and recover scoped human controls.
Status and deployment boundary#
Manifest version 5 supports rules 15, 16 and 17 on the Interlude v3 hub. It has one tournament lane and four independent friendly lanes with reusable official controllers. Rules 16/17 add heartbeat-based friendly pauses; rules 17 use 300-unit/s paddles and fresh presence at launch. Version 4 selects the earlier two-lane rules 15 deployment. Version 3 selects rules 11, and version 2 describes the rules 10 pool. Use the explicit capabilities and immutable rules in each manifest; old URLs retain their original contracts.
Read GET /api/agents/config and call validateAgentPoolManifest. The manifest pins Monad Testnet (10143), execution chain 4242, the hub, common contracts, approved arenas and runtime hashes. An explicit releaseStage: "testnet-preview" carries the published operational review reference in previewEvidence. It keeps verifiedCapacity: 0, qualificationEvidence: null and qualified: false; the endurance qualification has not passed. Normal qualified releases require their separate qualification evidence. Both stages require the actual on-chain admission switches. Deployed addresses do not prove available capacity.
Every match reference contains chainId, app, epoch and id. A published old match has no live node attached, so an old URL cannot join a later game after renewal.
Write an on-chain strategy#
Community agents implement IPongStrategy; externally hosted controllers remain disabled. The arcade calls decide(PongView) every 100 milliseconds of game time under a 50,000-gas STATICCALL. Return -1 for up, 0 to hold or 1 for down. A revert, excessive gas or another value retains the previous direction and counts as an invalid decision. It cannot stop the other seat.
The view identifies mode, side, game time, paddle centres, effective half-height, scores and live balls. Positions use 10¹² units per pixel, velocities 10⁶ units per pixel per second, and time microseconds. The court is 1024 by 576 pixels, with y increasing downwards. Pooled rules 10 and 11 include size effects in the reported half-height; SPLIT PADDLE still has a non-colliding central gap. Historical single-application rules report base height instead.
The immutable opcode check rejects storage access, external calls, block-dependent inputs, logs and contract creation. Proxies and mutable external dependencies cannot preserve a frozen tournament version. The complete runtime is scanned, including appended metadata. Compile the example without that trailer:
FOUNDRY_PROFILE=strategies forge build --root contracts
contracts/src/agents/examples/TrackerStrategy.sol is the starting example. Deploy it once on Monad Testnet with the intended immutable creator. Deployment requires creator-paid test gas. Keep the address and transaction reference instead of deploying another identity after a lost response.
Register a declared identity#
AgentCatalog verifies code, creator ownership and code hash. The SDK prepares a creator-signed registration; preparation does not send a transaction:
const call = await preparePoolRegistration(monad, manifest, creator, {
strategy: deployedStrategy,
name: 'My tracker',
avatar: 7,
modes: 3, // 1 Classic, 2 Chaos, 3 both
});
await sponsor.send(call);
The EIP-712 domain is PONGIT Agent Catalog, version 1, chain 10143, at the manifest's catalogue. The message binds strategy, creator, metadata, modes, expiry and nonce. The helper checks the on-chain digest before requesting a signature. Official bot status comes from registry identities, never a submitted name.
A new community strategy starts unavailable. Its creator calls setAvailable(strategy, true) directly on Monad before qualification can select it. This action requires creator-paid test gas; it is separate from free sponsored play and is not an entry fee.
The current example is agent-sdk/pool-strategy.ts, also packaged as pongit-register-strategy. Supply an already deployed STRATEGY and a dedicated CREATOR_KEY through your private environment. It reuses an existing matching identity and refuses a closed public gate before signing. AGENT_AVAILABILITY=on explicitly requests the creator-paid availability transaction; off disables future selection without interrupting a reserved match.
The example records the exact signed transaction before sending, resolves its receipt on retry, and stops if a nonce was consumed without that receipt. Keep its private .agent-state directory and use only one writer per creator. It stores no private key and prints no signed payload. Community metadata is a commitment: the current reader displays a strategy address, not a name reconstructed from a hash.
Technical qualification#
Friendly qualification is selected separately for each requested mode. A completed, published trial requires at least three valid decisions and no invalid decision. Winning is unnecessary. Cancellation or a temporary engine failure schedules another trial instead of declaring incompatibility.
Qualification and participation locks live on Monad. A newly deployed strategy waits for a suitable arena whose pinned base block includes it. Registration cannot rewrite an already running engine's external state. After setup is sealed, the service cannot manually award a passing qualification.
Authorize and authenticate#
Human challenges use a limited two-hour ArcadeFamily. preparePoolFamily requests the owner's explicit signature; loadPoolFamily restores the authorization. The root key is not persisted. The arcade key, grant and pending command journal stay in sessionStorage.
preparePoolChallenge binds grant, action, agent, mode, challenge ID, nonce and expiry to PONGIT Agent Challenges. This key authorizes admission and gameplay, not spending, strategy registration or administration. Approved successive arenas reuse the family. Final real cross-arena browser checks and physical-device verification must be distinguished.
Revocation of future admissions is distinct from revocation inside an active arena. A network failure does not prove revocation and must not erase an uncertain transaction. Explicit renewal reconciles sponsorship first, registers the family if needed, then authorizes it against the current arena revision.
Sponsorship and uncertain responses#
createPoolSponsor journals the exact signed call before sending it. The operation ID commits to target and calldata. POST /api/agents/transactions accepts canonical, signed, zero-value calls from a narrow allowlist: family registration/revocation, catalogue registration and challenge commands. Strategy execution, arena control, availability administration and financial calls are outside that endpoint.
GET /api/agents/operations/:id returns queued, pending, confirmed or failed. A lost reply, timeout, 429 or missing receipt preserves the call. Resume before signing another. A 404 resubmits the same intent, not a new nonce. An exact receipt or supported explicit non-acceptance resolves the journal.
Signatures and signed calldata must never appear in public diagnostics. Show an actionable status and diagnostic reference. The pending journal remains private even though the eventual chain transaction is public.
Observe and control#
createPoolObserver verifies arena, epoch, rules, participants and binding. It consumes engine events with bounded fallback reads, creates no wallet and sends no ticks.
A human participant uses createPoolPlayer with a saved family and a single writer lock:
const session = loadPoolFamily(manifest, playerAddress, sessionStorage);
if (!session) throw new Error('Authorize this arcade family first');
const player = createPoolPlayer(manifest, matchView, session, {
base: monad,
storage: sessionStorage,
socket: url => new WebSocket(url),
});
const stop = player.watch(renderConfirmedState);
await player.recover();
await player.move(-1);
// On blur or opening a dialog: await player.move(0).
// On leaving: stop(); player.close();
Hold a per-account Web Lock before enabling browser controls. Direction changes coalesce while a command is pending. After response loss, reconcile the exact transaction without deleting the journal. A receipt from an earlier match cannot become a snapshot of a new match.
For friendlyPause: "heartbeat-v1", an active human client calls player.heartbeat() from a 100-ms wake-up loop while it owns the control lock and has a freshly painted live view. Coalesce pending pulses; a receipt does not itself renew the picture's timestamp. In rules 17, this loop also supplies launch presence after the countdown. player.heartbeat(true) confirms readiness after an authoritative pause. These calls share the command journal; never start a second writer or pretend that local animation confirms a collision. Rules 16/17 preserve the physics cursor and queued movement while catching up.
Read interfaces#
The site prefixes these routes with /api/agents; private services use /agents. Read responses include an observation block, hash, timestamp and revision. ETags avoid re-downloading unchanged responses. RPC failure never becomes a cached empty catalogue.
| Method and route | Purpose |
|---|---|
GET /config | Versioned contracts, arenas and opening gates |
GET /catalog?offset=0&limit=16 | Identities, qualification and participation |
GET /live | Actual current matches |
GET /matches/:app/:epoch/:id | Full reference and published result |
GET /challenges/:player | Waiting or assigned challenge |
GET /tournaments?offset=0&limit=16 | Tournament history |
GET /tournaments/:id | Entrants, fixtures, standings, attempts and corrections |
GET /rankings?mode=0&offset=0&limit=16 | Published ELO; optional block pins pagination |
GET /replay?app=...&epoch=...&id=... | Recorded snapshots or explicit availability |
Pages contain at most 32 records. Invalid references return 404; invalid pagination returns 400. Busy reads return recoverable errors. Replay availability distinguishes available, partial, indexing, pruned, corrected and unavailable.
Sessions, publication and release#
A reusable arena accepts contract-issued tickets through the authorized testnet admission bridge. Tickets bind the full match reference, participants, rules and source block. Each result is published and captured before its lane is reused. A community controller must exist in the arena's pinned base state. Official friendly copies have separate controls, nonces, memory and results; they do not release competitive participation or change competitive ELO.
Hub versions have different release behaviour. A 3 October RPC-fork test of deployed v3 code released 16,000 synthetic batches with 1,024 distinct slots using 82,053 gas before refunds, retaining the latest values. That is a bytecode test, not a hosted capacity or full-load endurance proof. Older hub measurements and old interrupted delegations keep their own evidence. Closing any arena still requires the real protocol release delay; another verified arena must serve new games meanwhile.
Contracts own results, ratings and tournament progression. Initial publication remains contestable. Corrections retain old attempts and repair dependent standings or branches, without silently duplicating ELO.
Build the SDK and historical compatibility#
npm ci
node scripts/build-agent-sdk.mjs
npm pack ./agent-sdk
The package includes pool registration, sponsorship, observation, family and player helpers. Historical createAgentClient, agent-sdk/example.ts and agent-sdk/strategy.ts target the earlier single-application protocol. They are retained for old deployments and are not the rules-11 registration workflow.
The player guide describes tournament rules and the public qualification boundary. A simulated test, incomplete run or running container is not a final 24-hour service result.