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

# Quickstart

> Whiterail SDK로 5분 안에 시작하기

## 개요

Whiterail SDK는 이메일 OTP 인증과 Turnkey 통합을 통해 사용자를 안전하게 인증하는 간단한 방법을 제공합니다. SDK는 **완전한 Non-custodial** 아키텍처를 따르며, 개인키는 절대 클라이언트를 벗어나지 않습니다.

## 빠른 시작

### 1. SDK 초기화

```typescript theme={null}
import { WhiterailSDK } from "@whiterail/sdk";

const sdk = new WhiterailSDK();
```

<Note>
  **API 연결**: SDK는 기본적으로 `http://localhost:3009`에 연결됩니다. 프로덕션
  환경에서는 백엔드 서버 URL을 확인하세요.
</Note>

### 2. 계정 생성

사용자를 위한 Turnkey sub-organization을 생성합니다. 이 메서드는 멱등성(idempotent)이므로 동일한 이메일로 여러 번 호출해도 기존 계정을 반환합니다.

```typescript theme={null}
const result = await sdk.createAccount({
  email: "user@example.com",
});

if (!result.success) {
  console.error("계정 생성 실패:", result.error);
  return;
}

if (result.accountExists) {
  console.log("✅ 계정이 이미 존재합니다");
} else {
  console.log("✅ 새 계정이 생성되었습니다");
}

console.log("Sub-Organization ID:", result.subOrganizationId);
console.log("Solana 지갑 주소:", result.solanaAddress); // ✅ 지갑 주소 확인
```

<Info>
  **계정 생성 시 지갑도 함께 생성됨**: `createAccount`를 호출하면 Solana 지갑
  주소도 함께 생성되어 반환됩니다.
</Info>

### 3. 인증 시작 (OTP 전송)

사용자의 이메일로 OTP 코드를 전송합니다.

```typescript theme={null}
const authResult = await sdk.initAuth({
  email: "user@example.com",
});

if (!authResult.success) {
  console.error("OTP 전송 실패:", authResult.error);
  return;
}

console.log("✅ 이메일로 OTP가 전송되었습니다");
console.log("OTP ID:", authResult.otpId);
```

### 4. 인증 완료 (OTP 검증 + 지갑 생성)

OTP 코드를 검증하고 인증을 완료합니다. 첫 번째 인증 시 지갑이 생성됩니다.

```typescript theme={null}
const completeResult = await sdk.completeAuth({
  email: "user@example.com",
  otpCode: "123456", // 이메일로 받은 6자리 코드
});

if (!completeResult.success) {
  console.error("인증 실패:", completeResult.error);
  return;
}

if (completeResult.walletCreated) {
  console.log("✅ 지갑이 생성되었습니다!");
} else {
  console.log("✅ 기존 지갑으로 인증되었습니다");
}

console.log("Solana 주소:", completeResult.solanaAddress);
console.log("Sub-Organization ID:", completeResult.subOrganizationId);

// ✅ 세션이 자동으로 저장됩니다
// ✅ 이제 트랜잭션에 서명할 수 있습니다
if (sdk.isAuthenticated()) {
  console.log("🎉 인증 완료! 결제를 전송할 수 있습니다");
}
```

### 5. 결제 전송

```typescript theme={null}
// 통화 심볼 사용
const payment = await sdk.sendPayment({
  destination: "9aE476sH92Vz7DMPyq5WLPkrKWivxeuTKEFKd2sZZcde",
  currency: "USDC",
  amount: 100,
  network: "devnet",
});

if (!payment.success) {
  console.error("결제 실패:", payment.error);
  return;
}

console.log("🎉 결제가 전송되었습니다!");
console.log("트랜잭션 서명:", payment.signature);
console.log("탐색기 URL:", payment.explorerUrl); // ✅ 트랜잭션 확인 링크
```

**또는 민트 주소 직접 사용:**

```typescript theme={null}
const payment = await sdk.sendPayment({
  destination: "9aE476sH92Vz7DMPyq5WLPkrKWivxeuTKEFKd2sZZcde",
  mintAddress: "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
  decimals: 6,
  amount: 100,
  network: "devnet",
});
```

<Info>
  **트랜잭션 확인**: `explorerUrl`을 통해 Solscan에서 트랜잭션 상태를 확인할 수
  있습니다.
</Info>

## 주요 특징

<CardGroup cols={2}>
  <Card title="이메일 OTP 인증" icon="envelope">
    사용자의 이메일로 6자리 코드 전송
  </Card>

  <Card title="완전한 Non-custodial" icon="shield">
    개인키는 클라이언트 측에서만 생성 및 저장
  </Card>

  <Card title="간단한 SDK" icon="code">
    인증을 위한 단 3가지 메서드
  </Card>

  <Card title="Turnkey 통합" icon="key">
    Turnkey 인프라와 원활한 통합
  </Card>
</CardGroup>

## 인증 흐름

```
1. createAccount()  → Turnkey sub-organization 생성
2. initAuth()       → 이메일로 OTP 전송
3. completeAuth()   → OTP 검증 + 지갑 생성 (첫 인증 시)
```

<Warning>
  **세션 만료**: 인증 후 30분 동안 세션이 유효합니다. 페이지를 새로고침하거나
  30분이 지나면 재인증이 필요합니다.
</Warning>

<Info>
  **보안 설계**: 개인키는 절대 localStorage에 저장되지 않으며, 메모리에만
  존재합니다. 이는 XSS 공격으로부터 보호하기 위한 설계입니다.
</Info>

## 세션 관리

### 인증 상태 확인

```typescript theme={null}
// 사용자가 트랜잭션에 서명할 수 있는지 확인
if (sdk.isAuthenticated()) {
  console.log("✅ 사용자가 로그인되어 있습니다");
  // 결제 기능 활성화
} else {
  console.log("❌ 재인증이 필요합니다");
  // 로그인 화면 표시
}
```

### 현재 세션 가져오기

```typescript theme={null}
const session = sdk.getSession();
if (session) {
  console.log("이메일:", session.email);
  console.log("만료 시간:", new Date(session.expiresAt));
  // Note: ephemeralPrivateKey는 보안을 위해 접근 불가
}
```

### 로그아웃

```typescript theme={null}
const handleLogout = () => {
  sdk.clearSession();
  console.log("✅ 로그아웃 완료");
  // 로그인 페이지로 리다이렉트
};
```

<Warning>
  **세션 제한사항**: - 페이지를 새로고침하면 메모리가 지워지고 재인증이
  필요합니다 - `ephemeralPrivateKey`는 메모리에만 존재 (localStorage에 저장 안
  됨) - 이메일과 기본 정보는 localStorage에 저장되어 다음 로그인 시 자동으로
  채워집니다
</Warning>

## 완전한 예제 (에러 처리 포함)

```typescript theme={null}
import { WhiterailSDK } from "@whiterail/sdk";

const sdk = new WhiterailSDK();

async function authenticate(email: string, otpCode: string) {
  try {
    // 1단계: 계정 생성 (멱등성)
    const account = await sdk.createAccount({ email });
    if (!account.success) {
      throw new Error(account.error);
    }
    console.log("지갑 주소:", account.solanaAddress);

    // 2단계: OTP 전송
    const auth = await sdk.initAuth({ email });
    if (!auth.success) {
      throw new Error(auth.error);
    }
    console.log("OTP가 이메일로 전송되었습니다");

    // 사용자가 OTP 입력할 때까지 대기...

    // 3단계: 인증 완료
    const complete = await sdk.completeAuth({ email, otpCode });
    if (!complete.success) {
      throw new Error(complete.error);
    }

    console.log("✅ 인증 완료!");
    return complete.solanaAddress;
  } catch (error) {
    console.error("인증 실패:", error.message);
    throw error;
  }
}

async function sendPayment() {
  // 인증 확인
  if (!sdk.isAuthenticated()) {
    throw new Error("재인증이 필요합니다");
  }

  // 결제 전송
  const payment = await sdk.sendPayment({
    destination: "9aE476sH92Vz7DMPyq5WLPkrKWivxeuTKEFKd2sZZcde",
    currency: "USDC",
    amount: 100,
    network: "devnet",
  });

  if (!payment.success) {
    throw new Error(payment.error);
  }

  console.log("트랜잭션:", payment.explorerUrl);
  return payment.signature;
}
```

## 고급 사용법

### 3단계로 나누어서 결제하기

더 세밀한 제어가 필요한 경우, 결제를 3단계로 나누어 처리할 수 있습니다:

```typescript theme={null}
// 1단계: Payment intent 생성
const intent = await sdk.createPaymentIntent({
  destination: "9aE476sH92Vz7DMPyq5WLPkrKWivxeuTKEFKd2sZZcde",
  currency: "USDC",
  amount: 100,
  network: "devnet",
});

if (!intent.success) {
  throw new Error(intent.error);
}

console.log("Payment Intent ID:", intent.paymentIntentId);
console.log("트랜잭션 상세:", intent.details);

// 2단계: 트랜잭션 서명
const signed = await sdk.signTransaction(intent.transaction);

if (!signed.success) {
  throw new Error(signed.error);
}

// 3단계: 서명된 트랜잭션 제출
const result = await sdk.submitTransaction({
  paymentIntentId: intent.paymentIntentId,
  signedTransaction: signed.signedTransaction,
  network: "devnet",
});

if (!result.success) {
  throw new Error(result.error);
}

console.log("트랜잭션 서명:", result.signature);
console.log("확인 상태:", result.confirmationStatus);
```

<Note>
  **사용자 정의 수수료 지불자 (feePayer)**

  `feePayer` 파라미터를 사용하려면 위의 3단계 흐름을 사용해야 하며, 2단계와 3단계 사이에 수수료 지불자의 서명을 추가해야 합니다. 현재 SDK는 수수료 지불자 서명 기능을 제공하지 않으므로, 클라이언트에서 별도로 처리해야 합니다.

  ```typescript theme={null}
  const intent = await sdk.createPaymentIntent({
    destination: "RECIPIENT_ADDRESS",
    currency: "USDC",
    amount: 100,
    feePayer: "FEE_PAYER_ADDRESS", // 사용자 정의 수수료 지불자
    network: "devnet",
  });

  // SDK 서명
  const signed = await sdk.signTransaction(intent.transaction);

  // ⚠️ 여기서 feePayer의 서명을 추가해야 함 (SDK 외부에서 처리)
  // const feePayerSigned = await addFeePayerSignature(signed.signedTransaction);

  // 제출
  const result = await sdk.submitTransaction({
    paymentIntentId: intent.paymentIntentId,
    signedTransaction: signed.signedTransaction, // 또는 feePayerSigned
    network: "devnet",
  });
  ```
</Note>

## 다음 단계

<CardGroup cols={2}>
  <Card title="설치" icon="download" href="/sdk/installation">
    개발 환경 설정하기
  </Card>

  <Card title="SDK 레퍼런스" icon="book" href="/sdk/api-client">
    모든 메서드 살펴보기
  </Card>
</CardGroup>
