> ## Documentation Index
> Fetch the complete documentation index at: https://docs.range.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Privacy Protocol Screening

> Screen wallets before deposits to prevent tainted funds from entering your privacy protocol on Solana.

Privacy protocols on Solana - mixers, privacy pools, confidential transfer
programs - face a unique challenge: they need to prevent sanctioned or stolen
funds from entering the protocol while preserving user privacy for legitimate
users. If tainted funds flow through your protocol, it taints the entire pool
and exposes your project to regulatory risk.

Range's [Address Risk Score](/risk-api/risk/get-address-risk-score) provides a
straightforward solution: screen the depositor's wallet before accepting funds.
Block high-risk wallets at the door - before they can contaminate the pool.

<Info>
  **Prerequisites:** You need a [Risk API key](/introduction/getting-started)
  and familiarity with the [Address Risk
  Score](/risk-api/risk/get-address-risk-score) endpoint. For onchain
  enforcement, see the [Onchain Risk
  Verifier](/risk-api/guides/onchain-risk-verifier) guide.
</Info>

***

## Why Screen Deposits?

| Without screening                              | With screening                                 |
| ---------------------------------------------- | ---------------------------------------------- |
| Stolen funds enter your privacy pool           | Stolen funds are rejected before deposit       |
| OFAC-sanctioned addresses mix freely           | Sanctioned addresses are blocked at the door   |
| Your protocol becomes a laundering vector      | Your protocol stays clean for legitimate users |
| Regulators target your protocol                | Demonstrable compliance controls               |
| Honest users' funds are tainted by association | Pool integrity is preserved                    |

Privacy and compliance are not mutually exclusive. Screening depositors (not
tracking what happens inside the pool) lets you maintain privacy guarantees for
legitimate users while blocking bad actors.

***

## Architecture

```
User wants to deposit
      │
      ▼
┌──────────────────────────┐
│  Screen depositor wallet │
│  (Address Risk Score)    │
│                          │
│  riskScore 1–10          │
│  Includes sanctions +    │
│  ML + proximity analysis │
└──────────────────────────┘
      │
      ├── Score ≥ 7 ──────► REJECT deposit
      │                     Show reason
      │
      ├── Score 4–6 ──────► FLAG for review
      │                     Optional: require
      │                     additional verification
      │
      └── Score 1–3 ──────► ALLOW deposit
                            Proceed normally
```

<Note>
  Address Risk Score already includes sanctions and blacklist screening. You do
  not need a separate sanctions check - OFAC-flagged and stablecoin-blacklisted
  addresses are factored into the risk score.
</Note>

***

## Step 1: Screen the Depositor

Before accepting a deposit, check the wallet's risk score. This single API call
covers malicious proximity, ML-based behavioral analysis, sanctions, and
blacklist screening.

```bash theme={null}
curl -G https://api.range.org/v1/risk/address \
  --data-urlencode "address=DEPOSITOR_ADDRESS" \
  --data-urlencode "network=solana" \
  -H "Authorization: Bearer your_api_key_here"
```

```typescript theme={null}
async function screenDepositor(address: string) {
  const params = new URLSearchParams({
    address,
    network: 'solana',
  });

  const response = await fetch(
    `https://api.range.org/v1/risk/address?${params}`,
    { headers: { Authorization: `Bearer ${API_KEY}` } },
  );

  return response.json();
}
```

### Example Response - Flagged Wallet

```json theme={null}
{
  "riskScore": 9,
  "riskLevel": "Extremely high risk",
  "numHops": 1,
  "maliciousAddressesFound": [
    {
      "address": "SuspiciousAddr...",
      "distance": 1,
      "name_tag": "Exploit Funds",
      "entity": null,
      "category": "hack_funds"
    }
  ],
  "reasoning": "Address is 1 hop from known exploit funds.",
  "attribution": null
}
```

This wallet is 1 hop from known hack funds - it should be rejected.

***

## Step 2: Make a Deposit Decision

```typescript theme={null}
interface RiskData {
  riskScore: number;
  riskLevel: string;
  reasoning: string;
  attribution: { name_tag: string; entity: string } | null;
}

interface DepositDecision {
  decision: 'allow' | 'reject' | 'flag';
  reason: string;
}

function evaluateDeposit(riskData: RiskData): DepositDecision {
  const { riskScore, reasoning, attribution } = riskData;

  // Known system addresses and verified entities are always safe
  if (attribution) {
    return {
      decision: 'allow',
      reason: `Verified entity: ${attribution.name_tag} (${attribution.entity})`,
    };
  }

  // High risk - reject
  if (riskScore >= 7) {
    return {
      decision: 'reject',
      reason: `Risk score ${riskScore}/10: ${reasoning}`,
    };
  }

  // Medium risk - flag for review or require additional verification
  if (riskScore >= 4) {
    return {
      decision: 'flag',
      reason: `Elevated risk (${riskScore}/10): ${reasoning}`,
    };
  }

  // Low risk - allow
  return {
    decision: 'allow',
    reason: 'Address screening passed',
  };
}
```

***

## Step 3: Integrate into Your Deposit Flow

### Off-Chain (Backend / Frontend)

Screen wallets in your dApp frontend or backend before constructing the deposit
transaction:

```typescript theme={null}
async function handleDeposit(depositorAddress: string, amount: number) {
  // Screen the wallet
  const riskData = await screenDepositor(depositorAddress);
  const evaluation = evaluateDeposit(riskData);

  if (evaluation.decision === 'reject') {
    return {
      status: 'rejected',
      message:
        'This wallet has been flagged for connections to malicious activity. ' +
        'Deposit not permitted.',
    };
  }

  if (evaluation.decision === 'flag') {
    // Optional: require additional verification for medium-risk wallets
    const verified = await requestAdditionalVerification(depositorAddress);
    if (!verified) {
      return {
        status: 'rejected',
        message: 'Additional verification required.',
      };
    }
  }

  // Proceed with deposit
  return await executeDeposit(depositorAddress, amount);
}
```

### Onchain (Solana Program)

For protocol-level enforcement that can't be bypassed by calling the program
directly, use the
[Onchain Risk Verifier](/risk-api/guides/onchain-risk-verifier). This verifies
risk scores inside your Solana program using Switchboard oracles:

```rust theme={null}
// In your deposit instruction handler:
let risk_score = verified_feed.value(); // 0–100 scale (original 0–10 × 10)

// Reject deposits from high-risk wallets
if risk_score >= 70 {
    return Err(ErrorCode::HighRiskDepositor.into());
}

// Proceed with deposit logic
```

<Tip>
  Onchain enforcement is stronger than off-chain screening alone. Users can
  bypass your frontend, but they can't bypass your program's instruction logic.
  For maximum protection, use both: off-chain screening for UX (show the user
  why they're blocked) and onchain verification as the enforcement layer.
</Tip>

<Note>
  **Alternative: Signed message attestation.** Instead of using Switchboard
  oracles, you can implement a lighter-weight pattern where your backend signs
  an attestation message containing the screened wallet address and a timestamp.
  The Solana program then uses Ed25519 signature verification (via the Ed25519
  precompile) and instruction introspection to confirm: (1) the message was
  signed by your trusted attestation key, (2) the depositor matches the approved
  address in the message, and (3) the timestamp is fresh. This approach trades
  the decentralized oracle model for a simpler trusted-backend pattern.
</Note>

***

## Choosing Your Risk Threshold

The right threshold depends on your protocol's risk tolerance and regulatory
posture:

|   Threshold   | Rejects                                                       | Trade-off                                            |
| :-----------: | ------------------------------------------------------------- | ---------------------------------------------------- |
| **Score ≥ 8** | Only directly malicious or 1-hop addresses                    | Permissive - blocks clear threats, allows most users |
| **Score ≥ 7** | Addresses within 2 hops of malicious actors                   | Balanced - recommended starting point                |
| **Score ≥ 6** | Addresses within 2 hops (with multiple malicious connections) | Strict - fewer false negatives, more false positives |
| **Score ≥ 4** | Addresses within 3 hops                                       | Very strict - suitable for regulated environments    |

<Warning>
  Setting the threshold too low (e.g., ≥ 3) will reject a significant number of
  legitimate users. Many clean addresses are naturally within 4–5 hops of
  flagged addresses through shared exchange or program interactions. Start with
  ≥ 7 and adjust based on your data.
</Warning>

***

## Handling Edge Cases

### What If the Risk Check Fails?

Network errors or API timeouts shouldn't block legitimate deposits indefinitely.
Implement a fallback policy:

```typescript theme={null}
async function screenWithFallback(address: string) {
  try {
    const result = await Promise.race([
      screenDepositor(address),
      new Promise((_, reject) =>
        setTimeout(() => reject(new Error('timeout')), 5000),
      ),
    ]);
    return evaluateDeposit(result);
  } catch (error) {
    // Fallback: allow with logging for manual review
    console.warn(`Risk check failed for ${address}: ${error}`);
    return {
      decision: 'allow',
      reason: 'Risk check unavailable - allowed with manual review flag',
      requiresReview: true,
    };
  }
}
```

### Known System Addresses

Some addresses that interact with your protocol will be system programs, token
programs, or exchange hot wallets. These will have an `attribution` field in the
response - use it to fast-path known entities:

```typescript theme={null}
if (riskData.attribution) {
  // Verified non-malicious entity - skip further checks
  console.log(`Known entity: ${riskData.attribution.name_tag}`);
  return { decision: 'allow' };
}
```

### Rate Limits

If you're processing high deposit volume, see
[Rate Limits & Plans](/risk-api/product-info/rate-limits) for scaling options.
For onchain enforcement via Switchboard oracles, rate limits apply to the
oracle quote requests, not the onchain verification.

***

## Compliance Considerations

Screening deposits demonstrates that your protocol has taken reasonable steps to
prevent misuse. Document your screening policy and keep records:

* **Policy document** - Define your risk threshold and what happens when a
  deposit is rejected
* **Audit log** - Store the risk score, reasoning, and decision for each
  screened deposit
* **Threshold rationale** - Document why you chose your threshold and any
  adjustments over time
* **False positive handling** - Have a process for users to contest rejections

```typescript theme={null}
async function logScreeningResult(
  depositor: string,
  riskData: RiskData,
  decision: DepositDecision,
) {
  await complianceDB.insert({
    timestamp: new Date().toISOString(),
    depositor_address: depositor,
    risk_score: riskData.riskScore,
    risk_level: riskData.riskLevel,
    reasoning: riskData.reasoning,
    decision: decision.decision,
    decision_reason: decision.reason,
  });
}
```

***

## What's Next

<CardGroup cols={2}>
  <Card title="Address Risk Score" icon="location-dot" href="/risk-api/risk/get-address-risk-score">
    Full endpoint reference with scoring logic and examples.
  </Card>

  <Card title="Onchain Risk Verifier" icon="shield-halved" href="/risk-api/guides/onchain-risk-verifier">
    Enforce risk checks directly in your Solana program.
  </Card>

  <Card title="Compliance Pipeline" icon="gavel" href="/risk-api/guides/compliance-screening-pipeline">
    Build a full compliance screening workflow.
  </Card>

  <Card title="Supported Chains" icon="link" href="/risk-api/product-info/supported-chains">
    See which networks are supported for address risk scoring.
  </Card>
</CardGroup>
