Skip to content

feat(badges): add key NFT badge minting trigger service (#1148) - #1185

Merged
Chucks1093 merged 1 commit into
accesslayerorg:mainfrom
CHKM001:feature/badge-minting-trigger-1148
Oct 21, 2026
Merged

Chucks1093 merged 1 commit into
accesslayerorg:mainfrom
CHKM001:feature/badge-minting-trigger-1148

Conversation

@CHKM001

@CHKM001 CHKM001 commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

PR #1148 feat(badges): add key NFT badge minting trigger service

Summary

Adds a service that automatically awards key NFT badges when a wallet performs
qualifying on-chain activity. All minting is routed through the centralised
Soroban Contract Service, so submission
retries, transaction tracking, and resolution events are applied uniformly —
the service does not re-implement any of that.

The design mirrors the existing campaigns module (server-submitted contract
call + a deduplicating audit log), keeping it consistent with established repo
conventions.

Closes #1148.

Badges and triggers

Badge Qualifying event Rule
FIRST_BUYER first_swap The wallet's first swap.
WHALE_HOLDER whale_swap A single swap ≥ BADGE_WHALE_THRESHOLD_XLM (10,000).
GOVERNANCE_VOTER vote_cast The wallet cast a governance vote.
DIAMOND_HANDS hold_milestone Continuous hold ≥ BADGE_HOLD_MILESTONE_DAYS (365).
TOP_TRADER leaderboard_entry Leaderboard rank ≤ BADGE_TOP_TRADER_MAX_RANK (10).

A single swap can earn both FIRST_BUYER and WHALE_HOLDER.

What's included

  • Trigger service (badge-trigger.service.ts) — evaluateEvent (pure
    qualification rules) + processQualifyingEvent (dispatch to minting).
  • Mint service (badge-mint.service.ts) — mintBadge: deduplication guard
    → mint with retry → audit log, all dependencies injectable.
  • Contract client (badge-mint.client.ts) — submits mint_badge through
    the shared contract service; translates failures into retry-classifiable
    errors.
  • Transaction builder (badge-mint.builder.ts) — builds + signs the
    mint_badge(wallet, badge_type) envelope with the server minter key.
  • BadgeMintLog Prisma model + migration (audit table / dedup backing).
  • Config — badge env vars with sensible defaults; mint_badge added to
    ContractOperation.
  • Docs — docs/badge-minting.md, linked from the
    README.

How it meets the acceptance criteria

  • ✅ First swap → FIRST_BUYER — covered by trigger tests.
  • ✅ Whale threshold swap → WHALE_HOLDER — covered by trigger tests.
  • ✅ Governance vote → GOVERNANCE_VOTER — covered by trigger tests.
  • ✅ Deduplication prevents double-minting — unique (wallet, badgeType) on
    badge_mint_log; mintBadge returns SKIPPED for an already-minted badge
    with no contract call. Covered by tests.
  • ✅ Retry handles transient mint failures — transient outcomes retry up to
    BADGE_MINT_MAX_ATTEMPTS with linear backoff; user-kind failures are
    terminal. Covered by tests.

Deduplication & retry details

  • badge_mint_log keeps one row per (wallet, badgeType). Reprocessing the
    same (or an overlapping) event never re-mints. A row left FAILED by an
    exhausted retry is eligible to be re-attempted on the next event.
  • On top of the contract service's own submission retries, mintBadge retries
    the whole mint on transient outcomes (RPC down / timeout / unconfirmed). Final
    attempt count and last error are persisted on the log row.

Configuration

Minting stays disabled until both BADGE_CONTRACT_ID and
BADGE_MINTER_SECRET are set (fail-safe, consistent with other contract
adapters). Thresholds are overridable:

BADGE_CONTRACT_ID=
BADGE_MINTER_SECRET=
# BADGE_WHALE_THRESHOLD_XLM=10000
# BADGE_HOLD_MILESTONE_DAYS=365
# BADGE_TOP_TRADER_MAX_RANK=10
# BADGE_MINT_MAX_ATTEMPTS=3

Integration point

Indexer / webhook consumers call processQualifyingEvent as activity is
observed:

import { processQualifyingEvent } from './modules/badges';

await processQualifyingEvent({ kind: 'swap', wallet, amount, isFirstSwap, txHash });
await processQualifyingEvent({ kind: 'vote', wallet, proposalId, txHash });

Wiring this entry point into the live swap/governance/indexer emitters is
intentionally left as a follow-up so this PR stays within scope; the service
exposes a clean, tested entry point for it.

Testing

  • pnpm lint — clean
  • pnpm build (tsc) — clean
  • pnpm exec jest src/modules/badges src/modules/contracts — 57 passed
    (18 new badge tests + 39 existing contract tests)

Notes for reviewers

  • The builder uses a sequence-0 placeholder envelope, consistent with the repo's
    other Soroban builders (dividends/claims). If a deployment requires footprint
    simulation before submit, that belongs in the shared contract pipeline rather
    than this module.
  • "Whale" is interpreted here as a single-swap size in XLM. If it should mean
    cumulative holdings instead, the rule in evaluateEvent is the single place to
    change.

Closes #1148

…rg#1148)

Awards key NFT badges automatically from qualifying on-chain activity, routed through the centralised Soroban contract service so retry, transaction tracking, and resolution events apply uniformly.

- Triggers: first swap (FIRST_BUYER), whale swap (WHALE_HOLDER), governance vote (GOVERNANCE_VOTER), 365d hold (DIAMOND_HANDS), top-10 leaderboard (TOP_TRADER)
- Deduplication guard via unique (wallet, badgeType) on badge_mint_log
- Retry on transient mint failures; user-kind failures are terminal
- Every attempt persisted to badge_mint_log for audit
- Config thresholds + docs; mint_badge added to ContractOperation
@drips-wave

drips-wave Bot commented Oct 20, 2026

Copy link
Copy Markdown

@CHKM001 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@Chucks1093
Chucks1093 merged commit 62f1828 into accesslayerorg:main Oct 21, 2026
2 checks passed
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.

Add key NFT badge minting trigger service for automatic badge awards

2 participants