AppKit 타입
핵심 타입
AppKitOptions
interface AppKitOptions {
connectors?: IWalletConnector[];
clientId: string;
solanaMobileSession?: SolanaMobileSession;
solanaMobileStore?: SolanaMobileStore;
solanaMobileOpenLink?: (url: string) => void;
metaMask?: MetaMaskSdkOptions;
relayFetch?: typeof fetch;
relayWebSocketFactory?: (url: string) => WebSocketLike;
userHash?: string;
telemetry?: boolean | {
enabled?: boolean;
flushIntervalMs?: number;
batchSize?: number;
fetchImpl?: typeof fetch;
userHash?: string;
platform?: string;
};
genericWalletSession?: boolean | GenericWalletSessionOptions;
universalLinkOpenLink?: (url: string) => void;
universalLinkDappName?: string;
universalLinkStore?: UniversalLinkStore;
}
// relayWebSocketFactory가 반환해야 하는 최소 구조
interface WebSocketLike {
send(data: string): void;
close(code?: number, reason?: string): void;
onopen: ((event: unknown) => void) | null;
onmessage: ((event: { data: unknown }) => void) | null;
onclose: ((event: { code: number; reason: string }) => void) | null;
onerror: ((event: unknown) => void) | null;
}
| 필드 | 기본값·제약 |
|---|---|
connectors | InjectedConnector 하나 |
clientId | 필수 — 공개 X-App-Key(프로젝트별로 콘솔에서 발급). 없거나 공백이면 생성자가 INVALID_CONFIG. 잔액·릴레이·텔레메트리·제네릭 지갑 세션(genericWalletSession) 요청에서 프로젝트를 인증하는 값입니다. 백엔드 루트는 빌드 시점에 고정(DEFAULT_CONNECT_BASE_URL)되어 옵션이 아닙니다 |
solanaMobileSession | 생략하면 운영 지갑 연결이 아닌 오프라인 모의 세션 사용 |
solanaMobileStore | 생략하면 데이터를 저장하지 않습니다. 모바일 복귀를 지원하려면 영속 저장소 제공 — 브라우저용 localStorage 구현은 SDK가 createBrowserSolanaMobileStore() 함수로 제공합니다 |
solanaMobileOpenLink | 생략하면 현재 문서를 universal link로 이동 |
metaMask | MetaMask SDK 설정 — dappName·dappUrl이 MetaMask 연결 화면에 표시됩니다. 선택 의존성 @metamask/sdk는 처음 사용할 때 불러옵니다 |
relayFetch | 릴레이 토큰 요청용 fetch. 기본값은 전역 fetch |
relayWebSocketFactory | 릴레이 WebSocket 팩토리. 주로 테스트·플랫폼 어댑터에서 사용 |
userHash | 선택 — 텔레메트리 이벤트에 부착되는 사용자 식별값. dApp이 값을 결정해 전달합니다(아래 참고). 생략하거나 공백을 넘기고 telemetry.userHash도 지정하지 않으면 사용자 식별값을 전송하지 않습니다. 사용자 단위 조회가 필요하고 자체 식별자가 없으면 getOrCreateUserHash()를 사용 |
telemetry | 기본 비활성 — 생략하면 아무것도 전송하지 않습니다. true 또는 옵션 객체를 넘기면 활성화되고, false 또는 { enabled: false }는 계속 비활성. 옵션 객체의 userHash·platform은 각각 최상위 userHash·자동 감지 플랫폼보다 우선합니다 |
genericWalletSession | 기본 off. injected/EVM 지갑의 connect+sign을 Connect 백엔드(generic_wallet_requests)에 서버 검증 기록으로 남깁니다. 켜면 connect()가 세션 생성·connect 기록 후 personal_sign 소유 증명 1회를 요청·기록합니다. 기록이 실패하거나 사용자가 거절해도 지갑 연결 결과에는 영향을 주지 않습니다. 프로젝트 인증에는 생성자의 clientId를 사용하며, 릴레이·Phantom 모바일 커넥터는 영향받지 않습니다 |
universalLinkOpenLink | 유니버설 링크 지갑을 여는 함수를 교체합니다. 생략하면 현재 문서의 위치를 지갑 링크로 이동합니다 |
universalLinkDappName | 유니버설 링크 지갑의 승인 화면에 표시할 dApp 이름입니다 |
universalLinkStore | 앱 전환 중 페이지가 다시 로드되어도 연결·서명 상태를 복구하기 위한 저장소입니다. 브라우저에서는 createBrowserUniversalLinkStore()를 사용할 수 있습니다 |
엔드포인트는 AppKitOptions에 없습니다. Connect 백엔드 루트(DEFAULT_CONNECT_BASE_URL)와 릴레이 WSS 엔드포인트(DEFAULT_RELAY_URL)는 모두 SDK 빌드 시점에 고정됩니다 — 백엔드 호출은 전자를, pair()는 후자를 소켓 대상과 페어링 URI의 relay-url에 함께 사용합니다. dApp이 런타임에 다른 백엔드·릴레이를 지정할 수 없습니다. 패키지는 두 값을 읽기 전용 상수로 제공하므로 CSP connect-src 등에 사용할 수 있습니다.
텔레메트리 객체의 기본값은 flushIntervalMs: 2000, batchSize: 10입니다. fetchImpl은 전역 fetch를 사용하며 사용할 수 없으면 이벤트 전송을 비활성화합니다. telemetry.userHash와 telemetry.platform을 지정하면 각각 최상위 userHash와 자동 감지 플랫폼을 덮어씁니다. 전송될 수 있는 값의 범위는 AppKit 생성자의 텔레메트리 주의사항을, 로컬 저장소 동작은 아래 **사용자 식별값(userHash)**을 확인합니다.
사용자 식별값(userHash)
userHash는 선택 옵션입니다 — dApp이 값을 결정해 전달하며, SDK는 대체 ID를 생성하지 않습니다. 전달하면 AppKit이 그 값을 모든 텔레메트리 이벤트에 그대로 부착합니다(콘솔은 이 값으로 유니크/활성/신규 지표를 집계). telemetry.userHash도 지정한 경우에는 그 값이 최상위 userHash보다 우선합니다.
최상위 userHash를 생략하거나 공백으로 넘기고 telemetry.userHash도 지정하지 않으면 사용자 식별값을 전송하지 않습니다. 이때 전송 데이터에서 userHash 키 자체가 빠집니다. 연결·서명 동작은 동일하고, 콘솔에서 그 요청들이 다음과 같이 보입니다.
| 콘솔 화면 | userHash를 전달했을 때 | 전달하지 않았을 때 |
|---|---|---|
| Users(사용자 명단) | 사용자 1행으로 집계 | 행이 생기지 않음 |
| Analytics 사용자 지표 | 유니크/활성/신규에 포함 | 집계에서 제외 |
| Transactions·Signatures·Screening | User 열에 값 표시, 클릭 시 사용자 상세 | User 열이 미보고로 표시되고 사용자 조회 불가(빈 값으로 조회되지 않음) |
과거에는 SDK가 설치 단위 deviceId(랜덤 UUID)를 자동 생성했고, 이후 한동안 생략 시 저장된 설치 단위 id를 대신 사용했습니다. 두 동작 모두 사라졌습니다 — 이제 전송 여부는 전적으로 dApp이 결정합니다.
userHash에는 개인정보를 포함하지 않는 임의의 식별값을 사용합니다 — 이름·생년월일 같은 개인정보나 지갑·계정 데이터를 그대로 사용하거나 해시하지 않습니다. 사용자 단위 집계는 원하지만 자체 식별값이 없는 dApp은 SDK 함수 getOrCreateUserHash()를 사용합니다 — 설치 단위 임의 ID(CSPRNG 32바이트 → 64자 hex)를 localStorage에 한 번 생성·저장하고 이후 재사용합니다.
import { getOrCreateUserHash } from "@scope-connect/appkit";
// 저장된 설치 단위 임의 ID (없으면 생성·저장) — 브라우저에서는 항상 값이 있습니다
const appKit = new AppKit({ clientId: "ck_public_example", userHash: getOrCreateUserHash() });
// dApp이 자체 사용자 식별자를 쓰고 싶다면 그 값을 그대로 넘겨도 됩니다(개인정보 금지).
const appKitWithOwnId = new AppKit({ clientId: "ck_public_example", userHash: myOpaqueUserId });
// 사용자 단위 집계가 필요 없다면 아예 생략합니다 — 연결·서명 동작에는 영향이 없습니다.
const appKitWithoutUser = new AppKit({ clientId: "ck_public_example" });
| 함수 | 반환 |
|---|---|
getOrCreateUserHash() | 저장된 설치 단위 임의 ID 64자 hex(없으면 생성·저장). undefined는 WebCrypto(crypto.getRandomValues)가 없는 제한 환경에서만 반환되며, 그 값을 그대로 userHash에 넘겨도 됩니다(userHash 미보고로 처리). localStorage가 없는 환경(SSR 포함)에서는 저장 없이 호출마다 새 값을 반환하므로, 반드시 브라우저(클라이언트)에서 호출해 AppKit을 생성합니다 |
콘솔은 64자 hex와 36자 UUID를 모두 받아들이므로, 이전 SDK 버전이 저장해 둔 UUID id는 그대로 유지되어 사용자 이력이 끊기지 않습니다.
이메일·전화번호·이름·생년월일 등 개인정보나 그 해시를 userHash로 사용하지 않습니다. 콘솔은 이 값을 유니크/활성/신규 지표 집계에만 사용합니다.
WebSocketLike와 텔레메트리 옵션 객체는 패키지 루트에서 별도 타입으로 제공되지 않습니다. 일반 dApp은 해당 주입 옵션을 생략하고, 사용자 정의 플랫폼 통합에서는 위 구조에 맞춰 값을 제공합니다.
AppKitConfig
type AppKitConfig = Record<string, never>;
현재는 예약된 빈 타입입니다. 과거의 chains 허용 목록은 더 이상 init()에 전달하지 않고, loadConfig()가 프로젝트 콘솔 설정을 백엔드에서 가져와 적용합니다. 허용 목록은 연결과 전환의 최종 체인에만 적용되며 잔액 읽기에는 적용되지 않습니다.
WalletConnection
WalletConnection은 connect()·selectWallet()·pair() 등 어떤 경로로 연결하든 성공 시 반환되는 연결 결과 객체입니다. 호출 경로와 관계없이 아래 필드로 계정과 체인을 같은 방식으로 읽습니다.
interface WalletConnection {
connectorId: string;
accounts: string[];
account: Caip10AccountId;
chainId: Caip2ChainId;
transport?: "injected" | "relay" | "connect-service" | "a2a";
walletId?: string;
expiry?: number;
}
accounts는 원시 주소이고 account는 기본 계정의 CAIP-10 값입니다. transport는 연결 경로를 나타냅니다 — injected(EIP-1193/주입형 — MetaMask SDK 프로바이더 포함), relay(WSS 페어링), connect-service(Phantom 모바일 백엔드), a2a(Klip App2App). 내장 커넥터는 모두 값을 설정하며, 이 필드가 없는 경우는 직접 구현한 외부 커넥터뿐입니다.
walletId는 연결에 사용한 지갑 ID입니다(예: metamask). selectWallet()은 선택한 ID를 항상 기록하고, connect()는 커넥터 ID로 지갑 ID를 확인할 수 있을 때만 채웁니다(walletIdForConnectorId 참고). 릴레이 연결에는 현재 세션 승인 응답에 지갑 ID가 포함되지 않아 이 값이 없습니다. expiry는 지갑이 세션 만료 시각(epoch 초)을 보낸 경우에만 있으며 transport: "relay" 연결 전용입니다.
RequestArgs와 Eip1193Provider
interface RequestArgs {
method: string;
params?: unknown[];
chainId?: Caip2ChainId;
}
interface Eip1193Provider {
request<T = unknown>(args: {
method: string;
params?: unknown[] | object;
}): Promise<T>;
}
PairOptions와 PairResult
type CaipNamespaces = Record<string, {
chains?: string[];
methods?: string[];
events?: string[];
}>;
interface DappMetadata {
name: string;
url?: string;
icons?: string[];
}
interface PairOptions {
requiredNamespaces: CaipNamespaces;
optionalNamespaces?: CaipNamespaces;
metadata?: DappMetadata;
}
interface PairResult {
uri: string;
qr: string;
deepLink: string;
topic: string;
timeoutMs: number;
session: Promise<WalletConnection>;
cancel: () => Promise<void>;
}
qr(QR 표시용)과 deepLink(동일 기기 모바일용)는 현재 uri와 같은 값입니다. topic은 이 페어링을 식별하는 16진수 토픽입니다. timeoutMs는 지갑 응답을 기다리는 시간(기본 300,000ms)이며 사실상 QR의 유효 시간입니다 — 카운트다운을 그리고, 지나면 pair()를 다시 호출해 새 QR을 받습니다. cancel()은 진행 중인 페어링을 중단합니다(릴레이 소켓을 닫고 session이 거부됨; 여러 번 호출해도 안전). 세션이 이미 연결된 뒤에는 아무 것도 하지 않습니다 — 연결된 세션은 disconnect()로 종료합니다. 페어링 URI, 토픽과 채널 정보는 비밀값으로 취급하고 로그에 남기지 않습니다.
EIP-712와 EIP-3009 타입
interface Eip712TypedData {
types: Record<string, { name: string; type: string }[]>;
primaryType: string;
domain: {
name?: string;
version?: string;
chainId?: number;
verifyingContract?: Address;
salt?: Hex;
};
message: Record<string, unknown>;
}
interface Eip3009TypedData extends Eip712TypedData {
primaryType: "TransferWithAuthorization";
}
interface TransferWithAuthorizationMessage {
from: Address;
to: Address;
value: string;
validAfter: string;
validBefore: string;
nonce: Hex;
}
interface Eip3009Authorization {
signature: Hex;
r: Hex;
s: Hex;
v: number;
message: TransferWithAuthorizationMessage;
chainId: Caip2ChainId;
verifyingContract: Address;
}
value·validAfter·validBefore는 uint256 10진 문자열이고 시간 값은 unix seconds, nonce는 bytes32입니다. signature는 0x 접두사가 붙은 65바이트 값입니다.
SolanaSignedTransaction
interface SolanaSignedTransaction {
signatures: (Hex | null)[];
signedTransaction: string | null;
userSignature: Hex | null;
}
signatures는 트랜잭션 서명자(fee payer 우선) 순서의 슬롯별 값입니다. relayer가 slot 0을 partial-sign한 가스리스 트랜잭션에서는 사용자 서명이 slot 1입니다. userSignature는 연결된 지갑 자신의 서명입니다 — SDK가 서명자 키를 연결 계정과 대조해 찾고, 키를 알 수 없으면 마지막 null이 아닌 슬롯을 사용합니다. 데스크톱·모바일 어느 경로든 같은 필드이므로 사용자 서명만 필요한 dApp은 슬롯 규칙 없이 이 값을 읽습니다 — signSolanaTransaction을 참고합니다.
TokenBalanceQuery와 TokenBalance
interface TokenBalanceQuery {
chainId: Caip2ChainId;
tokenAddress: string;
account: string;
decimals: number;
}
interface TokenBalance {
raw: bigint;
decimals: number;
chainId: Caip2ChainId;
}
CAIP 타입과 함수
type Address = `0x${string}`;
type Hex = `0x${string}`;
type Caip2ChainId = string; // 예: "eip155:11155111"
type Caip10AccountId = string; // 예: "eip155:11155111:0xabc..."
toEip155Caip2(chainId: number): Caip2ChainId
toCaip10(chainId: Caip2ChainId, address: string): Caip10AccountId
parseEip155ChainId(chainId: Caip2ChainId): number | undefined
parseEip155ChainId는 eip155: 체인이 아니면 undefined를 반환합니다.
지갑과 네트워크 정보 타입
type WalletId =
| "metamask" | "rainbow" | "kaia" | "klip" | "keplr"
| "phantom" | "solflare" | "scope" | "okx";
interface SupportWalletInfo {
walletId: WalletId;
displayName: string;
icon?: string;
primaryNamespace?: ChainNamespace;
eip155?: Eip155WalletInfo;
solana?: SolanaWalletInfo;
}
interface NetworkInfo {
chainId: Caip2ChainId;
namespace: ChainNamespace;
evmChainId?: number;
name: string;
displayName: string;
isTestnet: boolean;
icon?: string;
addEthereumChainParams?: AddEthereumChainParameter;
}
지원 여부는 정적 숫자보다 현재 정보 조회 함수의 결과를 사용합니다.
icon은 <img src>에 그대로 넣을 수 있는 값(data: 또는 http(s) URL)입니다. 백엔드는 아이콘을 저장된 형태(원본 <svg> 마크업 또는 data URL)로 내려주므로, loadConfig가 이를 정규화한 뒤 지갑·네트워크 정보 항목에 채웁니다. 지갑은 프로젝트 콘솔에 아이콘이 설정돼 있으면 SDK 내장 브랜드 마크를 대체하고, 없으면 내장 마크를 유지합니다. 위 WalletId 목록에 없는 지갑도 SDK가 브랜드 마크를 포함하고 있으면(coinbase, subwallet, tronlink, xaman) 같은 규칙을 적용합니다. 그 외 지갑은 icon이 undefined일 수 있으므로 UI가 대체 이미지를 준비해야 합니다. 네트워크는 내장 아이콘이 없으며 프로젝트 콘솔에 값이 없을 수 있으므로 항상 대체 이미지를 준비합니다. 직접 받은 아이콘 문자열을 렌더링할 때는 패키지가 제공하는 toRenderableIcon 함수를 사용합니다.
addNetwork() 입력 타입
AppKit.addNetwork의 입력 타입입니다. 문자열이면 내장 네트워크 정보에서 찾은 SDK 기본값을 사용하고, 객체면 호출자가 준 EIP-3085 파라미터를 그대로 사용합니다. 두 타입 모두 패키지에서 직접 import할 수 있습니다.
type AddNetworkInput = Caip2ChainId | AddEthereumChainParameter;
interface AddEthereumChainParameter {
chainId: string; // 0x 접두, 앞자리 0 없는 16진수 (예: 0x164ce)
chainName: string;
nativeCurrency: { name: string; symbol: string; decimals: number };
rpcUrls: string[]; // 최소 1개, HTTPS (loopback만 http 허용)
blockExplorerUrls?: string[];
}
interface AddNetworkOptions {
switch?: boolean; // 등록 후 해당 체인으로 전환 (기본 false)
}