diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..f1c00c0 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,53 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + links: + name: Check Links + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Check links in markdown files + uses: lycheeverse/lychee-action@v2 + with: + args: > + --no-progress + --exclude-loopback + --accept 200,403,429 + --timeout 15 + --retry-wait-time 5 + --max-retries 3 + README.md SKILL.md references/*.md + fail: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + markdown: + name: Markdown Lint + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Write markdownlint config + run: | + cat > .markdownlint.json << 'EOF' + { + "default": true, + "MD013": false, + "MD033": false, + "MD041": false + } + EOF + + - name: Lint markdown files + uses: DavidAnson/markdownlint-cli2-action@v17 + with: + globs: "**/*.md" diff --git a/README.md b/README.md index caaeee6..8137fef 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # UZPROOF Agent Skill +[![npm](https://img.shields.io/npm/v/%40uzproof%2Fverify?label=npm%3A%20%40uzproof%2Fverify)](https://www.npmjs.com/package/@uzproof/verify) +[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE) + Agent Skill for verifying real on-chain usage on Solana. Works with [Claude Code](https://claude.ai/code), [Cursor](https://cursor.com), [GitHub Copilot](https://github.com/features/copilot), [Gemini CLI](https://geminicli.com), and [30+ other AI coding agents](https://agentskills.io/). ## What is UZPROOF? @@ -8,6 +11,8 @@ Agent Skill for verifying real on-chain usage on Solana. Works with [Claude Code UZPROOF is also the first Proof-of-Use attestor on the Solana Attestation Service (SAS), creating permanent on-chain records of verified usage. +**On-chain credential:** [`2chgBfvkwhnHQVVAyXKDK6CBjbCRMQ8aLWrysL5UQyyF`](https://explorer.solana.com/address/2chgBfvkwhnHQVVAyXKDK6CBjbCRMQ8aLWrysL5UQyyF) + ## Install ```bash diff --git a/SKILL.md b/SKILL.md index 652df72..dcd5829 100644 --- a/SKILL.md +++ b/SKILL.md @@ -14,6 +14,7 @@ You are building with UZPROOF, the first Proof-of-Use verification layer on Sola ## Use / Do Not Use **Use when:** + - Verifying a wallet performed a specific on-chain action (swap, stake, hold, mint) - Building reward distribution that requires proof of real usage - Gating access to features based on on-chain behavior @@ -23,6 +24,7 @@ You are building with UZPROOF, the first Proof-of-Use verification layer on Sola - Fetching token metadata and live prices **Do not use when:** + - Executing transactions (use Jupiter, Drift, etc. for that) - Reading raw transaction data (use Helius for that) - Building wallets or signing transactions @@ -142,6 +144,7 @@ See [references/attestation.md](references/attestation.md) for SAS integration d ## Action Types ### DeFi Actions + | Type | Verifies | |---|---| | `defi_swap` | Any swap on a supported DEX | @@ -164,6 +167,7 @@ See [references/attestation.md](references/attestation.md) for SAS integration d | `defi_create_lst` | LST creation | ### NFT Actions + | Type | Verifies | |---|---| | `nft_hold` | Owns NFT from collection | @@ -171,6 +175,7 @@ See [references/attestation.md](references/attestation.md) for SAS integration d | `nft_check` | General NFT ownership check | ### Utility Actions + | Type | Verifies | |---|---| | `token_balance` | Raw token balance | @@ -205,10 +210,10 @@ async function checkEligibility(wallet: string): Promise { } ``` -### Quest/Task Verification +### Task Verification ```typescript -async function verifyQuestTask(wallet: string, task: { type: string; config: object }) { +async function verifyTask(wallet: string, task: { type: string; config: object }) { const client = new UzproofClient({ apiKey: process.env.UZPROOF_API_KEY }); const result = await client.verify({ wallet, @@ -231,8 +236,7 @@ const info = await client.detectContract(userProgramId); if (info.detected) { console.log(`Detected: ${info.program.name} (${info.program.category})`); console.log(`Supported verifications: ${info.program.supportedActions.join(', ')}`); - // Use suggested quest template - const template = info.questTemplate; + const template = info.verificationTemplate; } ``` @@ -265,7 +269,7 @@ import { SAS_PROGRAM_ID, UZPROOF_CREDENTIAL, POU_SCHEMA, ACTION_TYPES, SUPPORTED SAS_PROGRAM_ID // "22zoJMtdu4tQc2PzL74ZUT7FrwgB1Udec8DdW4yw4BdG" UZPROOF_CREDENTIAL // "2chgBfvkwhnHQVVAyXKDK6CBjbCRMQ8aLWrysL5UQyyF" -POU_SCHEMA // "8yW2BboQuhp2MMmrQLFz35V6VSqC48MF7wZ5bmzcTeTF" +POU_SCHEMA // "9FQiiMtroSHP2Ewqfh3D94GPKnDjmeLT2ftqJ3E7QyWc" ACTION_TYPES // All 24 supported action types SUPPORTED_PROTOCOLS // 14 protocol names ``` diff --git a/references/anti-fraud.md b/references/anti-fraud.md new file mode 100644 index 0000000..8f3d889 --- /dev/null +++ b/references/anti-fraud.md @@ -0,0 +1,148 @@ +# Anti-Fraud Scoring + +## Overview + +Every verification request in UZPROOF runs an anti-fraud check alongside the on-chain data lookup. The check produces a **risk score (0–100)** based on suspicious signals detected on the wallet. It does not block requests — it flags them for review. + +The result is returned alongside verification data so your application can decide how to handle high-risk wallets. + +## Risk Score + +```typescript +const result = await client.verify({ + wallet: '7H4RVL...', + action: 'defi_swap', + config: { program_id: 'JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4' } +}); + +// result.riskScore = 0–100 +// result.signals = array of detected signals +// result.verified = true/false (on-chain result, unaffected by risk score) +``` + +A score of `0` means no suspicious signals. A score of `100` means maximum risk. + +## Signals + +### 1. `no_history` — No Transaction History (severity: high, +30) + +The wallet has zero recorded transactions on-chain. + +```text +Wallet has zero transaction history +``` + +Fresh wallets with no history are a strong indicator of sybil accounts created purely to farm rewards. + +### 2. `low_tx_count` — Low Transaction Count (severity: medium, +15) + +The wallet has fewer than 5 transactions total. + +```text +Only 2 transactions (min: 5) +``` + +Legitimate DeFi users accumulate transactions naturally. Very low counts suggest a wallet created for a single purpose. + +### 3. `new_wallet` — Recently Created Wallet (severity: high, +30) + +The wallet's oldest transaction is less than 3 days ago. + +```text +Wallet is only 1.2 days old (min: 3) +``` + +New wallets are a classic sybil pattern — attackers spin up fresh addresses in bulk before a campaign launches. + +### 4. `dust_balance` — Near-Zero SOL Balance (severity: medium, +15) + +The wallet holds less than 0.01 SOL. + +```text +SOL balance: 0.0021 SOL (below 0.01 SOL threshold) +``` + +Real users need SOL for gas. Dust balances indicate a funded-just-enough-to-transact sybil wallet. + +### 5. `fast_completion` — Instant Verification (severity: high, +30) + +The wallet was verified less than 10 seconds after the verification flow started. + +```text +Verified 3s after start (min: 10s) +``` + +Automated bots complete verifications near-instantly. Humans take longer to read instructions and connect wallets. + +## Risk Score Calculation + +Scores are additive and capped at 100: + +| Severity | Score Added | +|---|---| +| `low` | +5 | +| `medium` | +15 | +| `high` | +30 | +| `critical` | +50 | + +**Example:** A wallet that is 1 day old (`new_wallet`: +30) and has 2 transactions (`low_tx_count`: +15) gets a risk score of **45**. + +## Using Risk Score in Your Application + +```typescript +const client = new UzproofClient({ apiKey: process.env.UZPROOF_API_KEY }); + +const result = await client.verify({ + wallet, + action: 'defi_swap', + config: { program_id: 'JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4' } +}); + +if (!result.verified) { + // On-chain action not found + return { eligible: false, reason: 'action_not_verified' }; +} + +if (result.riskScore >= 60) { + // High risk — flag for manual review or block + return { eligible: false, reason: 'high_fraud_risk', score: result.riskScore }; +} + +if (result.riskScore >= 30) { + // Medium risk — allow but mark as suspicious + return { eligible: true, flagged: true, score: result.riskScore }; +} + +// Clean wallet, verified action +return { eligible: true, flagged: false }; +``` + +## Recommended Thresholds + +| Risk Score | Suggested Action | +|---|---| +| 0–29 | Allow | +| 30–59 | Allow with flag / extra review | +| 60–100 | Block or require manual review | + +Thresholds depend on your campaign's risk tolerance. Airdrops with large rewards should use stricter thresholds than loyalty programs. + +## Signal Response Format + +```typescript +// result.signals example +[ + { + signalType: 'new_wallet', + severity: 'high', + details: 'Wallet is only 1.2 days old (min: 3)', + value: 1.2 + }, + { + signalType: 'low_tx_count', + severity: 'medium', + details: 'Only 2 transactions (min: 5)', + value: 2 + } +] +``` diff --git a/references/contract-detect.md b/references/contract-detect.md index b6e2b4a..5c96716 100644 --- a/references/contract-detect.md +++ b/references/contract-detect.md @@ -25,7 +25,7 @@ const info = await client.detectContract('JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNy "category": "dex", "supportedActions": ["defi_swap", "defi_swap_buy", "defi_swap_sell", "defi_swap_volume"] }, - "questTemplate": { + "verificationTemplate": { "suggestedTitle": "Get started with Jupiter Aggregator v6", "suggestedDescription": "Complete tasks to earn XP and prove your on-chain activity with Jupiter Aggregator v6.", "suggestedTasks": [ @@ -79,7 +79,7 @@ If a program ID is not in the supported list, `detectContract()` returns: { "detected": false, "program": null, - "questTemplate": null + "verificationTemplate": null } ```