Skip to main content
Glama
edycutjong

LineLock

by edycutjong

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
-
quality - not tested
A
maintenance

Maintenance

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

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

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