WalletKit 세션 메시지
세션 메시지 형식 — SessionWire
세션 연결과 요청 메시지를 생성·해석하는 API입니다. AppKit dApp SDK와 바이트 단위로 호환되며, 모든 페이로드는 UTF-8 JSON을 ChannelCrypto(ChaCha20-Poly1305)로 암호화해 전송합니다.
- Kotlin (Android · JVM)
- Swift (iOS)
public object SessionWire {
public const val RELAY_PROTOCOL: String = "scr"
public const val SIGN_METHOD: String = "eth_signTypedData_v4"
}
class SessionWire {
class var shared: SessionWire { get }
var RELAY_PROTOCOL: String { get }
var SIGN_METHOD: String { get }
}
relay-protocol 값 scr은 WalletConnect의 irn과 호환되지 않습니다. eth_signTypedData_v4는 현재 세션 요청이 지원하는 유일한 RPC 메서드입니다.
태그별 메시지 생성·해석 함수
| tag | 방향 | 빌더 | 파서 |
|---|---|---|---|
| 1100 propose | dApp → 지갑 | propose(publicKeyRaw, dappName) | proposePublicKey, requestedChains, dappName |
| 1101 propose 응답 | 지갑 → dApp | proposeResponse(publicKeyRaw) | responderPublicKey |
| 1102 settle | 지갑 → dApp | settle(controllerPublicKeyRaw, chains, account) | settledAddress |
| 1103 settle ack | dApp → 지갑 | settleAck() | — |
| 1108 서명 요청 | dApp → 지갑 | signRequest(from, typedDataJson, chainId) | signRequestTypedData |
| 1109 서명 응답 | 지갑 → dApp | signResponse(signatureHex), signRejection(code, message) | signResponseSignature |
생성 함수는 모두 암호화 전 UTF-8 JSON 바이트를 반환하고, 해석 함수는 복호화된 바이트를 받습니다. 지갑이 직접 호출하는 것은 1101·1102·1109 생성 함수와 1100·1108 해석 함수이며, 나머지는 dApp(AppKit)에서 사용합니다.
- Kotlin (Android · JVM)
- Swift (iOS)
// 1100 scope_sessionPropose
fun propose(publicKeyRaw: ByteArray, dappName: String): ByteArray
fun proposePublicKey(bytes: ByteArray): ByteArray
fun requestedChains(bytes: ByteArray): List<String>
fun dappName(bytes: ByteArray): String
// 1101 propose 응답
fun proposeResponse(publicKeyRaw: ByteArray): ByteArray
fun responderPublicKey(bytes: ByteArray): ByteArray
// 1102 settle · 1103 ack
fun settle(controllerPublicKeyRaw: ByteArray, chains: List<String>, account: String): ByteArray
fun settledAddress(bytes: ByteArray): String
fun settleAck(): ByteArray
// 1108 서명 요청
fun signRequest(from: String, typedDataJson: String, chainId: String): ByteArray
fun signRequestTypedData(bytes: ByteArray): String
// 1109 서명 응답
fun signResponse(signatureHex: String): ByteArray
fun signRejection(code: Int, message: String): ByteArray
fun signResponseSignature(bytes: ByteArray): ByteArray
// 모두 SessionWire.shared 를 통해 호출합니다.
// 1100 scope_sessionPropose
func propose(publicKeyRaw: KotlinByteArray, dappName: String) -> KotlinByteArray
func proposePublicKey(bytes: KotlinByteArray) -> KotlinByteArray
func requestedChains(bytes: KotlinByteArray) -> [String]
func dappName(bytes: KotlinByteArray) -> String
// 1101 propose 응답
func proposeResponse(publicKeyRaw: KotlinByteArray) -> KotlinByteArray
func responderPublicKey(bytes: KotlinByteArray) -> KotlinByteArray
// 1102 settle · 1103 ack
func settle(
controllerPublicKeyRaw: KotlinByteArray,
chains: [String],
account: String
) -> KotlinByteArray
func settledAddress(bytes: KotlinByteArray) -> String
func settleAck() -> KotlinByteArray
// 1108 서명 요청
func signRequest(from: String, typedDataJson: String, chainId: String) -> KotlinByteArray
func signRequestTypedData(bytes: KotlinByteArray) -> String
// 1109 서명 응답
func signResponse(signatureHex: String) -> KotlinByteArray
func signRejection(code: Int32, message: String) -> KotlinByteArray
func signResponseSignature(bytes: KotlinByteArray) -> KotlinByteArray
| 함수 | 설명 |
|---|---|
propose | publicKeyRaw는 32바이트 X25519 공개키. eip155:1 체인과 eth_signTypedData_v4 메서드를 필수 네임스페이스에 포함합니다 |
proposePublicKey | dApp의 32바이트 X25519 공개키. proposer·proposer.publicKey가 없거나 32바이트가 아니면 예외 |
requestedChains | required + optional namespace의 CAIP-2 체인을 중복 제거해 반환. 비어 있으면 ["eip155:1"] |
dappName | proposer.metadata.name. 없으면 예외가 아니라 "Unknown dApp"을 반환하므로 표시 전 확인이 필요합니다 |
proposeResponse | 지갑의 32바이트 X25519 공개키를 responderPublicKey로 실어 보냅니다 |
responderPublicKey | 지갑의 32바이트 공개키. 필드 누락이나 길이 불일치 시 예외 |
settle | chains를 CAIP-2 namespace 접두사로 묶어 각 체인에 {chain}:{account} CAIP-10 계정을 부여합니다. 이벤트는 accountsChanged |
settledAddress | 지갑이 승인한 첫 번째 계정의 주소만 추출(eip155:1:0x… → 0x…). namespaces 누락 또는 계정 0건이면 예외 |
settleAck | 릴레이 프로토콜만 담은 dApp 측 확인 응답. 인자 없음 |
signRequest | params는 [from, typedDataJson] 순서. method는 항상 eth_signTypedData_v4 |
signRequestTypedData | params[1]의 구조화 데이터 JSON. method가 eth_signTypedData_v4가 아니거나 params가 2개 미만이면 예외 |
signResponse | {"result": signatureHex}를 빌드합니다. signatureHex는 0x 접두사가 붙은 65바이트 서명 hex |
signRejection | {"error": {"code", "message"}} 응답을 만듭니다. 사용자 거절은 EIP-1193 4001을 사용하며, AppKit의 JSON-RPC 오류 응답 형식과 일치합니다 |
signResponseSignature | {result}에서 65바이트(r‖s‖v) 서명을 반환. {error} 응답이거나 result 누락, 65바이트 불일치면 예외 |
signResponse와 signRejection은 같은 태그 1109로 발행됩니다 — 성공과 거절을 태그가 아니라 본문(result 대 error)으로 구분합니다. 지갑 흐름에서 두 함수가 호출되는 지점은 세션 연결의 시퀀스에 표시되어 있습니다.
EIP-3009 구조화 데이터
- Kotlin (Android · JVM)
- Swift (iOS)
fun eip3009TypedData(
name: String,
version: String,
chainId: Long,
verifyingContract: String,
authz: TransferWithAuthorization,
): String
fun parseEip3009TypedData(typedDataJson: String): ParsedEip3009
data class ParsedEip3009(
val name: String,
val version: String,
val chainId: Long,
val verifyingContract: String,
val primaryType: String,
val authz: TransferWithAuthorization,
)
// SessionWire.shared 를 통해 호출합니다.
func eip3009TypedData(
name: String,
version: String,
chainId: Int64,
verifyingContract: String,
authz: TransferWithAuthorization
) -> String
func parseEip3009TypedData(typedDataJson: String) -> SessionWire.ParsedEip3009
class SessionWire.ParsedEip3009 {
var name: String { get }
var version: String { get }
var chainId: Int64 { get }
var verifyingContract: String { get }
var primaryType: String { get }
var authz: TransferWithAuthorization { get }
}
eth_signTypedData_v4의 params[1]에 들어가는 구조화 데이터 JSON을 생성·해석합니다. 지원하는 primaryType은 TransferWithAuthorization(가스리스 전송)과 ReceiveWithAuthorization(수신형 결제) 두 가지입니다.