Skip to main content
Glama
edycutjong

LineLock

Live Site Pitch Deck Live API Demo Video HackQuest · Injective Global Cup 2026

Node.js TypeScript Express Next.js Vitest Injective EVM x402 License: MIT CI


Every World Cup tipster shows you screenshots. LineLock shows you receipts.

A pay-per-pick World Cup edge API on Injective EVM where the x402 USDC receipt IS the pre-kickoff timestamp, plus a free public ledger that CLV-scores every settled pick — losses included.

The one flow, with depth: pay x402 → edge + conviction ladder returned → receipt = pre-kickoff proof → match settles → CLV scored → public ledger row

Buy the next pick for 5 cents. Audit every past pick for free.

What's built & green today (no funds needed): 70 passing tests, a live HTTP 402 quote from the real @injectivelabs/x402 middleware, a 5-screen Next.js ledger site, an independent audit CLI, and an on-chain Merkle anchor contract. And as of 2026-07-18 the live API has real paid mainnet receipts — four settled x402 sales, first one 0xc4327f40…c41a1beb — and the code still refuses to fabricate any receipt it didn't earn.

🚀 Quickstart (no funds needed for the 402 proof)

npm install
npm run settle           # build the CLV ledger from fixtures/picks.csv (24 rows)
npm test                 # 70 tests: CLV · edge · ladder · hash · similar · invariants I1–I5 · settle · 402 handshake
npm run api &            # boot the Express API on :8402 (x402 gate ON)
curl -i -X POST http://localhost:8402/api/edge   # → HTTP 402 + a real x402 quote (no funds)
npm run buyer            # parse that quote with the first-party client
kill %1                  # stop the gated API (frees :8402), then:
npm run api -- --demo &  # same API, x402 gate OFF → the paid 200 payload without funds
curl -s -X POST http://localhost:8402/api/edge   # → 200: edge + ladder + similar_settled (receipt:null)
npm run audit -- --all   # independently re-audit the whole ledger → "LEDGER PASSES"
# the ledger site:
cd web && npm install && npm run dev  # → http://localhost:3402

The 402 handshake — real, from the real middleware, zero funds

$ curl -i -X POST http://localhost:8402/api/edge

HTTP/1.1 402 Payment Required
PAYMENT-REQUIRED: eyJ4NDAyVmVyc2lvbiI6MiwiZXJyb3IiOiJQQVlNRU5U...   (base64 mirror of the body)
Content-Type: application/json

{
  "x402Version": 2,
  "error": "PAYMENT-SIGNATURE header is required",
  "resource": { "url": "http://localhost:8402/api/edge", "description": "LineLock — one World Cup knockout edge…" },
  "accepts": [{
    "scheme": "exact",
    "network": "eip155:1776",
    "amount": "50000",
    "payTo": "0x45078eD96C2bB171009A47a57aF5C085Bf4fD0e3",
    "maxTimeoutSeconds": 120,
    "asset": "0xa00C59fF5a080D2b954d0c75e46E22a0c371235a",
    "extra": { "name": "USDC", "version": "2", "assetTransferMethod": "eip3009" }
  }]
}

That quote is produced by the real @injectivelabs/x402 middleware, needs no wallet funds, and parses cleanly with the package's own parsePaymentRequired() (see test/handshake.test.ts).

The actual paid payload — witnessed without funds (--demo)

The same POST /api/edge returns the real edge payload once you run the server with the gate disabled — no funds, and it labels itself as unpaid (receipt: null, note: "Demo/unsettled response"):

$ npm run api -- --demo &   # same server, x402 gate OFF
$ curl -s -X POST http://localhost:8402/api/edge
{
  "side_label": "France to advance",
  "model_prob": 0.58, "edge_pct": 0.0922, "recommended_tier": 2,
  "ladder": [ /* 4 conviction rungs */ ],
  "pick_hash": "f7ad4164…588c22",
  "similar_settled": [
    { "fixture": "FRA vs AUS", "edge_pct": 0.0922, "result": "win", "clv_pct":  0.0904 },
    { "fixture": "ENG vs SUI", "edge_pct": 0.0895, "result": "win", "clv_pct":  0.0756 },
    { "fixture": "CRO vs CAN", "edge_pct": 0.0852, "result": "win", "clv_pct": -0.0612 }
  ],
  "receipt": null,
  "note": "Demo/unsettled response (no on-chain receipt bound)."
}

similar_settled is drawn from LineLock's own settled ledger, losses included (I3) — note CRO vs CAN: a win that lost CLV. The system publishes its own bad prices. The receipt field is where the USDC tx lands; its block time is the pre-kickoff proof (I1). See DEMO.md for the full ~25-second "402 → --demo 200 → audit --all" magic-moment beat.

Related MCP server: hyperd-mcp

🧑‍⚖️ Notes for judges

  • Keyless — nothing to sign up for. No account, no login, no API key to witness the paid gate. curl -i -X POST :8402/api/edge returns a real HTTP 402 quote. The data-API responses are cached in the repo, so settle, the ledger, the audit, and the site all run with an empty .env.local.

  • The 402 proof runs with zero funds. The quote is built from config by the real middleware — it needs no wallet balance. Only settling a real payment needs gas, and scripts/paid-call-smoke.ts does a balance preflight and refuses to fabricate a receipt when the wallet is unfunded (it exits, it never fakes one).

  • Witness the paid 200 without funds, then recompute the ledger. npm run api -- --demo disables the gate so POST /api/edge returns the actual payload a buyer gets — edge, conviction ladder, and similar_settled (losses included) — self-labeled receipt: null. Then npm run audit -- --all re-hashes every pick and recomputes every Merkle root → "LEDGER PASSES". That ~25-second beat (402 → --demo 200 → audit) is the whole product, witnessed with zero funds — see DEMO.md.

  • Reproduce the whole core in <5 minutes:

    git clone https://github.com/edycutjong/linelock && cd linelock
    npm install
    npm test                                        # 70 green: CLV · invariants I1–I5 · 402 handshake
    npm run settle && npm run audit -- --all        # build the ledger, then independently re-audit it
    npm run api & sleep 1                           # API on :8402
    curl -i -X POST http://localhost:8402/api/edge  # → HTTP 402 + a real x402 quote, zero funds
  • Real on-chain paid receipts now exist (2026-07-18). The live API (api.linelock.edycu.dev) has settled four real x402 sales at 0.05 USDC each on Injective EVM mainnet — the first (0xc4327f40…c41a1beb) is now a real is_placeholder: false row on the live public ledger, and two of the four were bought autonomously by other AI agents (CupOracle's wc_edge and AgentDuel's RED duelist).

  • What is honestly pending (never faked): just the LedgerAnchor mainnet deploy — funds are now available but the contract is not yet deployed. The live site, API, demo and pitch videos are all shipped. Status is tracked in STATUS.md.

🏗️ Architecture

flowchart LR
  subgraph BUYER["Buyer — human or AI agent"]
    SK["worldcup-linelock<br/>Agent Skill"]
    MCP["Injective MCP<br/>balances · USDC info · CCTP"]
    CL["createInjectiveClient().fetch()"]
    SK --> MCP
    SK --> CL
  end

  subgraph API["Express API on :8402"]
    X402["injectivePaymentMiddleware<br/>x402 gate · 0.05 USDC"]
    EDGE["edgeHandler<br/>edge + ladder + pick_hash"]
    FREE["free routes<br/>ledger · receipt · anchor · verify"]
    X402 --> EDGE --> FREE
  end

  subgraph CHAIN["Injective EVM · mainnet"]
    USDC[("USDC 0xa00C…235a")]
    ANCHOR["LedgerAnchor.sol<br/>daily Merkle root"]
  end

  CL -->|"POST /api/edge → 402 → PAYMENT-SIGNATURE"| X402
  X402 -->|"settle 'before'"| USDC
  FREE --> WEB["Next.js site<br/>5 screens"]
  EDGE -.->|"pick_hash = leaf"| ANCHOR
  BASE[("Base USDC")] -->|"CCTP burn → Iris → mint"| USDC

The whole system exists to make one flow undeniable: pay → the receipt is the pre-kickoff proof → the match settles → CLV scores the pick → it lands on the public ledger. Full request-path table and the exact x402 wiring are in ARCHITECTURE.md.


🛠️ Injective technologies used

Five honestly-named Injective surfaces, each load-bearing:

1. x402 — @injectivelabs/x402

  • Server: injectivePaymentMiddleware(routes, options) gates POST /api/edge at 0.05 USDC (50000 units) on mainnet USDC 0xa00C59fF5a080D2b954d0c75e46E22a0c371235a, network eip155:1776. Code: api/middleware.ts, wired in api/server.ts.

  • Client: createInjectiveClient({ privateKey }).fetch(url) auto-handles the 402 and signs an EIP-3009 authorization; parsePaymentRequired / parsePaymentResponseHeader read the quote + receipt. Code: scripts/buyer.ts, scripts/paid-call-smoke.ts.

  • Why it's the whole product: the payment tx's block time < kickoff is the notarization — the receipt is the pre-kickoff proof, bound to pick_hash in the same ledger row (invariant I1).

2. MCP Server — InjectiveLabs/mcp-server (buyer side, driven by the Skill)

  • account_balances (pre-purchase USDC check) · usdc_native_info (pin the asset address + decimals, never hardcode from docs) · address_normalize (inj1…0x…) · CCTP tool family. Documented in skills/worldcup-linelock/SKILL.md. Hosted Documentation MCP (SearchInjectiveDocs) used at build time to pin the surface.

3. Agent Skills — shipped worldcup-linelock

  • An installable markdown skill that teaches any harness to be a buyer: balance → pay → validate hash → audit. npx skills add https://github.com/edycutjong/linelock --skill worldcup-linelock. It is the distribution channel — any agent becomes a customer with no API key. Code: skills/worldcup-linelock/SKILL.md.

4. USDC CCTP V2 — cross-chain buyer funding

  • Base (domain 6) burn → Iris attestation (iris-api.circle.com) → cctp_mint on Injective. TokenMessengerV2 0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d.

  • Executed for real (2026-07-18): burn on Base 0x66ce1116e75f780e60259e394304e86f7565b52276f9d49e4c7fc66209427b37 → Iris attestation → mint on Injective 0xd757a98d…3c4464ac (2 USDC). Details in STATUS.md and DEMO.md.

5. Injective EVM smart contract — LedgerAnchor.sol

  • ~40-line write-once daily Merkle checkpoint of pick hashes (event AnchorPosted(day, root, count)), built (deploy-ready) via the official injective-evm-developer Agent Skill. Receipts prove when each pick existed; the anchor proves the ledger DB was never rewritten (invariant I5). Code: contracts/LedgerAnchor.sol, deploy notes contracts/DEPLOY.md.

x402: the real surface (spec correction). The sibling ARCHITECTURE.md sketch shows a flat injectivePaymentMiddleware({ endpoint, network, asset, amount }). The shipped dist/*.d.ts types (verified 2026-07-12) use a routes map: injectivePaymentMiddleware(routes, options) where routes["POST /api/edge"] = { description, accepts: [{ network, asset, amount, payTo }] } and options = { facilitator: { privateKey }, settlementPolicy }. We build against the shipped types; the flat sketch is outdated. Headers are v2: 402 carries PAYMENT-REQUIRED, the client sends PAYMENT-SIGNATURE, and the 200 carries PAYMENT-RESPONSE with the settlement {success, transaction, network, payer} — the receipt tx arrives in-band.


📈 The differentiator: CLV (Closing Line Value)

Anyone can post a winning screenshot. Pro syndicates track CLV — did you get a better price than the market's closing line? Positive CLV across a sample = real edge, independent of win/loss variance.

implied(odds)   = 1 / odds
clv_prob_points = implied(closing) − implied(entry)  # probability points
clv_pct         = (implied(closing) − implied(entry)) / implied(entry)
                = entry_odds / closing_odds − 1      # the RELATIVE headline (matches the UI)

For a backed side, positive = the line moved our way (we bet at longer odds than it closed). Example: entry 1.85 → close 1.72 = +7.6% CLV. Code + tests: engine/clv.ts, test/clv.test.ts. The ledger deliberately keeps wins with negative CLV and losses with positive CLV — that divergence is the whole point.

🔒 Data invariants (these ARE the product)

#

Invariant

Enforced in

I1

ledger row valid only if receipt_block_time < kickoff_utc (UI shows the delta)

scripts/settle.ts assertPreKickoff, api/routes.ts

I2

pick_hash = sha256(canonical pick JSON); served pick re-hashes to it

engine/hash.ts, in-browser re-verify on the pick page

I3

every paid pick enters the ledger (losses included); rows == receipts

db/ledger.ts receiptCount

I4

closing odds = last odds snapshot ≤ kickoff; raw snapshots kept

data/odds.ts closingLineFromSnapshots

I5

each row is a leaf under a daily Merkle root anchored on-chain

engine/merkle.ts, contracts/LedgerAnchor.sol

All five are covered by the 70-test suite (npm test).

📒 Ledger at a glance (seed)

24 settled rows · record 15–9 · ROI +24.2% · avg CLV +2.9% · beat-close 67% · 11 banker / 3 value / 10 probe · 158 calls sold.

⚠️ The seed rows are placeholder data (is_placeholder: true, synthetic receipt hashes) used to exercise the engine — labeled everywhere and in STATUS.md. As of 2026-07-18 the live public ledger also carries its first real paid receipt (is_placeholder: false, fixture "SF: FRA vs ESP", tx 0xc4327f40…c41a1beb) — 24 of the 25 live rows remain labeled seed/placeholder.

⚡ Bench (N=20, in-process)

metric

value

402-quote p50

~0.5 ms

402-quote p95

~2.6 ms

facilitator-confirm

N/A — not yet re-benched (real settlements exist as of 2026-07-18)

total paid

N/A — not yet re-benched

npm run bench regenerates fixtures/bench.json.

📁 Repo layout

api/        Express app + injectivePaymentMiddleware (x402) + free ledger/receipt/anchor/verify routes
engine/     edge · CLV · conviction ladder · pick-commit hash · Merkle (pure, fully tested)
data/       football-data.org + the-odds-api clients (cached, quota-safe) + closing-line logic (I4)
db/         SQLite schema + ledger repository (better-sqlite3)
web/        Next.js 14 ledger site — 5 screens (ledger · pay/reveal · audit · agent · verify)
skills/worldcup-linelock/SKILL.md    installable Agent Skill (balance → pay → validate → audit)
contracts/  LedgerAnchor.sol + DEPLOY.md (built via injective-evm-developer skill)
scripts/    settle · bench · audit · buyer · anchor · paid-call-smoke · check_submission_readiness
fixtures/   picks.csv (seed) · ledger-state.json · anchors.json · bench.json · odds-snapshots/
test/       70 tests (vitest)
e2e/        Playwright specs (demo-mode · ledger-flow · responsive) — run in demo mode

🧪 Testing, CI & engineering harness

A 6-stage GitHub Actions pipeline gates every push and PR: Quality → Security → Build → E2E → Performance → Deploy. Concurrency-guarded, Node 20/22/24 matrix on main.

# ── Core quality (root: engine + API) ───────────
npm run typecheck     # tsc --noEmit (strict)
npm test              # 70 vitest tests (edge · ladder · CLV · hash · similar · settle · I1–I5 · 402)
npm run test:coverage # v8 coverage (engine ~98%)
npm run ci            # typecheck + coverage gate

# ── The web ledger site (Next.js 14 on :3402) ───
npm run web:build     # production build
npm run web:start     # serve it

# ── Advanced testing ────────────────────────────
npm run e2e           # Playwright E2E — 3 specs, demo mode (no keys/wallet/API)
npm run e2e:ui        # Playwright interactive
npm run lighthouse    # Lighthouse CI (build web/ first)

Layer

Tool

Status

Code quality

TypeScript strict (tsc --noEmit)

Unit testing

Vitest — 70 tests, engine ~98% coverage

E2E testing

Playwright — 3 specs (demo-mode · ledger-flow · responsive)

Security (SAST)

CodeQL (javascript-typescript)

Security (SCA)

Dependabot (npm root + web + actions) + npm audit

Secret scanning

TruffleHog (verified-only)

Performance

Lighthouse CI (web/ on :3402)

Community health

CoC · Contributing · Security · Issue/PR templates

The web app runs the E2E and Lighthouse gates in demo mode off the committed fixtures/ledger-state.json — no wallet, no API server, and no secrets required, so the whole harness is green in CI without any funded key.

🧾 Honesty & funding status

The ops wallet is now funded (CCTP Base→Injective mint executed 2026-07-18) and real mainnet settlements have happened: the live API has settled four paid x402 calls at 0.05 USDC each on Injective EVM mainnet (payer 0x95DdED219bD3d763A184eB4187056b9F238aAaA2 → payTo 0x45078eD96C2bB171009A47a57aF5C085Bf4fD0e3), first receipt 0xc4327f40…c41a1beb — two of the four bought autonomously by other AI agents. Still pending: the LedgerAnchor mainnet deploy; 24 of the 25 live ledger rows remain labeled seed/placeholder — see STATUS.md. No on-chain receipt or mainnet tx is ever fabricated.

📄 License

MIT © 2026 Edy Cu.

🤝 Contributing & community

Issues and PRs welcome. See CONTRIBUTING, CODE_OF_CONDUCT, and the security policy (report vulnerabilities privately to edy.cu@live.com). Bug/feature issue templates and a PR checklist live under .github/.

🙏 Acknowledgments

Built for the Injective Global Cup hackathon. Thanks to Injective for the x402, MCP server, Agent Skills, USDC CCTP, and EVM developer tooling. Football data by Football-Data.org; odds by the-odds-api.com.


Football data provided by the Football-Data.org API. Odds via the-odds-api.com. Brand: ninja-cyberpunk — hot pink #FF007A + proof gold #FFB300 on #0A0A12.

A
license - permissive license
Not graded
quality - not tested
A
maintenance

Maintenance

Maintainers
Response time
0dRelease cycle
6Releases (12mo)
Commit activity

Related MCP Servers

View all related MCP servers

Related MCP Connectors

  • Paid-verified directory of x402 APIs — we pay endpoints real USDC and confirm settlement on-chain.

  • 32 paid x402 endpoints for crypto, Zora & on-chain analysis. 10 MCP tools. USDC on Base.

  • 914 paid market-data & token-safety endpoints — keyless x402 USDC: Base, Solana, XRPL, +8 chains

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/edycutjong/linelock'

If you have feedback or need assistance with the MCP directory API, please join our Discord server