WalletKit API
WalletKit은 지갑 제공사가 자체 지갑 앱에 임베드하는 Kotlin Multiplatform SDK입니다. 스캔한 scope: 페어링 URI로부터 dApp과의 세션을 수립하고, 사용자 승인을 거쳐 EIP-712·EIP-3009 서명을 릴레이로 응답합니다. 이 API Reference는 현재 배포된 WalletKit에서 지갑 앱이 사용할 수 있는 공개 API만 설명합니다.
현재 버전은 0.0.1입니다. iOS 패키지는 공개 Swift Package로 배포됩니다. Android·JVM 패키지는 아직 Maven Central에 게시하지 않았으며, 파트너 프리뷰 기간에는 SCOPE Connect 담당자를 통해 아티팩트를 전달합니다. 플랫폼별 설치 방법은 설치에서 다룹니다.
API 개요
| 영역 | 범위 |
|---|---|
| 세션 | WalletKit 클래스, 사용자 승인, Signer, PairingUri |
| 프로토콜 구성 요소 | 릴레이 전송, 세션 메시지 형식, 채널 암호화, 키 합의, EIP-712, 서명 |
| 암호화·진단 유틸리티 | 암호 제공자, 바이트·해시 유틸리티, 로깅 |
지원 플랫폼과 배포 형태
| 대상 | 요구 사항 | 배포 상태 |
|---|---|---|
| Android | API 28 이상, compile SDK 35 | 0.0.1 Maven 아티팩트 파트너 전달 |
| iOS | iOS 16 이상, arm64·simulator arm64 XCFramework | 공개 Swift Package 0.0.1 |
| JVM | JDK 21 | 0.0.1 Maven 아티팩트 파트너 전달 |
모든 네트워크 suspend 함수는 Kotlin 비동기 작업(코루틴)의 취소를 호출자에게 전달합니다. 아래에 명시한 RelayRpcException 외에도 Ktor, TLS, WebSocket, 플랫폼 예외나 CancellationException이 전달될 수 있습니다.
Swift (iOS) API
이 문서의 함수와 타입 정의는 Kotlin 기준입니다. XCFramework를 통해 Swift에서는 다음 규칙으로 노출됩니다. Swift 통합 예시는 세션 연결과 승인 단계의 Swift 탭에서 확인합니다.
| Kotlin | Swift |
|---|---|
@Throws 선언된 suspend 함수 | async throws — try await로 호출 |
suspend 함수형 파라미터 | KotlinSuspendFunction1 프로토콜 — NSObject를 상속한 클래스로 구현 |
sealed 계층 (ApprovalRequest.Connection 등) | 평탄화된 클래스 이름 (ApprovalRequestConnection) |
companion 멤버 (ApprovalDecision.APPROVE) | .companion 접근 (ApprovalDecision.companion.APPROVE) |
object 싱글턴 | .shared 접근 (Secp256k1Signer.shared) |
ByteArray | KotlinByteArray |
| 예외 | NSError로 래핑 — userInfo["KotlinException"]으로 원본 복원 |
| 생성자 기본 인자 | 브릿징되지 않음 — WalletKitConfig 구성은 공유 Kotlin 모듈에 둡니다 |
Kotlin Flow는 Swift에 노출되지 않으므로 RelayTransport.incoming 같은 Flow 기반 스트림은 공유 Kotlin 모듈에서 다룹니다.
WalletKit 클래스
페어링 URI 파싱부터 릴레이 연결, 키 합의, 사용자 승인과 서명 응답까지 처리하는 클래스입니다.
- Kotlin (Android · JVM)
- Swift (iOS)
public class WalletKit(private val config: WalletKitConfig)
class WalletKit {
init(config: WalletKitConfig)
}
WalletKitConfig
- Kotlin (Android · JVM)
- Swift (iOS)
public data class WalletKitConfig(
val metadata: WalletMetadata,
val handshakeTimeoutMs: Long = 20_000L,
val signRequestTimeoutMs: Long = 300_000L,
val userHash: String? = null,
val transportFactory: (relayUrl: String, userHash: String?) -> RelayTransport =
{ url, dev -> KtorRelayTransport(url, userHash = dev) },
)
public data class WalletMetadata(val name: String)
// 기본 인자가 브릿징되지 않아 모든 파라미터를 명시해야 합니다.
class WalletKitConfig {
init(
metadata: WalletMetadata,
handshakeTimeoutMs: Int64,
signRequestTimeoutMs: Int64,
userHash: String?,
transportFactory: @escaping (String, String?) -> RelayTransport
)
var metadata: WalletMetadata { get }
var handshakeTimeoutMs: Int64 { get }
var signRequestTimeoutMs: Int64 { get }
}
class WalletMetadata {
init(name: String)
}
| 필드 | 기본값 | 설명 |
|---|---|---|
metadata | 필수 | dApp의 연결 승인 화면에 표시할 지갑 정보. 현재는 name 하나입니다 |
handshakeTimeoutMs | 20_000 | 세션 연결 각 단계의 대기 시간 |
signRequestTimeoutMs | 300_000 | 서명 요청 대기 시간. AppKit과 마찬가지로 사용자가 내용을 확인하고 승인할 수 있도록 5분간 기다립니다 |
userHash | null | 콘솔 분석용 설치 단위 식별값(선택). null이면 userHash를 전송하지 않습니다 — 대체 id를 생성하지 않고, 방문 비컨을 생략하며 릴레이 이벤트에도 사용자 식별값을 기록하지 않습니다 |
transportFactory | KtorRelayTransport | 테스트용 전송 구현을 주입하기 위한 팩토리 |
릴레이 WSS 엔드포인트는 설정 항목이 아닙니다. WalletKit 빌드 시점에 고정되며(RelayDefaults.DEFAULT_RELAY_URL, -PscopeRelayUrl / $SCOPE_RELAY_URL), 스캔한 페어링 URI의 relay-url보다 우선합니다. QR은 신뢰할 수 없는 입력이므로 지갑의 소켓 대상을 바꿀 수 없어야 하기 때문입니다. 두 값이 다르면 연결은 빌드 값으로 진행되고 그 사실이 WalletSessionResult.steps에 기록됩니다.
Kotlin 생성자 기본 인자는 Swift로 브릿징되지 않아 Swift에서 만들려면 모든 인자를 명시해야 하고, 기본 transportFactory는 Swift에서 재구성할 수 없습니다. iOS는 인스턴스 구성을 공유 Kotlin 모듈에 두고 Swift에서 호출합니다. 자세한 구성은 설치의 iOS 안내에서 다룹니다.
플랫폼 정보는 릴레이 발행 요청에 항상 포함됩니다. userHash를 지정하면 사용자 식별값도 암호화되지 않은 릴레이 메타데이터로 전송되고 방문 이벤트가 활성화됩니다(E2E 본문은 계속 암호화되어 릴레이가 내용을 읽을 수 없습니다). 기본값 null에서는 userHash와 방문 이벤트만 생략됩니다. 이전 버전이 만들던 프로세스 단위 UUIDv4 대체 ID는 제거되었습니다. 값을 지정할 때는 운영 적용 전 수집 목적, 동의, 보존 기간과 비활성화 정책을 확정합니다.
connect()
- Kotlin (Android · JVM)
- Swift (iOS)
@Throws(Throwable::class)
public suspend fun connect(pairingUri: String, signer: Signer): WalletSessionResult
func connect(pairingUri: String, signer: Signer) async throws -> WalletSessionResult
스캔한 scope: 페어링 URI로부터 다음 순서로 진행합니다.
- URI를 파싱하고 인증 없이 WSS 릴레이에 연결합니다.
pairingTopic에 참여합니다. 토픽 미생성(2403)이면 250ms 간격으로 최대 60회 재시도합니다.- 태그 1100
scope_sessionPropose를 수신하면 연결 승인을 받습니다. - X25519 ECDH로 세션 키를 합의하고 태그 1101로 응답합니다.
sessionTopic은 세션 키의 SHA-256입니다. sessionTopic을 구독하고 태그 1102scope_sessionSettle로 계정을 전송합니다.- 태그 1108
scope_sessionRequest(EIP-3009 구조화 데이터)를 수신하면 서명 승인을 받습니다. - 승인 시
signer.signRecoverable(digest)결과를 태그 1109{result}로, 거절 시{error: {code: 4001}}로 응답합니다.
한 번의 connect()는 서명 요청 한 건을 처리하고 종료됩니다. 성공·거절·예외 어느 경로든 반환 전에 열었던 전송을 닫으며, 추가 요청은 새 페어링 URI로 connect()를 다시 호출해 처리합니다. 연결 거절 시에는 dApp에 응답을 발행하지 않고 반환합니다.
세션 연결·서명 대기가 제한 시간을 초과하면 TimeoutCancellationException(CancellationException의 하위 타입)이, 2403 재시도 기간이 끝나면 IllegalStateException이 전파됩니다.
Swift에는 async throws 함수로 노출되어 try await로 호출합니다. @Throws(Throwable::class) 선언은 Swift에서 try/catch로 잡히도록 하기 위한 것입니다. 미선언 시 KMP 예외가 iOS 앱을 종료시킵니다.
setApprovalHandler()
- Kotlin (Android · JVM)
- Swift (iOS)
public fun setApprovalHandler(handler: suspend (ApprovalRequest) -> ApprovalDecision)
// suspend 함수형 파라미터는 KotlinSuspendFunction1 프로토콜로 노출됩니다.
func setApprovalHandler(handler: KotlinSuspendFunction1)
지갑 앱의 연결·서명 승인 UI를 연결하는 suspend 콜백을 등록합니다. 핸들러는 인스턴스 전역 상태로 등록 즉시 교체됩니다. connect() 흐름을 시작하기 전에 등록해야 하며, 진행 중에는 교체하지 않습니다.
Swift에서는 suspend 함수형 파라미터가 KotlinSuspendFunction1 프로토콜로 노출되어 Swift 클로저를 직접 전달할 수 없습니다. NSObject를 상속한 클래스를 구현해 등록하며, 구체 코드는 WalletKit 승인과 승인 단계의 Swift 탭에서 다룹니다.
핸들러를 등록하지 않으면 모든 연결과 서명 요청이 자동 승인됩니다. 운영 지갑은 반드시 사용자 확인 UI를 거치는 핸들러를 등록합니다.
WalletSessionResult
- Kotlin (Android · JVM)
- Swift (iOS)
public data class WalletSessionResult(
val sessionTopic: String,
val account: String,
val signatureHex: String,
val approved: Boolean,
val steps: List<String>,
)
class WalletSessionResult {
var sessionTopic: String { get }
var account: String { get }
var signatureHex: String { get }
var approved: Bool { get }
var steps: [String] { get }
}
| 필드 | 설명 |
|---|---|
sessionTopic | 연결된 세션 토픽. 거절 시 빈 문자열 |
account | 세션에 등록된 계정 주소 |
signatureHex | 65바이트 서명의 16진수 문자열(130자, 0x 접두사 없음 — 릴레이 1109 응답에만 0x가 붙습니다). 거절 시 빈 문자열 |
approved | 사용자 승인 여부 |
steps | 단계별 처리 기록. 디버깅용 |
Swift에서는 같은 프로퍼티를 가진 클래스로 노출되어 result.approved, result.signatureHex를 그대로 읽습니다.
관련 API
| 영역 | 문서 |
|---|---|
| 연결·서명 승인 | WalletKit Approval |
| Signer와 페어링 URI | WalletKit Signer & Pairing |
| 릴레이 전송 인터페이스 | WalletKit Relay Transport |
| 태그별 세션 메시지 형식 | WalletKit Session Messages |
| 채널 암호화와 EIP-712 | WalletKit Cryptography |
| 예외와 제한 시간 | WalletKit 오류 처리 |