Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -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"
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -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?
Expand All @@ -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
Expand Down
14 changes: 9 additions & 5 deletions SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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 |
Expand All @@ -164,13 +167,15 @@ 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 |
| `nft_mint` | Minted an NFT |
| `nft_check` | General NFT ownership check |

### Utility Actions

| Type | Verifies |
|---|---|
| `token_balance` | Raw token balance |
Expand Down Expand Up @@ -205,10 +210,10 @@ async function checkEligibility(wallet: string): Promise<boolean> {
}
```

### 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,
Expand All @@ -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;
}
```

Expand Down Expand Up @@ -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
```
Expand Down
148 changes: 148 additions & 0 deletions references/anti-fraud.md
Original file line number Diff line number Diff line change
@@ -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
}
]
```
4 changes: 2 additions & 2 deletions references/contract-detect.md
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand Down Expand Up @@ -79,7 +79,7 @@ If a program ID is not in the supported list, `detectContract()` returns:
{
"detected": false,
"program": null,
"questTemplate": null
"verificationTemplate": null
}
```

Expand Down
Loading