{
  "openapi": "3.1.0",
  "info": {
    "title": "BSCJS Mint Preparation",
    "version": "1.0.0",
    "description": "Keyless, read-only transaction preparation for the fixed official BSCJS deployment on BNB Smart Chain (56). The service checks on-chain state and prepares a limited approval or a simulated Mint. It never signs or broadcasts. Read https://xixi.heyue.app/ai/mint.md before execution; preserve the user's requested shares and spending limits."
  },
  "servers": [
    { "url": "https://xixi.heyue.app" }
  ],
  "security": [],
  "paths": {
    "/api/mint/project": {
      "get": {
        "operationId": "getBscjsMintProject",
        "summary": "Read the official BSCJS project and current on-chain readiness",
        "description": "Returns the fixed project contracts and current state. Check project.mintReady, not merely PROJECT_READY. When oracleReady is false, project.recovery may describe separate unsimulated oracle maintenance; it must not be signed or broadcast automatically. No wallet connection, API key, signature or funds are required to read. State is time-sensitive.",
        "responses": {
          "200": {
            "description": "Project state. Inspect status, code and message before preparing a transaction.",
            "headers": { "Cache-Control": { "schema": { "type": "string" }, "description": "Live responses must not be cached." } },
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProjectResponse" } } }
          },
          "default": { "$ref": "#/components/responses/ServiceError" }
        }
      }
    },
    "/api/mint/prepare": {
      "get": {
        "operationId": "prepareBscjsMintTransaction",
        "summary": "Check the connected wallet and prepare its next approval or Mint transaction",
        "description": "Read-only. Use the actual connected account on chain 56, requested shares and payment asset. USDT payment accepts maxUsdt only; BNB payment accepts maxBnb only. Mixed asset budgets return INVALID_BUDGET. The BNB entry cannot enforce a USDT-denominated ceiling; choose USDT for that requirement. Never increase budgets automatically. If none was specified, show the live quote for wallet confirmation. After confirmed approval, prepare again with the same intent and budget. Before signing, both latest and pending wallet nonce must equal transaction.nonce. Stop on PENDING_TRANSACTION or NONCE_CHANGED; never change nonce and resend blindly. No endpoint submits a transaction.",
        "parameters": [
          {
            "name": "account", "in": "query", "required": true,
            "description": "Actual connected wallet; do not substitute a sample, deployer or previous successful minter.",
            "schema": { "$ref": "#/components/schemas/Address" }
          },
          {
            "name": "shares", "in": "query", "required": true,
            "description": "User-requested shares. Also limited by identity quota and remaining issuance.",
            "schema": { "type": "integer", "minimum": 1, "maximum": 10 }, "example": 1
          },
          {
            "name": "payment", "in": "query", "required": true,
            "description": "Payment asset. BNB still calls mintWithBnb; a plain native transfer does not mint.",
            "schema": { "type": "string", "enum": ["USDT", "BNB"] }, "example": "USDT"
          },
          {
            "name": "referrer", "in": "query", "required": false,
            "description": "Optional user-specified inviter wallet. Omit when absent; no inviter is inserted by the service.",
            "schema": { "$ref": "#/components/schemas/Address" }
          },
          {
            "name": "maxUsdt", "in": "query", "required": false,
            "description": "USDT payment only. User-approved maximum USDT purchase cost, human decimal string, at most 18 decimals. Gas is additional. A quote above this budget must stop. Rejected with INVALID_BUDGET for BNB payment; the deployed BNB entry cannot enforce a USDT cost ceiling.",
            "schema": { "$ref": "#/components/schemas/DecimalAmount" }, "example": "1"
          },
          {
            "name": "maxBnb", "in": "query", "required": false,
            "description": "BNB payment only. User-approved maximum BNB payment value, human decimal string, at most 18 decimals. Gas is additional. Rejected with INVALID_BUDGET for USDT payment. Cannot enforce a USDT-denominated ceiling.",
            "schema": { "$ref": "#/components/schemas/DecimalAmount" }, "example": "0.002"
          }
        ],
        "responses": {
          "200": {
            "description": "Preparation result. Submit only a successful, reviewed transaction. action=approve means approval only, not Mint success; after its receipt confirms successfully, prepare again. action=mint is a simulated Mint awaiting the user's wallet signature.",
            "headers": { "Cache-Control": { "schema": { "type": "string" }, "description": "Do not cache quotes or transaction calldata." } },
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PreparationResponse" } } }
          },
          "default": { "$ref": "#/components/responses/ServiceError" }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Address": { "type": "string", "pattern": "^0x[0-9a-fA-F]{40}$" },
      "HexQuantity": { "type": "string", "pattern": "^0x(?:0|[1-9a-fA-F][0-9a-fA-F]*)$" },
      "DecimalAmount": { "type": "string", "pattern": "^(?:0|[1-9][0-9]*)(?:\\.[0-9]{1,18})?$" },
      "RawTransaction": {
        "type": "object",
        "description": "Unsigned EVM wallet request, bound to observed nonce. Before signing, both latest and pending wallet counts must equal this nonce; never replace it with a new nonce to resend a purchase. Check official targets, actual account, payment, budget and validity. Quantities use RPC hex. No signature or private key is returned.",
        "required": ["from", "to", "data", "value", "chainId", "nonce"],
        "properties": {
          "from": { "$ref": "#/components/schemas/Address" },
          "to": { "$ref": "#/components/schemas/Address" },
          "data": { "type": "string", "pattern": "^0x[0-9a-fA-F]*$" },
          "value": { "$ref": "#/components/schemas/HexQuantity", "description": "Native BNB amount in wei. Must be 0x0 for USDT approve and USDT mint." },
          "chainId": { "type": "string", "const": "0x38", "description": "BNB Smart Chain mainnet only." },
          "nonce": { "$ref": "#/components/schemas/HexQuantity" },
          "gas": { "$ref": "#/components/schemas/HexQuantity" },
          "gasPrice": { "$ref": "#/components/schemas/HexQuantity" }
        },
        "additionalProperties": true
      },
      "ProjectResponse": {
        "type": "object",
        "properties": {
          "status": { "type": "string", "const": "ok" },
          "code": { "type": "string", "const": "PROJECT_READY" },
          "message": { "type": "string" },
          "project": { "$ref": "#/components/schemas/ProjectState" },
          "asOf": { "$ref": "#/components/schemas/ChainSnapshot" }
        },
        "additionalProperties": true
      },
      "PreparationResponse": {
        "type": "object",
        "properties": {
          "status": { "type": "string", "const": "ready", "description": "A successful preparation is not a submitted or completed Mint." },
          "code": { "type": "string", "enum": ["APPROVAL_REQUIRED", "MINT_READY"] },
          "message": { "type": "string" },
          "action": { "type": "string", "enum": ["approve", "mint"] },
          "method": { "type": "string", "enum": ["approve", "mint", "mintWithBnb"] },
          "payment": { "type": "string", "enum": ["USDT", "BNB"] },
          "shares": { "type": "integer", "minimum": 1, "maximum": 10 },
          "referrer": { "$ref": "#/components/schemas/Address" },
          "project": { "$ref": "#/components/schemas/ProjectState" },
          "asOf": { "$ref": "#/components/schemas/ChainSnapshot" },
          "quote": { "$ref": "#/components/schemas/Quote" },
          "account": { "$ref": "#/components/schemas/WalletState" },
          "transaction": { "$ref": "#/components/schemas/RawTransaction" },
          "preconditions": { "$ref": "#/components/schemas/Preconditions" },
          "nextAction": { "type": "string", "description": "After approval, reprepare with the same account, shares, payment, referrer and user budgets." }
        },
        "additionalProperties": true
      },
      "ProjectState": {
        "type": "object",
        "description": "Official linked deployment and current chain state. A successful project read is not necessarily a ready Mint.",
        "properties": {
          "chainId": { "type": "integer", "const": 56 },
          "paused": { "type": "boolean" },
          "mintReady": { "type": "boolean", "description": "True only when Mint is not paused and the oracle is ready." },
          "oracleReady": { "type": "boolean" },
          "oracleUpdatedAt": { "type": "integer" },
          "oracleObservationAt": { "type": "integer" },
          "oracleTwapWindow": { "type": "integer" },
          "oracleMaxAge": { "type": "integer" },
          "recovery": { "$ref": "#/components/schemas/OracleRecovery" }
        },
        "additionalProperties": true
      },
      "Preconditions": {
        "type": "object",
        "description": "Recheck chain, actual wallet, nonce and expiry immediately before signing. On nonce changes, resolve existing transactions before preparing again; do not blindly resend.",
        "properties": {
          "chainId": { "type": "integer", "const": 56 },
          "account": { "$ref": "#/components/schemas/Address" },
          "nonce": { "description": "Observed nonce bound to the raw transaction. Compare numeric values of latest, pending and transaction.nonce." },
          "until": { "type": "integer" },
          "onNonceChanged": { "type": "string", "const": "reprepare" },
          "retrySignedTransactionOnly": { "type": "boolean", "const": true }
        },
        "additionalProperties": true
      },
      "OracleRecovery": {
        "type": "object",
        "description": "Separate unsimulated maintenance information, not a Mint transaction or authorization to send. Review and simulate maintenance separately, get wallet confirmation, honor the live waiting period, then reread readiness. An old observation can require two updates separated by the TWAP window; an update does not guarantee reserve/deviation/Chainlink checks pass.",
        "properties": {
          "action": { "type": "string", "const": "updateOracle" },
          "permissionless": { "type": "boolean", "const": true },
          "requiresWalletConfirmation": { "type": "boolean", "const": true },
          "automatic": { "type": "boolean", "const": false },
          "simulationPassed": { "type": "boolean", "const": false },
          "transactionTemplate": {
            "type": "object",
            "description": "Unsimulated updateOracle() template; never automatically send as if it were a prepared Mint.",
            "properties": {
              "to": { "$ref": "#/components/schemas/Address" },
              "data": { "type": "string" },
              "value": { "type": "string", "const": "0x0" },
              "chainId": { "type": "string", "const": "0x38" }
            },
            "additionalProperties": true
          },
          "observationAt": { "type": "integer" },
          "twapWindow": { "type": "integer" },
          "oracleMaxAge": { "type": "integer" },
          "oracleUpdatedAt": { "type": "integer" },
          "observedAt": { "type": "integer" },
          "waitSeconds": { "type": "integer", "minimum": 0 },
          "mayNeedSecondUpdate": { "type": "boolean" },
          "expectedUpdateTransactions": { "type": "integer", "enum": [1, 2] },
          "message": { "type": "string" },
          "warning": { "type": "string" }
        },
        "additionalProperties": true
      },
      "ChainSnapshot": {
        "type": "object",
        "description": "The on-chain snapshot used for the response. Refresh after approval, expiry or a changed intent.",
        "properties": {
          "blockNumber": { "type": "integer" },
          "blockHash": { "type": "string" },
          "timestamp": { "type": "integer", "description": "Unix seconds from the chain." }
        },
        "additionalProperties": true
      },
      "WalletState": {
        "type": "object",
        "description": "Actual wallet and identity/quota checks. The returned address must match the connected account.",
        "properties": {
          "address": { "$ref": "#/components/schemas/Address" },
          "agentHandle": { "type": "string" },
          "agentId": { "type": "string" },
          "quota": { "description": "Total identity mint quota." },
          "used": { "description": "Shares already used by the identity." },
          "remaining": { "description": "Remaining identity mint quota." },
          "usdtBalance": { "type": "string" },
          "bnbBalance": { "type": "string" },
          "usdtAllowance": { "type": "string" }
        },
        "additionalProperties": true
      },
      "Quote": {
        "type": "object",
        "description": "Live purchase cost and payment quote. Preserve user budgets. Exact cost/value is used for approve and Mint limits; no extra service fee buffer is added. Gas is separate.",
        "properties": {
          "costUsdt": { "type": "string", "description": "Human decimal USDT purchase cost." },
          "costUsdtRaw": { "type": "string", "description": "USDT raw amount, 18 decimals." },
          "minQuoteOut": { "type": "string", "description": "Raw BNCB minimum for the internal liquidity conversion, not the BSCJS receipt amount." },
          "fairQuoteOut": { "type": "string" },
          "minQuoteOutAsset": { "description": "Identifies the quote asset used by minQuoteOut." },
          "until": { "type": "integer", "description": "On-chain deadline in Unix seconds." },
          "expiresAt": { "type": "string", "format": "date-time" },
          "validForSeconds": { "type": "integer", "const": 180 },
          "gasPaidSeparately": { "type": "boolean", "const": true },
          "gasReserveBnb": { "type": "string" },
          "approvalAmountUsdt": { "type": "string", "description": "Limited approval amount, human USDT decimal. Approval is not Mint success." },
          "spender": { "$ref": "#/components/schemas/Address" },
          "bnbValue": { "type": "string", "description": "Human BNB transaction value for mintWithBnb." },
          "bnbValueRaw": { "type": "string", "description": "BNB transaction value in wei." }
        },
        "additionalProperties": true
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "status": { "type": "string", "const": "error" },
          "code": { "type": "string", "description": "Machine-readable reason, including INVALID_BUDGET, BUDGET_EXCEEDED, PENDING_TRANSACTION, NONCE_CHANGED or ORACLE_NOT_READY." },
          "message": { "type": "string", "description": "User-readable reason. Stop, explain the cause and do not broadcast an unprepared Mint." },
          "transaction": { "type": "null" },
          "recovery": { "$ref": "#/components/schemas/OracleRecovery" }
        },
        "additionalProperties": true
      }
    },
    "responses": {
      "ServiceError": {
        "description": "Invalid input, budget/state/identity/allowance/balance/simulation problem, rate limit or temporarily unavailable chain service. Inspect code and message. Do not fall back to plain transfer or guessed calldata.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
      }
    }
  }
}
