본문으로 건너뛰기

AppKit 선택적 API

이 문서는 기본 AppKit 클래스와 별도로, 필요할 때 선택적으로 사용하는 공개 함수, 클라이언트와 인터페이스를 설명합니다.

createBalanceReader(options)

연결 상태 없이 잔액 리더를 만듭니다. 조회는 지갑의 활성 체인과 무관하게 query.chainId가 가리키는 체인에서 수행됩니다. 리더는 공개 RPC를 직접 읽지 않고 Connect 백엔드(GET /api/v1/connect/balances, Nodit 연동)에 X-App-Key를 붙여 조회하므로 EVM·Solana 모두 백엔드 경유로 동작하며, 별도 RPC 엔드포인트나 @solana/web3.js peer가 필요하지 않습니다.

function createBalanceReader(options: BalanceReaderOptions): BalanceReader

interface BalanceReaderOptions {
baseUrl: string; // Connect 백엔드 기준 URL (끝의 "/"·"/api/v1"은 자동 정리)
appKey: string; // 공개 client_id — X-App-Key 헤더로 전송
fetchImpl?: typeof fetch; // 테스트·비브라우저 호스트용 fetch 주입
}

interface BalanceReader {
getBalance(query: TokenBalanceQuery): Promise<TokenBalance>;
}

options는 필수입니다. baseUrl·appKey가 없거나 fetch를 사용할 수 없으면 INVALID_CONFIG를 던집니다.

오류: 생성 시 INVALID_CONFIG(baseUrl·appKey 누락 또는 fetch 부재), 조회 시 UNSUPPORTED_CHAIN(백엔드가 지원하지 않는 체인), RPC_ERROR(전송 실패·비정상 응답·잘못된 본문).

import { createBalanceReader } from "@scope-connect/appkit";

const reader = createBalanceReader({
baseUrl: "https://scope-connect-be.example",
appKey: "ck_public_example",
});

const usdc = await reader.getBalance({
chainId: "eip155:84532",
tokenAddress: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
account: "0x0000000000000000000000000000000000000001",
decimals: 6,
});
// { raw: 1234560000n, decimals: 6, chainId: "eip155:84532" }

createConnectConfigClient(options)

프로젝트 콘솔에서 활성화한 네트워크·지갑 목록을 백엔드(GET /api/v1/connect/config)에서 직접 읽는 클라이언트입니다. AppKit.loadConfig()가 내부에서 사용하므로 대부분의 dApp은 직접 쓸 필요가 없고, 사용자 정의 목록 조회가 필요할 때 사용합니다.

function createConnectConfigClient(options: ConnectConfigClientOptions): ConnectConfigClient

interface ConnectConfigClientOptions {
baseUrl: string; // Connect 백엔드 기준 URL (끝의 "/"·"/api/v1"은 자동 정리)
appKey: string; // 공개 client_id — X-App-Key 헤더로 전송
fetchImpl?: typeof fetch; // 테스트·비브라우저 호스트용 fetch 주입
}

interface ConnectConfigClient {
fetchConfig(): Promise<ConnectConfig>;
}

interface ConnectConfig {
networks: ConnectConfigNetwork[]; // networkId(CAIP-2)로 키잉된 활성 네트워크
wallets: ConnectConfigWallet[]; // walletId(slug)로 키잉된 활성 지갑
}

baseUrl·appKey가 없거나 fetch를 사용할 수 없으면 생성 시 INVALID_CONFIG를 던집니다. fetchConfig()는 전송 실패·비정상 응답·잘못된 본문에서 RPC_ERROR를 던집니다.

지갑과 네트워크 정보 조회 함수

지갑 선택 UI는 내장 지갑·네트워크 정보를 복사해 두지 않고 다음 순수 함수로 조회합니다.

listSupportedWallets(options?: ListWalletsOptions): SupportWalletInfo[]
getWalletInfo(walletId: WalletId): SupportWalletInfo
listSupportedNetworks(): NetworkInfo[]
getNetworkInfo(chainId: Caip2ChainId): NetworkInfo
getNetworksForWallet(walletId: WalletId): NetworkInfo[]
getWalletsForNetwork(chainId: Caip2ChainId): SupportWalletInfo[]
isWalletNetworkSupported(walletId: WalletId, chainId: Caip2ChainId): boolean
listConnectableTargets(): ConnectableTarget[]

getWalletNamespaces(info: SupportWalletInfo)
walletSupportsNamespace(info: SupportWalletInfo, namespace: ChainNamespace): boolean
walletSupportsMobile(info: SupportWalletInfo): boolean
walletSupportsMobileOn(info: SupportWalletInfo, namespace: ChainNamespace): boolean
walletSupportsRelay(info: SupportWalletInfo): boolean
walletSupportsRelayOn(info: SupportWalletInfo, namespace: ChainNamespace): boolean

walletIdForConnectorId(connectorId: string): WalletId | undefined

알 수 없는 지갑은 WALLET_NOT_FOUND, 알 수 없는 체인은 UNSUPPORTED_CHAIN을 던집니다. 패키지는 supportWallets·supportNetworks 배열도 제공하지만, 지갑·네트워크 정보 조회에는 위 함수를 우선 사용합니다.

walletIdForConnectorId는 커넥터 id에 해당하는 지갑 ID를 찾습니다 — eip6963:{rdns}는 rdns로, solana:{walletId}는 내장 id로, 지갑 ID와 같은 값(metamask·klip)은 그대로 찾습니다. 지갑 목록의 항목과 연결할 수 없는 id(injected·relay)는 undefined를 반환합니다. WalletConnection.walletId가 이 규칙으로 채워집니다.

import {
getWalletsForNetwork,
listConnectableTargets,
} from "@scope-connect/appkit";

const wallets = getWalletsForNetwork("eip155:11155111");
const targets = listConnectableTargets();
console.info(wallets.length, targets.length);

커넥터와 IWalletConnector

사용자 정의 지갑 연결 방식은 IWalletConnector 인터페이스를 구현해 생성자 connectors에 등록합니다.

interface IWalletConnector {
readonly id: string; // 안정 커넥터 ID (예: "injected")
isAvailable(): boolean;
connect(): Promise<WalletConnection>;
request<T>(args: RequestArgs): Promise<T>;
getAccounts(): string[];
disconnect(): Promise<void>;
getProvider?(): Eip1193Provider | undefined;
}

getProvider()는 선택 멤버이므로 EIP-1193이 아닌 커넥터는 구현하지 않아도 됩니다.

커넥터생성 경로대상
InjectedConnector(provider?)직접 생성 — 기본 커넥터데스크톱 EIP-1193 window.ethereum
MetaMaskConnector(options)모바일 selectWallet("metamask")가 자동 생성MetaMask SDK 프로바이더 위임과 모바일 딥링크
Eip1193Connector상속사용자 정의 EIP-1193 커넥터의 기반 클래스
Eip6963Connector(rdns, options?)데스크톱 selectWalletEIP-6963 역 DNS로 특정 EVM 지갑 지정
SolanaConnector(options?)Solana 데스크톱 selectWallet주입형 Solana 프로바이더
SolanaMobileConnector(walletId, options?)Phantom 모바일 selectWalletuniversal link 딥링크와 백엔드 폴링

커넥터 구성 함수:

findEip6963Provider(rdns: string, timeoutMs?: number)
createMetaMaskSdkProviderGetter(options?)
createConnectorForWallet(info, namespace, options?)

findEip6963Provider는 EIP-6963 announce 이벤트를 기본 타임아웃까지 대기해 프로바이더를 찾고, createConnectorForWallet은 지갑 정보 항목을 커넥터 인스턴스로 변환합니다.

유니버설 링크 함수와 커넥터

모바일 지갑의 HTTPS 유니버설 링크를 만들고 앱 전환 중 연결 상태를 저장하는 공개 API입니다. 일반 dApp은 프로젝트 콘솔의 지갑 목록과 selectWallet()을 사용하고, 연결 경로를 직접 구성해야 할 때만 UniversalLinkConnector를 생성합니다.

API설명
createBrowserUniversalLinkStore()브라우저 localStorage를 사용하는 UniversalLinkStore 생성. SSR이나 저장소를 사용할 수 없는 환경에서는 안전하게 아무 작업도 하지 않음
buildUniversalLinkUrl(universalLink, inputs)지갑 링크에 scSessionId·scNonce·scChain 등 표준 파라미터를 추가. 절대 HTTPS URL이 아니면 INVALID_CONFIG
UNIVERSAL_LINK_PARAMS지갑 제공사가 해석해야 하는 sc* 쿼리 파라미터 이름
SOLANA_SIGN_TRANSACTION_METHODSolana 서명 요청 메서드 상수 "solana_signTransaction"
UniversalLinkConnector백엔드 세션 생성·결과 조회와 지갑 앱 전환을 처리하는 IWalletConnector 구현
interface UniversalLinkStore {
save(state: UniversalLinkPersistedState): void;
load(): UniversalLinkPersistedState | null;
clear(): void;
}

interface UniversalLinkConnectorOptions {
walletId: string;
universalLink: string;
session: GenericWalletSessionClient;
chainId?: Caip2ChainId;
openLink?: (url: string) => void;
returnUrl?: string;
dappName?: string;
store?: UniversalLinkStore;
}

class UniversalLinkConnector implements IWalletConnector {
constructor(options: UniversalLinkConnectorOptions);
resume(): Promise<UniversalLinkResumeResult>;
rehydrate(state: UniversalLinkPersistedState): void;
getConnection(): WalletConnection | null;
}

createBrowserUniversalLinkStore()는 연결·서명 재개에 필요한 세션 토큰을 localStorage에 저장합니다. 저장소를 로그나 분석 도구로 복사하지 말고, 연결을 해제하거나 흐름이 끝나면 SDK가 정리하도록 둡니다.

릴레이 페어링 함수

pair()가 페어링 흐름 전체를 처리하므로 일반 dApp은 다음 함수를 직접 호출할 필요가 없습니다. 사용자 정의 페어링 UI나 연결 진단을 구현할 때 사용합니다.

API종류설명
createPairing(relayUrl)함수새 페어링 토픽·대칭 키·URI를 생성해 Pairing 반환. AppKit.pair()는 빌드 시점에 고정된 DEFAULT_RELAY_URL을 넘깁니다
buildPairingUri(topic, symKeyP, relayUrl)함수페어링 URI 문자열 조립
parsePairingUri(uri)함수페어링 URI 파싱. 형식 위반은 INVALID_CONFIG
RELAY_PROTOCOL상수"scr"
SCOPE_PAIRING_VERSION상수1
relayTokenExpired(grant, nowMs?)함수릴레이 토큰 정보가 만료 시점(5초 여유 포함)에 도달했으면 true

createPairing이 반환하는 PairingparsePairingUri가 반환하는 ParsedPairingUri는 다음 형태입니다. 토픽 필드 이름은 topic이 아니라 pairingTopic입니다.

interface Pairing {
readonly pairingTopic: string; // 32바이트 소문자 hex
readonly symKeyP: Uint8Array; // 32바이트 페어링 대칭 키
readonly uri: string; // QR·딥링크로 노출할 페어링 URI
}

interface ParsedPairingUri {
readonly pairingTopic: string;
readonly version: number; // 1
readonly relayUrl: string;
readonly symKeyP: Uint8Array;
readonly relayProtocol: string; // "scr"
}

페어링 URI 형식은 다음과 같습니다.

scope:{topic}@1?relay-url={wss://...}&symKey={64자리 hex}&relay-protocol=scr

relay-urlws:// 또는 wss://여야 하고 symKey는 정확히 32바이트(64 hex)입니다. scr 프로토콜은 WalletConnect의 irn과 호환되지 않습니다.

연결된 세션은 다음 타입으로 표현됩니다.

interface Session {
readonly topic: string;
readonly symKeyS: Uint8Array;
readonly namespaces: SettledNamespaces;
readonly peerPublicKey: string;
readonly expiry?: number;
}

type SettledNamespaces = Record<string, {
chains?: string[];
accounts: string[];
methods?: string[];
events?: string[];
}>;

SettledNamespacesCaipNamespaces와 달리 지갑이 승인한 accounts(CAIP-10)를 포함합니다. 세션 대칭 키와 토픽은 비밀값으로 취급합니다.

GenericWalletSessionClient

주입형 EVM 지갑의 연결·서명 과정을 Connect 백엔드에 서버 검증 기록으로 남기는 클라이언트입니다. 대부분의 dApp은 이 클라이언트를 직접 쓰지 않고 AppKitOptions.genericWalletSession 옵션으로 활성화합니다. 연결 처리를 직접 구성해야 할 때만 호출합니다.

class GenericWalletSessionClient {
constructor(baseUrl: string, clientId: string, options?: GenericWalletSessionOptions);
readonly baseUrl: string;
createSession(caip2Chain: string): Promise<GenericSession>;
getResult(sessionId: string, sessionToken: string): Promise<GenericSessionResult>;
postConnect(body: {
sessionId: string;
walletSlug: string;
caip2Chain: string;
walletAddress: string;
}): Promise<void>;
postSignMsg(body: {
sessionId: string;
signature: string;
timestamp: number;
publicKey?: string;
}): Promise<void>;
}

interface GenericSession {
readonly sessionId: string;
readonly nonce: string;
readonly sessionToken: string;
}

interface GenericSessionResult {
readonly sessionId: string;
readonly status: string;
readonly connected: boolean;
readonly signed: boolean;
readonly walletAddress: string | null;
readonly signature: string | null;
readonly txSignature: string | null;
readonly errorCode: string | null;
}

interface GenericWalletSessionOptions {
fetchImpl?: typeof fetch;
}

function buildSignedMessageEnvelope(
session: string,
nonce: string,
walletAddress: string,
timestamp: number,
): string

function pollGenericSessionResult(
client: GenericWalletSessionClient,
sessionId: string,
sessionToken: string,
options?: PollGenericSessionOptions,
): Promise<GenericSessionResult>

createSessionX-App-Key로 세션을 만들어 sessionId·nonce·결과 조회용 sessionToken을 반환합니다. getResult는 세션 토큰으로 현재 연결·서명 결과를 조회하고, pollGenericSessionResult는 완료·오류·만료까지 반복 조회합니다. postConnect/postSignMsg는 결과를 백엔드 콜백에 기록합니다. buildSignedMessageEnvelope는 지갑이 서명할 메시지 문자열을 정해진 형식으로 만들며, 백엔드와 같은 키 순서(session,nonce,walletAddress,timestamp)로 직렬화합니다. baseUrl은 HTTPS가 필수이며 개발용 localhost만 HTTP를 허용합니다.

백엔드 요청 실패와 429를 포함한 비-2xx 응답은 현재 BACKEND_ERROR로 전달됩니다. AppError에는 응답 헤더가 포함되지 않으므로 SDK 호출자는 Retry-After를 읽을 수 없습니다. 재시도할 때는 작업의 멱등성을 확인하고 지수 백오프를 적용합니다.

서명 함수

buildEip3009AuthorizationsignTypedDataOverRelaysignTransferAuthorization이 내부에서 사용하는 함수입니다. 기본 AppKit 메서드로 처리할 수 없는 사용자 정의 연결 경로에서만 사용합니다.

setDebugModesetConsoleLogging

setDebugMode(enabled: boolean): void
setConsoleLogging(enabled: boolean): void

두 함수는 오류를 던지지 않습니다. 기본값은 둘 다 꺼짐이며, 디버그 모드를 켜면 콘솔 로깅도 켜지고, 디버그 모드를 꺼도 콘솔 로깅 값은 자동으로 꺼지지 않습니다.

import {
setConsoleLogging,
setDebugMode,
} from "@scope-connect/appkit";

setDebugMode(true);
setConsoleLogging(true);

isProviderRpcError(value)

직접 프로바이더 또는 사용자 정의 커넥터에서 전달된 숫자 코드 오류인지 확인합니다.

isProviderRpcError(value: unknown): value is ProviderRpcError

기본 AppKit 작업은 사용자 거부를 AppError로 정규화하므로 먼저 AppError를 처리합니다.

import {
AppError,
isProviderRpcError,
USER_REJECTED_CODE,
} from "@scope-connect/appkit";

function rejectionKind(error: unknown): "appkit" | "provider" | "other" {
if (error instanceof AppError) return "appkit";
if (isProviderRpcError(error) && error.code === USER_REJECTED_CODE) {
return "provider";
}
return "other";
}