메인 콘텐츠로 건너뛰기
이 가이드에서는 ChainStream KYT/KYA 컴플라이언스 기능을 애플리케이션에 통합하는 방법을 다룹니다. CEX 입출금 리스크 관리, 지갑 리스크 알림, 일괄 스크리닝의 완전한 시나리오를 포함합니다.

사전 요구사항

API 설정

설정
Base URLhttps://api.chainstream.io/
Auth Domaindex.asia.auth.chainstream.io
Audiencehttps://api.dex.chainstream.io

KYT 관련 스코프

스코프설명
kyt.readKYT API 읽기 권한 (거래 리스크 조회)
kyt.writeKYT API 쓰기 권한 (거래 분석 등록)

액세스 토큰 생성

import { AuthenticationClient } from 'auth0';

const auth0Client = new AuthenticationClient({
  domain: 'dex.asia.auth.chainstream.io',
  clientId: 'your-client-id',
  clientSecret: 'your-client-secret'
});

// 전체 KYT 권한으로 토큰 획득
const response = await auth0Client.oauth.clientCredentialsGrant({
  audience: 'https://api.dex.chainstream.io',
  scope: 'kyt.read kyt.write'
});

const accessToken = response.data.access_token;

API 호출

모든 요청에 헤더에 토큰을 포함해야 합니다:
const response = await fetch('https://api.chainstream.io/v1/kyt/transfer', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${accessToken}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ /* 요청 본문 */ })
});

CEX 입금 리스크 관리

거래소 입금 시나리오는 KYT의 핵심 사용 사례로, 자금이 입금되기 전에 리스크 평가가 필요합니다.

비즈니스 흐름

통합 단계

1

클라이언트 초기화

import { AuthenticationClient } from 'auth0';

// 토큰 생성 (캐싱 권장, 만료 전 갱신)
async function getAccessToken() {
  const auth0Client = new AuthenticationClient({
    domain: 'dex.asia.auth.chainstream.io',
    clientId: process.env.CHAINSTREAM_CLIENT_ID,
    clientSecret: process.env.CHAINSTREAM_CLIENT_SECRET
  });

  const { data } = await auth0Client.oauth.clientCredentialsGrant({
    audience: 'https://api.dex.chainstream.io',
    scope: 'kyt.read kyt.write'
  });

  return data.access_token;
}
2

거래 감지

사용자 입금 주소로의 수신 거래 모니터링:
async function onDepositDetected(tx) {
  const deposit = {
    network: 'ethereum',           // 네트워크: bitcoin, ethereum, Solana
    asset: tx.asset,               // 자산 유형: ETH, SOL 등
    transferReference: tx.hash,    // 거래 해시
    direction: 'received'          // 방향: sent 또는 received
  };
  
  // KYT 분석 호출
  const result = await registerTransfer(deposit);
  
  // 리스크 평가 조회
  const risk = await getTransferSummary(result.externalId);
  
  // 결정 실행
  await executeDecision(tx, risk);
}
3

거래 등록

KYT API 호출로 거래 등록:
async function registerTransfer(deposit) {
  const response = await fetch('https://api.chainstream.io/v1/kyt/transfer', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${accessToken}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      network: deposit.network,
      asset: deposit.asset,
      transferReference: deposit.transferReference,
      direction: deposit.direction
    })
  });
  
  return await response.json();
}
4

리스크 평가 조회

거래 리스크 요약 조회:
async function getTransferSummary(transferId) {
  const response = await fetch(
    `https://api.chainstream.io/v1/kyt/transfers/${transferId}/summary`,
    {
      headers: {
        'Authorization': `Bearer ${accessToken}`
      }
    }
  );
  
  return await response.json();
}
5

자동 결정

리스크 수준에 따라 적절한 조치 실행:
async function executeDecision(tx, risk) {
  const riskLevel = risk.rating; // SEVERE, HIGH, MEDIUM, LOW
  
  switch (riskLevel) {
    case 'SEVERE':
      await freezeDeposit(tx);
      await createSARReport(tx, risk);
      await notifyCompliance(tx, risk);
      break;
      
    case 'HIGH':
      await holdDeposit(tx, { hours: 24 });
      await createManualReview(tx, risk);
      break;
      
    case 'MEDIUM':
      await creditDeposit(tx);
      await flagForMonitoring(tx, risk);
      break;
      
    case 'LOW':
      await creditDeposit(tx);
      break;
  }
  
  // 감사 로그 기록
  await auditLog.record({
    action: 'DEPOSIT_RISK_DECISION',
    txHash: tx.hash,
    riskLevel,
    timestamp: new Date()
  });
}

전체 흐름 상세

엔드투엔드 컴플라이언스 통합 흐름: 감지 → 등록 → 폴링 → 리스크 판단 → 해제/동결

1. 감지 단계

트리거 소스설명지연 시간
온체인 모니터링입금 주소 모니터링블록 컨펌 시간
사용자 제출출금 요청즉시
스케줄 스캔캐치업 메커니즘설정 가능

2. 등록 단계

POST https://api.chainstream.io/v1/kyt/transfer
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "network": "ethereum",
  "asset": "ETH",
  "transferReference": "0x1234567890abcdef...",
  "direction": "received"
}
응답:
{
  "externalId": "123e4567-e89b-12d3-a456-426614174000",
  "asset": "ETH",
  "network": "ethereum",
  "transferReference": "0x1234567890abcdef...",
  "direction": "received",
  "updatedAt": "2024-01-15T10:30:00.000Z"
}

3. 조회 단계

async function pollForResult(transferId, maxAttempts = 10) {
  for (let i = 0; i < maxAttempts; i++) {
    const response = await fetch(
      `https://api.chainstream.io/v1/kyt/transfers/${transferId}/summary`,
      {
        headers: { 'Authorization': `Bearer ${accessToken}` }
      }
    );
    const data = await response.json();
    
    if (data.rating) {
      return data;
    }
    
    await new Promise(r => setTimeout(r, 3000)); // 3초 간격
  }
  
  throw new Error('Analysis timeout');
}

4. 판단 단계

리스크 판단 규칙 설정:
risk_rules:
  severe:
    action: FREEZE
    auto_execute: true
    notify:
      - compliance@company.com
      - security@company.com
    
  high:
    action: MANUAL_REVIEW
    auto_execute: false
    hold_period: 24h
    escalation: 4h
    
  medium:
    action: FLAG
    auto_execute: true
    monitoring_period: 30d
    
  low:
    action: PASS
    auto_execute: true

5. 실행 단계

조치트리거 조건후속 조치
해제LOW 리스크정상 입금/결제
플래그MEDIUM 리스크입금하되 모니터링 지속
보류HIGH 리스크수동 검토 대기열 진입
동결SEVERE 리스크동결 + 컴플라이언스 보고

전체 서비스 구현

import { AuthenticationClient } from 'auth0';

class ComplianceService {
  constructor() {
    this.accessToken = null;
    this.tokenExpiry = null;
  }

  // 토큰 획득 또는 갱신
  async getAccessToken() {
    if (this.accessToken && this.tokenExpiry > Date.now()) {
      return this.accessToken;
    }

    const auth0Client = new AuthenticationClient({
      domain: 'dex.asia.auth.chainstream.io',
      clientId: process.env.CHAINSTREAM_CLIENT_ID,
      clientSecret: process.env.CHAINSTREAM_CLIENT_SECRET
    });

    const { data } = await auth0Client.oauth.clientCredentialsGrant({
      audience: 'https://api.dex.chainstream.io',
      scope: 'kyt.read kyt.write'
    });

    this.accessToken = data.access_token;
    // 토큰은 보통 24시간 유효, 1시간 전에 갱신
    this.tokenExpiry = Date.now() + (23 * 60 * 60 * 1000);
    
    return this.accessToken;
  }

  // 입금 컴플라이언스 확인
  async checkDeposit(deposit) {
    const token = await this.getAccessToken();
    
    // 1. 거래 등록
    const registerResponse = await fetch('https://api.chainstream.io/v1/kyt/transfer', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        network: deposit.network,
        asset: deposit.asset,
        transferReference: deposit.txHash,
        direction: 'received'
      })
    });
    const registered = await registerResponse.json();

    // 2. 리스크 평가 대기 및 조회
    const risk = await this.waitForAnalysis(token, registered.externalId);
    
    // 3. 결정 생성
    const decision = this.makeDecision(risk);
    
    // 4. 감사 기록
    await this.auditLog(deposit, risk, decision);
    
    return decision;
  }

  async waitForAnalysis(token, transferId, maxAttempts = 10) {
    for (let i = 0; i < maxAttempts; i++) {
      const response = await fetch(
        `https://api.chainstream.io/v1/kyt/transfers/${transferId}/summary`,
        { headers: { 'Authorization': `Bearer ${token}` } }
      );
      const result = await response.json();
      
      if (result.rating) {
        return result;
      }
      await new Promise(r => setTimeout(r, 3000));
    }
    throw new Error('Analysis timeout');
  }

  makeDecision(risk) {
    const decisions = {
      'SEVERE': {
        action: 'FREEZE',
        requireSAR: true,
        notify: ['compliance@company.com', 'security@company.com']
      },
      'HIGH': {
        action: 'HOLD',
        requireReview: true,
        holdHours: 24
      },
      'MEDIUM': {
        action: 'PASS',
        flagMonitoring: true
      },
      'LOW': {
        action: 'PASS'
      }
    };
    return decisions[risk.rating] || decisions['LOW'];
  }

  async auditLog(deposit, risk, decision) {
    console.log({
      timestamp: new Date().toISOString(),
      type: 'COMPLIANCE_CHECK',
      deposit,
      risk,
      decision
    });
  }
}

// 사용 예시
const compliance = new ComplianceService();

app.post('/deposit/process', async (req, res) => {
  const deposit = req.body;
  const decision = await compliance.checkDeposit(deposit);
  res.json(decision);
});

CEX 출금 리스크 관리

출금 시나리오에서는 사용자가 출금을 요청할 때 대상 주소의 리스크를 확인해야 합니다.

비즈니스 흐름

구현 예시

async function handleWithdrawal(request) {
  const { toAddress } = request;
  const token = await complianceService.getAccessToken();
  
  // 1. 주소 등록
  const registerResponse = await fetch('https://api.chainstream.io/v1/kyt/address', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ address: toAddress })
  });
  await registerResponse.json();
  
  // 2. 주소 리스크 조회
  const riskResponse = await fetch(
    `https://api.chainstream.io/v1/kyt/addresses/${toAddress}/risk`,
    { headers: { 'Authorization': `Bearer ${token}` } }
  );
  const addressRisk = await riskResponse.json();
  
  // 3. 리스크 처리
  switch (addressRisk.rating) {
    case 'SEVERE':
      return {
        status: 'REJECTED',
        reason: 'Target address is associated with known criminal activity',
        riskLevel: 'SEVERE'
      };
      
    case 'HIGH':
      return {
        status: 'PENDING_CONFIRMATION',
        warning: 'This address has been flagged as high risk',
        riskDetails: addressRisk,
        requiresConfirmation: true
      };
      
    default:
      return {
        status: 'APPROVED',
        riskLevel: addressRisk.rating
      };
  }
}

// Express 라우트 예시
app.post('/withdraw/request', async (req, res) => {
  const result = await handleWithdrawal(req.body);
  res.json(result);
});

지갑 리스크 알림

지갑 애플리케이션에서 사용자가 전송을 시작하기 전에 리스크 알림을 제공합니다.

사용자 경험 흐름

프론트엔드/백엔드 통합

프론트엔드에서 clientSecret을 직접 노출하지 마세요. 백엔드 API 프록시를 사용하여 ChainStream을 호출하세요.
// 주소 입력 변경 시 트리거
async function onAddressChange(address) {
  if (!isValidAddress(address)) return;
  
  setLoading(true);
  
  try {
    // 백엔드 프록시 API 호출
    const response = await fetch('/api/risk/check-address', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ address })
    });
    const risk = await response.json();
    
    setRiskInfo({
      level: risk.rating,
      labels: risk.labels,
      warnings: risk.warnings
    });
  } finally {
    setLoading(false);
  }
}

일괄 주소 스크리닝

엔터프라이즈 수준의 기존 주소 컴플라이언스 스크리닝.

사용 사례

  • 정기 컴플라이언스 감사
  • 새로운 규제 요구사항 적용
  • M&A 실사
  • 리스크 스크리닝

일괄 스크리닝 구현

async function batchScreenAddresses(addresses) {
  const token = await complianceService.getAccessToken();
  const results = [];
  
  for (const address of addresses) {
    try {
      // 주소 등록
      await fetch('https://api.chainstream.io/v1/kyt/address', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${token}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ address })
      });
      
      // 리스크 조회
      const riskResponse = await fetch(
        `https://api.chainstream.io/v1/kyt/addresses/${address}/risk`,
        { headers: { 'Authorization': `Bearer ${token}` } }
      );
      const risk = await riskResponse.json();
      
      results.push({
        address,
        rating: risk.rating,
        riskScore: risk.riskScore
      });
    } catch (error) {
      results.push({
        address,
        error: error.message
      });
    }
  }
  
  // 고위험 주소 처리
  const highRiskAddresses = results.filter(
    r => r.rating === 'SEVERE' || r.rating === 'HIGH'
  );
  
  return { all: results, highRisk: highRiskAddresses };
}

모범 사례

임계값 설정 권장

비즈니스 유형에 따라 리스크 임계값을 조정하세요:
비즈니스 유형SEVERE 처리HIGH 처리MEDIUM 처리
인가된 CEX자동 동결수동 검토모니터링 플래그
지갑 앱강한 경고경고알림
DeFi 프로토콜상호작용 거절경고정상
OTC 플랫폼거래 거절추가 KYC정상

감사 로그 요구사항

완전한 감사 추적 보장:
{
  "eventId": "evt_123456",
  "timestamp": "2024-01-15T10:30:00Z",
  "eventType": "RISK_DECISION",
  "subject": {
    "transferId": "123e4567-e89b-12d3-a456-426614174000",
    "txHash": "0x...",
    "userId": "user_789"
  },
  "riskAssessment": {
    "rating": "HIGH",
    "riskScore": 72
  },
  "decision": {
    "action": "HOLD",
    "decidedBy": "SYSTEM",
    "reason": "Auto-hold per risk policy"
  },
  "metadata": {
    "policyVersion": "1.2.0",
    "engineVersion": "2024.01"
  }
}

다음 단계

인증 문서

상세 인증 가이드

KYT 개념

KYT 핵심 개념

KYA 개념

KYA 핵심 개념

KYT API 레퍼런스

KYT API 전체 문서