WalletKit 승인
승인 단계
connect()는 두 지점에서 호스트 승인을 받습니다.
| 승인 단계 | 시점 | 거절 시 |
|---|---|---|
ApprovalRequest.Connection | dApp propose 수신 후, settle 전 | 세션을 수립하지 않고 approved = false로 반환합니다 (dApp에는 응답을 발행하지 않습니다) |
ApprovalRequest.Sign | 서명 직전 | dApp에 EIP-1193 4001 오류로 응답하고 approved = false로 반환합니다 |
두 SDK의 오류 코드 대응 관계는 오류 처리을 참고합니다.
ApprovalRequest와 ApprovalDecision
- Kotlin (Android · JVM)
- Swift (iOS)
public sealed interface ApprovalRequest {
public data class Connection(val info: ConnectionRequestInfo) : ApprovalRequest
public data class Sign(val info: SignRequestInfo) : ApprovalRequest
}
public data class ApprovalDecision(val approved: Boolean, val reason: String? = null) {
public companion object {
public val APPROVE: ApprovalDecision
public fun reject(reason: String): ApprovalDecision
}
}
// sealed 계층은 평탄화된 클래스 이름으로 노출됩니다.
protocol ApprovalRequest {}
class ApprovalRequestConnection: ApprovalRequest {
var info: ConnectionRequestInfo { get }
}
class ApprovalRequestSign: ApprovalRequest {
var info: SignRequestInfo { get }
}
class ApprovalDecision {
init(approved: Bool, reason: String?)
var approved: Bool { get }
var reason: String? { get }
class var companion: ApprovalDecision.Companion { get }
class Companion {
var APPROVE: ApprovalDecision { get }
func reject(reason: String) -> ApprovalDecision
}
}
reject(reason)의 reason은 호출한 앱 코드에서 쓰기 위한 문자열입니다 — dApp에 전달되거나 라이브러리가 기록하지는 않으며, 서명 거절 응답에는 고정 메시지와 4001 코드가 실립니다.
sealed 계층은 Swift에 평탄화된 클래스 이름으로 노출됩니다 — ApprovalRequest.Connection은 ApprovalRequestConnection, ApprovalRequest.Sign은 ApprovalRequestSign으로 캐스팅해 분기합니다. companion 멤버는 ApprovalDecision.companion.APPROVE·ApprovalDecision.companion.reject(reason:)으로 접근합니다.
ConnectionRequestInfo
- Kotlin (Android · JVM)
- Swift (iOS)
public data class ConnectionRequestInfo(
val dappName: String,
val chains: List<String>,
val pairingTopic: String,
)
class ConnectionRequestInfo {
var dappName: String { get }
var chains: [String] { get }
var pairingTopic: String { get }
}
| 필드 | 설명 |
|---|---|
dappName | proposer.metadata에 담긴 dApp 이름 |
chains | 요청된 CAIP-2 체인 목록 |
pairingTopic | 페어링 토픽 |
SignRequestInfo
디코딩된 EIP-3009 eth_signTypedData_v4 요청입니다. 필드는 표시용 문자열이며 금액은 토큰의 최소 단위로 표현된 정수입니다.
- Kotlin (Android · JVM)
- Swift (iOS)
public data class SignRequestInfo(
val account: String,
val chainId: String,
val method: String,
val tokenName: String,
val verifyingContract: String,
val from: String,
val to: String,
val value: String,
val validAfter: String,
val validBefore: String,
val nonce: String,
)
class SignRequestInfo {
var account: String { get }
var chainId: String { get }
var method: String { get }
var tokenName: String { get }
var verifyingContract: String { get }
var from: String { get }
var to: String { get }
var value: String { get }
var validAfter: String { get }
var validBefore: String { get }
var nonce: String { get }
}
| 필드 | 설명 |
|---|---|
account | 서명 계정 |
chainId | eip155:{n} 형식의 CAIP-2 체인 |
method | eth_signTypedData_v4 |
tokenName | EIP-712 domain.name |
verifyingContract | 서명 검증에 사용할 컨트랙트 주소 |
from, to | 전송 당사자 |
value | 토큰의 최소 단위로 표현된 uint256 정수 |
validAfter, validBefore | 유효 기간 |
nonce | bytes32 랜덤 nonce |
사용 예
- Kotlin (Android · JVM)
- Swift (iOS)
val wallet = WalletKit(WalletKitConfig(metadata = WalletMetadata("My Wallet")))
wallet.setApprovalHandler { request ->
when (request) {
is ApprovalRequest.Connection ->
if (userAllows(request.info.dappName)) ApprovalDecision.APPROVE
else ApprovalDecision.reject("user declined")
is ApprovalRequest.Sign ->
if (userConfirms(request.info)) ApprovalDecision.APPROVE
else ApprovalDecision.reject("user declined")
}
}
val result = wallet.connect(scannedUri, signer)
if (result.approved) submitElsewhere(result.signatureHex)
// 승인 핸들러: suspend 함수형 파라미터는 KotlinSuspendFunction1로 노출됩니다
final class ApprovalHandler: NSObject, KotlinSuspendFunction1 {
func invoke(p1: Any?) async throws -> Any? {
switch p1 {
case let request as ApprovalRequestConnection:
return await userAllows(request.info.dappName)
? ApprovalDecision.companion.APPROVE
: ApprovalDecision.companion.reject(reason: "user declined")
case let request as ApprovalRequestSign:
return await userConfirms(request.info)
? ApprovalDecision.companion.APPROVE
: ApprovalDecision.companion.reject(reason: "user declined")
default:
return ApprovalDecision.companion.reject(reason: "unknown request")
}
}
}
// wallet: 공유 Kotlin 모듈에서 구성한 WalletKit 인스턴스 (설치 문서의 iOS 안내 참고)
wallet.setApprovalHandler(handler: ApprovalHandler())
let result = try await wallet.connect(pairingUri: scannedUri, signer: signer)
if result.approved { submitElsewhere(result.signatureHex) }
승인 전에는 요청의 도메인, 체인 ID, 서명 검증에 사용할 컨트랙트와 모든 메시지 필드를 사용자에게 표시하고 검증합니다.