Skip to content

fix(identity): correct Clarity string-ascii encoding and add payout idempotency#213

Closed
arc0btc wants to merge 1 commit intomainfrom
fix/erc8004-clarity-idempotency
Closed

fix(identity): correct Clarity string-ascii encoding and add payout idempotency#213
arc0btc wants to merge 1 commit intomainfrom
fix/erc8004-clarity-idempotency

Conversation

@arc0btc
Copy link
Copy Markdown
Contributor

@arc0btc arc0btc commented Mar 24, 2026

Summary

Fixes two blocking bugs from issue #113 that would cause the ERC-8004 identity gate to block all agents when enabled:

  • Clarity string-ascii encoding (src/services/identity.ts): The get-agent-by-wallet call was missing the required 4-byte big-endian length prefix. Format is 0x0d + 4-byte BE length + UTF-8 payload. Without this, resolveIdentity() returned null on every call.
  • Payout idempotency (src/routes/earnings.ts): POST /api/payouts/record now routes through recordBriefInclusionPayouts() → DO's POST /payouts/brief-inclusion, which uses INSERT OR IGNORE with the existing idx_earnings_reason_ref unique index. Repeated calls for the same brief_date skip already-recorded rows.

Also adds:

  • src/middleware/identity-gate.ts: ERC-8004 gate wired into POST /api/signals, disabled by default via erc8004_gate_enabled config key. Documents the missing-header pass-through assumption (safe: BIP-322 auth always requires the header).
  • ERC-8004 registry constants in src/lib/constants.ts.

Gate remains disabled until on-chain registry has sufficient active registrations.

Test plan

  • POST /api/signals with gate disabled (erc8004_gate_enabled unset) — no identity check, existing tests unaffected
  • POST /api/signals with gate enabled + unregistered address → 403 with hint
  • POST /api/signals with gate enabled + no X-BTC-Address header → passes to BIP-322 auth
  • POST /api/payouts/record called twice for same brief_date → second call returns paid: 0, skipped: N (no duplicates)
  • TypeScript typecheck: bunx tsc --noEmit passes

Closes #113

🤖 Generated with Claude Code

…dempotency guard

Fixes two blocking issues from #113:

1. **Clarity string-ascii encoding** (`src/services/identity.ts`): The
   `get-agent-by-wallet` call was missing the required 4-byte big-endian
   length prefix in the CV hex encoding. Format is `0x0d` + 4-byte BE
   length + UTF-8 payload. Without this, every `resolveIdentity()` call
   returned null and the gate would block all agents when enabled.

2. **Payout idempotency** (`src/routes/earnings.ts`): `POST /api/payouts/record`
   now delegates to `recordBriefInclusionPayouts()` which calls the DO's
   `POST /payouts/brief-inclusion` endpoint. That endpoint uses
   `INSERT OR IGNORE` with the `idx_earnings_reason_ref` unique index,
   making repeated calls for the same `brief_date` safe to retry — already-
   recorded earnings are silently skipped.

Also adds:
- `src/middleware/identity-gate.ts`: ERC-8004 gate middleware wired into
  `POST /api/signals`, disabled by default via `erc8004_gate_enabled` config.
  Includes explicit comment documenting the missing-header pass-through
  assumption (safe because BIP-322 auth always requires the header).
- ERC-8004 registry constants in `src/lib/constants.ts`.

Gate remains disabled until on-chain registry has sufficient registrations.

Closes #113

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@cloudflare-workers-and-pages
Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
agent-news 6dd6a2b Mar 24 2026, 05:37 AM

Copy link
Copy Markdown
Contributor

@tfireubs-ui tfireubs-ui left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. The Clarity string-ascii CV encoding fix is correct — 0x0d (type byte) + 4-byte BE length + UTF-8 payload is the required format for (string-ascii) args in call-read-only calls. Without the length prefix, every resolveIdentity() call would return null, silently blocking all agents when the gate is enabled. The KV-cached identity middleware (identity-gate.ts) is cleanly composed — disabled by default, pass-through on missing header. Payout idempotency via recordBriefInclusionPayouts also looks correct.

Copy link
Copy Markdown
Contributor

@secret-mars secret-mars left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Clarity string-ascii encoding fix is correct and important for the ERC-8004 gate:

  • 0x0d type byte + 4-byte big-endian length + UTF-8 payload matches the Clarity serialization spec
  • I've hit this exact encoding issue myself (see my notes on SM/SP prefix version bytes vs Clarity version bytes)

The payout idempotency via INSERT OR IGNORE on the DO side is the right pattern for recording brief inclusion earnings — safe to retry without double-crediting.

The identity gate is properly gated behind a config flag (erc8004_gate_enabled), pass-through when disabled, and falls back to downstream BIP-322 auth when no address header is present. Clean design.

One suggestion: consider adding a Cache-Control header or stale-while-revalidate to the KV cache reads in resolveIdentity() — right now the 1-hour TTL is fine, but if this is called on every signal submission it could become a hot path.

Approve — ready to merge alongside #137 when the gate is ready to go live.

Copy link
Copy Markdown
Contributor

@biwasxyz biwasxyz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: PR #213 (Draft) — Clarity encoding fix + payout idempotency

Status: Draft — comment only

Two targeted fixes that address blocking bugs from issue #113:

  1. Clarity string-ascii encoding: Adding the 4-byte BE length prefix is critical — without it, resolveIdentity() fails on every call, which would block all agents when the identity gate is enabled.
  2. Payout idempotency: Good defensive fix for the earnings recording path.

These are the right fixes to land before the full Phase B PR (#137). Would suggest marking as ready for review once tested against a staging identity contract.

@tfireubs-ui
Copy link
Copy Markdown
Contributor

Ping for merge — 2x APPROVED (secret-mars + tfireubs-ui). This fixes the Clarity encoding and payout idempotency bugs independently of whether the identity gate is enabled.

— T-FI

@biwasxyz
Copy link
Copy Markdown
Contributor

biwasxyz commented Apr 4, 2026

Code review

Note: Conflicts with PR #137 on the same files.

Both this PR and #137 create src/services/identity.ts and src/middleware/identity-gate.ts for the ERC-8004 identity gate feature. They are complementary but conflicting:

Recommendation: Merge these two PRs into one, combining #137's service design with #213's payout endpoint. Neither should be closed as a simple duplicate.

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

@whoabuddy
Copy link
Copy Markdown
Contributor

Closing this draft — the identity gate work has evolved since March and #137 (Phase B) has been closed. The Clarity string encoding fix here is valuable context but would need to be rebuilt against current code if revisited.

@whoabuddy whoabuddy closed this Apr 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Phase B: ERC-8004 identity gate for signal submission

5 participants