@abcwalletio/core
v1.1.1
Published
ABC WaaS core SDK — MPC-based wallet authentication, signing, and storage primitives
Readme
@abcwalletio/core
ABC Wallet-as-a-Service 핵심 SDK입니다. MPC(Multi-Party Computation) 기반 지갑 생성, 인증, 서명 기능을 제공합니다.
Installation
pnpm add @abcwalletio/coreQuick Start
import { createWaasSDK, MemoryStorage } from '@abcwalletio/core';
// SDK 초기화
const sdk = await createWaasSDK({
storage: new MemoryStorage(),
auth: {
v2: {
clientId: 'your-client-id',
clientSecret: 'your-client-secret',
},
},
});
// WASM 초기화 확인
if (sdk.isReady()) {
console.log('SDK is ready!');
}Modules
Auth Module
인증 모듈은 두 가지 경로를 제공합니다 — 동일 SDK 인스턴스에서 공존 가능합니다.
| 경로 | 구조 | 적합한 경우 |
|------|------|------------|
| V2 (Secure Channel) | 브라우저가 이메일/비밀번호를 암호화해 WaaS에 직접 로그인 | 백엔드 없는 SPA, 간단한 통합, PoC |
| V3 (TokenProvider 주입) | 고객사 백엔드가 /auth-service/v3/login으로 토큰을 발급해 프론트로 전달 | 자체 인증 스택이 있는 엔터프라이즈 |
// ─────────────────────────────────────────────────────────
// V3 인증 — TokenProvider 주입
// ─────────────────────────────────────────────────────────
// V3만 사용하면 clientSecret이 브라우저에 필요 없다.
const sdk = await createWaasSDK({
storage,
auth: {
v3: {
tokenProvider: async () => {
// 고객사 백엔드가 자체 세션을 검증하고 WaaS 토큰을 대신 발급
const r = await fetch('/api/waas/token', { credentials: 'include' });
if (!r.ok) throw new Error('unauthorized');
const { access_token, expires_in } = await r.json();
return { accessToken: access_token, expiresIn: expires_in };
},
// 선택: 만료 몇 초 전에 재발급할지 (기본 300초)
refreshThresholdSec: 300,
},
},
});
// 필요 시 명시적 토큰 확보 (자동 발급 포함)
const token = await sdk.auth.v3.getValidAccessToken();
// ─────────────────────────────────────────────────────────
// V2 인증 — Secure Channel 기반 (이메일/비밀번호)
// ─────────────────────────────────────────────────────────
const result = await sdk.auth.v2.loginWithEmail('[email protected]', 'password');
// 회원가입
await sdk.auth.v2.sendVerificationCode('[email protected]');
await sdk.auth.v2.registerWithEmail({
email: '[email protected]',
password: 'password123',
verificationCode: '123456',
});
// 비밀번호 초기화
await sdk.auth.v2.sendResetPasswordCode('[email protected]');
await sdk.auth.v2.resetPassword('[email protected]', '123456', 'newPassword');
// ─────────────────────────────────────────────────────────
// 공통 기능 — V3/V2 어느 쪽이든 인증되면 성립
// ─────────────────────────────────────────────────────────
// 로컬 토큰 존재 여부 확인 (서버 왕복 없음)
const isAuthV3 = await sdk.auth.v3.isAuthenticated(); // V3 tokenProvider 활성 + 발급 성공
const isAuthV2 = await sdk.auth.v2.isAuthenticated();
// 서버 측 세션 유효성까지 검증 (getWalletUser 경로)
const serverOk = await sdk.verifyServerSession();
// 현재 사용자 정보 — V3는 user 정보를 보관하지 않으므로 V2만 조회
const user = await sdk.auth.v2.getUser();
// Access Token 가져오기 — V3 활성이면 필요 시 자동 발급 후 반환, 아니면 V2 폴백
const token = await sdk.auth.getAccessToken();
// SDK 레벨 로그아웃: abc_waas_* prefix 키만 삭제 + v3/v2 토큰 정리 + 'logout' broadcast
await sdk.logout();
// 또는 특정 모듈만 정리
await sdk.auth.v3.logout(); // V3 메모리 캐시 정리 + 'logout' 이벤트
await sdk.auth.v2.logout();서버 세션 무효화 범위 현재 backend는 refresh token revocation endpoint를 제공하지 않습니다.
logout()이후에도 refresh token은 TTL이 만료될 때까지 서버 측에서 유효합니다. 토큰 유출 위험 환경에서는 호출측에서 별도 세션 파기 플로우를 추가하세요.
MPC Module
MPC 키 생성 및 서명 기능을 제공합니다.
// 키 생성
const keyResult = await sdk.mpc.generateKeyShare(
'secp256k1', // curve: 'secp256k1' | 'ed25519'
'password123' // 비밀번호 (8자 이상)
);
console.log('Key ID:', keyResult.keyId);
console.log('Public Key:', keyResult.publicKey);
// 키 복구 (기존 키가 있는 경우)
const recovered = await sdk.mpc.recoverKeyShare(
'secp256k1',
'password123'
);
// 일반 서명 (secp256k1 + ed25519)
const signResult = await sdk.mpc.sign(keyId, messageHash, 'password123');
console.log('Signature:', signResult.signature);
// MTA 서명 (secp256k1 전용, 더 효율적)
const mtaResult = await sdk.mpc.signMta(keyId, messageHash, 'password123');
console.log('Signature:', mtaResult.signature);
// 자동 서명 — curve에 따라 최적 방식 선택 (secp256k1 → signMta, ed25519 → sign)
const autoResult = await sdk.mpc.signAuto(keyId, messageHash, 'password123');
// 공개키 조회
const publicKey = await sdk.mpc.getPublicKey(keyId);
// 비밀번호 검증
const isValid = await sdk.mpc.validatePassword('password123');
// Share 유효성 검증
const shareValid = await sdk.mpc.validateShare('password123');Wallet API Module
V3 Wallet API를 호출합니다.
// 지갑 정보 조회
const wallet = await sdk.wallet.getWallet();
// 지갑 키 목록 조회
const keys = await sdk.wallet.getWalletKeys();
// 지갑 사용자 정보 조회
const user = await sdk.wallet.getWalletUser();
// 지갑 토큰 발급 (MPC 노드 인증용)
const walletToken = await sdk.wallet.getWalletToken(keyId);
// 새 키 등록
await sdk.wallet.registerWalletKey(keyId, curve, publicKey);Storage
다양한 Storage 구현체를 제공합니다. 모두 StorageInterface를 구현합니다.
MemoryStorage
인메모리 저장소입니다. 테스트나 SSR 환경에서 사용합니다.
import { MemoryStorage } from '@abcwalletio/core';
const storage = new MemoryStorage();LocalStorageAdapter
window.localStorage를 래핑합니다. 영구 저장이 필요한 브라우저 환경에서 사용합니다.
import { LocalStorageAdapter } from '@abcwalletio/core';
const storage = new LocalStorageAdapter();SessionStorageAdapter
window.sessionStorage를 래핑합니다. 세션 단위 저장이 필요한 경우 사용합니다.
import { SessionStorageAdapter } from '@abcwalletio/core';
const storage = new SessionStorageAdapter();IndexedDBStorage
IndexedDB 기반 저장소입니다. 대용량 데이터나 더 안전한 저장이 필요한 경우 사용합니다.
import { IndexedDBStorage } from '@abcwalletio/core';
const storage = new IndexedDBStorage();
await storage.waitForInit(); // 비동기 초기화 대기
// 초기화 상태 확인
if (storage.ready) {
console.log('IndexedDB is ready');
}
// 사용 후 정리
storage.close();BridgeStorage
모바일 앱의 웹뷰에서 네이티브 앱과 통신하기 위한 비동기 저장소입니다. AsyncStorageInterface를 구현합니다.
import { BridgeStorage } from '@abcwalletio/core';
// 기본 사용법 (window.abcWaas.storage 자동 참조)
const storage = new BridgeStorage();
// 커스텀 storageKey 사용
const storage = new BridgeStorage({ storageKey: 'myCustomBridge' });
// 커스텀 핸들러 직접 주입
const storage = new BridgeStorage({ handler: myCustomHandler });
// 비동기 API 사용
const value = await storage.getItem('key');
await storage.setItem('key', 'value');
await storage.removeItem('key');
await storage.clear();네이티브 앱 개발자 가이드
네이티브 앱에서 웹뷰에 브릿지를 주입해야 합니다.
1. window.abcWaas.storage 주입
// 네이티브 앱에서 웹뷰에 주입할 객체
window.abcWaas = {
storage: {
// 필수: ABC WaaS 브릿지 마커 메서드
isAbcWaasBridge: () => Promise.resolve(true),
// 필수: Storage 메서드들
getItem: (key) => { /* Promise<string | null> 반환 */ },
setItem: (key, value) => { /* Promise<void> 반환 */ },
removeItem: (key) => { /* Promise<void> 반환 */ },
// 선택: 추가 메서드
clear: () => { /* Promise<void> 반환 */ },
getAllKeys: () => { /* Promise<string[]> 반환 */ },
}
};2. 필수 메서드
SDK는 REQUIRED_BRIDGE_METHODS 상수로 필수 메서드를 정의합니다:
import { REQUIRED_BRIDGE_METHODS } from '@abcwalletio/core';
// ['isAbcWaasBridge', 'getItem', 'setItem', 'removeItem']isAbcWaasBridge(): 유효한 ABC WaaS 브릿지임을 확인하는 마커 메서드. 반드시Promise<true>를 반환해야 합니다.getItem(key): 키에 해당하는 값을 반환setItem(key, value): 키-값 쌍을 저장removeItem(key): 키에 해당하는 값을 삭제
3. AsyncBridgeHandler 인터페이스
import type { AsyncBridgeHandler } from '@abcwalletio/core';
const handler: AsyncBridgeHandler = {
isAbcWaasBridge: () => Promise.resolve(true),
getItem: (key) => nativeStorage.get(key),
setItem: (key, value) => nativeStorage.set(key, value),
removeItem: (key) => nativeStorage.delete(key),
// prefix-aware clear 또는 getAllKeys 중 **하나는 반드시 제공** 필요.
// 둘 다 없으면 페이지 새로고침 후 `sdk.logout()`이 영속 키를 못 지움.
clear: (prefix) => nativeStorage.clear(prefix), // 선택 (prefix 인자 받음)
getAllKeys: () => nativeStorage.keys(), // 선택 (권장)
};Custom Storage
StorageInterface를 직접 구현할 수도 있습니다. 모든 메서드가 비동기입니다.
import type { StorageInterface } from '@abcwalletio/core';
class CustomStorage implements StorageInterface {
async getItem(key: string): Promise<string | null> { /* ... */ }
async setItem(key: string, value: string): Promise<void> { /* ... */ }
async removeItem(key: string): Promise<void> { /* ... */ }
/**
* prefix가 있으면 해당 prefix로 시작하는 키만, 없으면 전체 삭제.
* 빈 문자열 prefix는 throw해야 합니다 (전체와 구분 모호).
*/
async clear(prefix?: string): Promise<void> { /* ... */ }
}AsyncStorageInterface (Deprecated)
StorageInterface의 별칭입니다. 하위 호환을 위해 유지되며, 새 코드에서는
StorageInterface를 직접 사용하세요.
Event System
SDK는 이벤트 시스템을 통해 인증 상태 변화를 알립니다.
이벤트 타입
| Event | Payload | Description |
|-------|---------|-------------|
| tokenRefreshed | { accessToken, refreshToken } | 토큰이 자동 갱신되었을 때 |
| authError | { error: WaasError } | 인증 관련 에러 발생 시 |
| logout | void | 로그아웃 시 |
이벤트 구독/해제
import { createWaasSDK, type WaasEventHandler } from '@abcwalletio/core';
const sdk = await createWaasSDK({ ... });
// 이벤트 핸들러 정의
const handleTokenRefreshed: WaasEventHandler<'tokenRefreshed'> = (payload) => {
console.log('토큰이 갱신되었습니다:', payload.accessToken);
};
const handleAuthError: WaasEventHandler<'authError'> = (payload) => {
console.error('인증 에러:', payload.error.message);
// 로그인 페이지로 이동 등의 처리
};
const handleLogout: WaasEventHandler<'logout'> = () => {
console.log('로그아웃되었습니다');
// UI 상태 초기화 등
};
// 이벤트 구독
sdk.on('tokenRefreshed', handleTokenRefreshed);
sdk.on('authError', handleAuthError);
sdk.on('logout', handleLogout);
// 이벤트 해제
sdk.off('tokenRefreshed', handleTokenRefreshed);Auto Token Refresh
SDK는 JWT 토큰 자동 갱신 기능을 제공합니다. getValidAccessToken() 메서드를
사용하면 토큰 만료를 자동으로 처리합니다. V2와 V3의 갱신 방식이 다릅니다.
| 경로 | 갱신 방식 | 저장소 |
|------|----------|--------|
| V2 | refresh_token을 서버에 제출해 access token 재발급 | Storage(localStorage 등) |
| V3 | tokenProvider를 재호출해 고객사 백엔드에서 새 토큰을 가져옴 | 메모리만 (Storage 미저장) |
V2 사용법
try {
const accessToken = await sdk.auth.v2.getValidAccessToken();
// accessToken을 사용한 API 호출
} catch (error) {
console.error('인증 필요:', error);
}V3 사용법
try {
const accessToken = await sdk.auth.v3.getValidAccessToken();
// 만료 임박 시 SDK가 자동으로 tokenProvider를 재호출해 새 토큰을 발급받음
} catch (error) {
console.error('tokenProvider 호출 실패:', error);
}동작 방식
- 토큰 유효성 검사: 현재 Access Token의 만료 시간 확인
- 자동 갱신:
- V2 — 만료 5분 전이면 Refresh Token으로 자동 갱신
- V3 — 만료
refreshThresholdSec초 전(기본 300)이면tokenProvider재호출 (동시 호출은 싱글플라이트로 1회만 실행)
- 이벤트 발생: 갱신 성공 시
tokenRefreshed이벤트 발생 - 에러 처리: 갱신 실패 시
authError이벤트 발생 후 자동 로그아웃
자동 갱신 흐름
getValidAccessToken() 호출
│
▼
토큰 존재 확인 ──No──▶ authError 이벤트 + 에러 throw
│
Yes
▼
만료 5분 전? ──No──▶ 현재 토큰 반환
│
Yes
▼
refreshToken() 호출
│
├─Success─▶ tokenRefreshed 이벤트 + 새 토큰 반환
│
└─Failure─▶ authError 이벤트 + logout 이벤트 + 에러 throw수동 토큰 갱신
필요한 경우 수동으로 토큰을 갱신할 수 있습니다.
const result = await sdk.auth.v2.refreshToken();
if (result.success) {
console.log('새 토큰:', result.accessToken);
} else {
console.error('갱신 실패:', result.error);
// 갱신 실패 시 자동으로 logout 이벤트가 발생합니다
}Storage Keys
SDK가 내부적으로 사용하는 Storage 키입니다. 모두 abc_waas_ prefix를 공유하며,
sdk.logout()은 이 prefix 범위만 정리해 호스트 앱의 다른 localStorage 데이터를
보존합니다.
| Key | Description |
|-----|-------------|
| abc_waas_shares | MPC 쉐어 번들 (curve별 ShareData를 JSON으로 묶어 저장) |
| abc_waas_auth_token | V2 Access Token |
| abc_waas_refresh_token | V2 Refresh Token |
| abc_waas_user | V2 사용자 정보 |
V3는 토큰을 Storage에 저장하지 않습니다. 새로고침 시
tokenProvider가 다시 호출되어 메모리 캐시를 복구합니다.
Error Handling
SDK는 WaasError 클래스를 통해 에러를 처리합니다.
import { WaasError, WaasErrorCode } from '@abcwalletio/core';
try {
await sdk.mpc.generateKeyShare('secp256k1', 'password');
} catch (error) {
if (error instanceof WaasError) {
console.log('Code:', error.code);
console.log('Message:', error.message);
console.log('Details:', error.details);
switch (error.code) {
case WaasErrorCode.AUTH_FAILED:
// 인증 실패 처리
break;
case WaasErrorCode.MPC_SHARE_NOT_FOUND:
// Share 없음 처리
break;
// ...
}
}
}API 레벨 에러 처리
SDK는 HTTP 200 응답이라도 응답 body에 에러 코드가 포함된 경우 자동으로 감지하여 처리합니다.
API 에러 응답 형식
서버에서 반환하는 API 레벨 에러 응답 형식:
interface ApiErrorResponse {
code: string; // "103", "104" 등 문자열
message: string; // "JWT_EXPIRED", "INVALID_TOKEN" 등
}지원하는 API 에러 코드
| API 에러 코드 | 메시지 | SDK 에러 코드 | 설명 |
|--------------|--------|--------------|------|
| 103 | JWT_EXPIRED | AUTH_EXPIRED | JWT 토큰 만료 - 자동 갱신 시도 |
| 104 | INVALID_TOKEN | AUTH_INVALID_TOKEN | 유효하지 않은 토큰 |
HTTP 상태 코드 기반 자동 갱신
| HTTP 상태 코드 | SDK 에러 코드 | 설명 |
|---------------|--------------|------|
| 401 Unauthorized | AUTH_EXPIRED | 인증 만료 - 자동 갱신 시도 |
| 403 Forbidden | AUTH_FAILED | 접근 권한 없음 - 자동 갱신 시도 |
HTTP 401/403 응답 시에도 JWT 만료와 동일하게 토큰 갱신을 시도합니다.
JWT 만료 시 자동 재시도
JWT가 만료된 경우(code: "103"), SDK는 자동으로 다음 과정을 수행합니다:
- Refresh Token을 사용하여 새로운 Access Token 발급 시도
- 갱신 성공 시 원래 요청을 새 토큰으로 1회 재시도
- 갱신 실패 시
AUTH_EXPIRED에러 발생 및authError이벤트 emit
API 요청 → HTTP 200 + { code: "103" } 응답
│
▼
토큰 갱신 시도 (refreshToken)
│
├─ 성공 → 원래 요청 재시도 (새 토큰으로)
│ │
│ ├─ 성공 → 정상 결과 반환
│ └─ 실패 → 에러 throw
│
└─ 실패 → authError 이벤트 + AUTH_EXPIRED 에러 throw에러 처리 예시
import { WaasError, WaasErrorCode } from '@abcwalletio/core';
// authError 이벤트 구독
sdk.on('authError', (payload) => {
if (payload.error.code === WaasErrorCode.AUTH_EXPIRED) {
// 토큰 만료로 인한 갱신 실패
// 로그인 페이지로 이동 등의 처리
console.log('세션이 만료되었습니다. 다시 로그인해주세요.');
}
});
try {
const walletToken = await sdk.wallet.getWalletToken(keyId);
} catch (error) {
if (error instanceof WaasError) {
switch (error.code) {
case WaasErrorCode.AUTH_EXPIRED:
// JWT 만료 및 갱신 실패
break;
case WaasErrorCode.AUTH_INVALID_TOKEN:
// 유효하지 않은 토큰
break;
}
}
}Error Codes
| Category | Code Range | Description | |----------|------------|-------------| | AUTH | 1000-1999 | 인증 관련 에러 | | WALLET | 2000-2999 | 지갑 관련 에러 | | MPC | 3000-3999 | MPC 연산 에러 | | STORAGE | 4000-4999 | Storage 에러 | | NETWORK | 5000-5999 | 네트워크 에러 | | PROVIDER | 6000-6999 | Provider 에러 | | WASM | 7000-7999 | WASM 에러 |
Configuration
Environment
SDK는 프로덕션 환경 (https://api.waas.myabcwallet.com)에 고정됩니다.
environment는 공개 설정 필드가 아닙니다.
TypeScript Types
import type {
// SDK
WaasSDK,
WaasSDKConfig,
Environment,
CurveType,
// Auth
AuthModule,
AuthProvider,
User,
AuthResult,
// MPC
MpcModule,
GenerateShareResult,
RecoverShareResult,
SignResult,
// Wallet API
WalletApiModule,
WalletInfo,
WalletKey,
WalletUser,
// Storage
StorageInterface,
AsyncStorageInterface,
AsyncBridgeHandler,
BridgeStorageOptions,
// Error
WaasError,
WaasErrorCode,
} from '@abcwalletio/core';License
MIT — Copyright (c) 2026 Ahnlab Blockchain Company
