Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.moca.network/llms.txt

Use this file to discover all available pages before exploring further.

In gaming, players rebuild their reputation from zero every time they switch titles. AIR Kit lets you make achievements, badges, and identities portable — a player’s rank in your game becomes a credential they carry to partner games, tournaments, communities, and airdrops.

What You Can Build

  • Achievement badges — Issue on-chain proof that a player reached Diamond rank, completed a raid, or earned a title
  • Cross-game identity — Let players carry their AIR Account identity and credentials into any game in the Moca ecosystem
  • Anti-cheat / bot gating — Verify a player holds a “verified human” or KYC credential before allowing tournament entry
  • Airdrop gating — Reward only players who hold specific credentials (e.g. “played > 100 hrs”, “held NFT for 90 days”)
  • Guild / DAO membership — Issue guild membership credentials that gate Discord roles, in-game benefits, and governance votes

Architecture

{
  "title": "Game Achievement",
  "description": "On-chain proof of a player achievement or rank",
  "properties": {
    "gameId": {
      "type": "string",
      "description": "Unique identifier of the game"
    },
    "achievementId": {
      "type": "string",
      "description": "Unique identifier of the achievement"
    },
    "achievementName": {
      "type": "string",
      "description": "Display name — e.g. 'Diamond Rank Season 3'"
    },
    "tier": {
      "type": "string",
      "enum": ["Bronze", "Silver", "Gold", "Platinum", "Diamond"],
      "description": "Achievement tier"
    },
    "earnedAt": {
      "type": "string",
      "format": "date-time"
    },
    "season": {
      "type": "string",
      "description": "Game season, e.g. 'Season 3'"
    }
  },
  "required": ["gameId", "achievementId", "achievementName", "earnedAt"]
}

Implementation

Step 1 — Issue an achievement credential on rank-up

// game-server.js
const { getPartnerJwt } = require('./lib/jwt');

async function issueAchievement({ playerEmail, achievement }) {
  const token = await getPartnerJwt(playerEmail);

  const res = await fetch('https://api.sandbox.mocachain.org/v1/credentials/issue-on-behalf', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-partner-auth': token,
    },
    body: JSON.stringify({
      issuerDid: process.env.ISSUER_DID,
      credentialId: process.env.ACHIEVEMENT_CREDENTIAL_ID,
      credentialSubject: {
        gameId: process.env.GAME_ID,
        achievementId: achievement.id,
        achievementName: achievement.name,
        tier: achievement.tier,
        earnedAt: new Date().toISOString(),
        season: 'Season 3',
      },
      onDuplicate: 'ignore', // achievements are one-time — don't re-issue
    }),
  });

  if (!res.ok) throw new Error(`Achievement issuance failed: ${res.status}`);
  return res.json();
}

// Wire into your game's rank-up event system
gameEvents.on('player:rank_up', async ({ playerEmail, newRank }) => {
  await issueAchievement({
    playerEmail,
    achievement: {
      id: `rank-${newRank.toLowerCase()}`,
      name: `${newRank} Rank`,
      tier: newRank,
    },
  });
  console.log(`Achievement issued to ${playerEmail}: ${newRank}`);
});

Step 2 — Gate tournament entry with credential check

// tournament-gate.js  (frontend or server)
import { AirService } from '@mocanetwork/airkit';

import { AirService, BUILD_ENV } from "@mocanetwork/airkit";

const airService = new AirService({ partnerId: process.env.PARTNER_ID });
await airService.init({ buildEnv: BUILD_ENV.SANDBOX });

async function canJoinTournament() {
  const result = await airService.verifyCredential({
    programId: process.env.TOURNAMENT_VERIFY_PROGRAM_ID,
    // Verifier program rule: tier === "Diamond" AND gameId === process.env.GAME_ID
  });
  return result.status === 'COMPLIANT';
}

const eligible = await canJoinTournament();
if (!eligible) {
  showMessage('Reach Diamond rank to enter this tournament.');
}

Step 3 — Cross-game SSO with AIR Account

Partner games authenticate using the same AIR Account — no new signup required.
// partner-game.js  (any game integrating AIR Kit)
import { AirService } from '@mocanetwork/airkit';

const airService = new AirService({ partnerId: 'PARTNER_GAME_PARTNER_ID' });
await airService.init({ buildEnv: BUILD_ENV.SANDBOX });

await airService.login(); // player logs in with existing AIR Account
const user = await airService.getUserInfo();
// user.email is the same across all games — portable, unified identity

Step 4 — Bot-resistant airdrop gating

Only distribute tokens to players who provably hold a real game achievement — filtered on-chain.
// airdrop.js  (server-side)
async function getEligiblePlayers(allPlayers) {
  const eligible = [];
  for (const { email } of allPlayers) {
    const token = await getPartnerJwt(email);
    // Use your verifier program to check credential status per player
    // In production, batch this with your own DB query first to reduce API calls
    const res = await fetch(
      `https://api.sandbox.mocachain.org/v1/credentials/status?...`,
      { headers: { 'x-partner-auth': token } }
    );
    const { vcStatus } = await res.json();
    if (vcStatus === 'ONCHAIN') eligible.push(email);
  }
  return eligible;
}

Examples

ZK Age Verification — Issuer

Identity provider app: issues age credential; user proves 18+ without revealing date of birth.

ZK Age Verification — Verifier

iGaming or age-gated app: verifies age via ZK proof and grants access; no PII received.

Next Steps

Issue on Behalf API

Full endpoint reference and error codes.

Issuing Credentials (SDK)

User-initiated issuance for in-game claim flows.

Schema Use Cases

More schema examples including event pass.

AIR for Ticketing & Events

Attendance credentials for live events.