본문으로 건너뛰기

잔액 조회

잔액 조회는 SCOPE Connect 백엔드의 Nodit 연동 데이터를 통해 네이티브 자산과 토큰 잔액을 읽습니다. 대상 체인은 요청에 넣는 CAIP-2 체인 ID로 명시하며, 연결된 지갑의 현재 체인에서 추론하지 않습니다. AppKit.getBalance() 또는 createBalanceReader()를 사용해 조회합니다.

잔액 조회는 연결 허용 네트워크와 별개입니다

잔액 조회는 loadConfig()가 적용하는 체인 허용 목록과 별도로 동작합니다. 허용 목록은 연결과 전환의 최종 체인에만 적용되며 잔액 읽기에는 적용되지 않습니다. account를 명시하면 연결 없이도 조회할 수 있습니다. AppKit.getBalance()는 생성자의 필수 clientId로 인증하고, 독립 리더를 쓸 때는 createBalanceReader()baseUrlappKey를 직접 넘깁니다.

사전 준비

  • AppKit.getBalance()는 생성자의 필수 clientId를 그대로 사용합니다. 백엔드 주소는 SDK 빌드 시점에 고정되어 설정할 값이 없습니다.
  • account를 생략할 경우 연결된 계정이 있어야 합니다.
  • 조회할 체인 ID, 토큰 컨트랙트 주소(EVM) 또는 mint 주소(Solana)를 준비합니다.

1. TokenBalanceQueryTokenBalance

TokenBalanceQuery는 명시적 체인, 토큰 주소, 계정 주소, decimals 힌트를 받습니다. TokenBalanceraw와 응답 기준 decimals를 돌려줍니다.

타입 개요
interface TokenBalanceQuery {
chainId: Caip2ChainId;
tokenAddress: string;
account: string;
decimals: number;
}

interface TokenBalance {
raw: bigint;
decimals: number;
chainId: Caip2ChainId;
}

account는 CAIP-10이 아니라 실제 주소 문자열입니다. EVM은 0x..., Solana는 base58 주소를 사용합니다.

EVM 네이티브 자산은 네이티브 자산을 나타내는 0 주소를 사용합니다.

네이티브 자산 식별자
const EVM_NATIVE_TOKEN = "0x0000000000000000000000000000000000000000";

tokenAddress는 TypeScript 타입에서 필수입니다. EVM 네이티브 조회도 값을 비우지 않고 0 주소를 전달합니다.

2. AppKit.getBalance()로 조회

연결된 상태에서는 AppKit.getBalance()로 현재 계정의 잔액을 바로 읽을 수 있습니다. account를 생략하면 getAccounts()[0]를 사용하고, 연결이 없으면 NOT_CONNECTED가 발생합니다.

AppKit에서 EVM 네이티브 잔액 조회
import { AppKit, getOrCreateUserHash } from "@scope-connect/appkit";

const appKit = new AppKit({
clientId: import.meta.env.VITE_SCOPE_CLIENT_ID,
// 선택 — 사용자 단위 조회·집계를 원할 때만 전달합니다(생략하면 userHash 미보고)
userHash: getOrCreateUserHash(),
telemetry: false,
});

appKit.init();

const sepolia = appKit
.getSupportedNetworks()
.find((network) => network.chainId === "eip155:11155111")!;

await appKit.selectWallet("metamask", { chainId: sepolia.chainId });

const balance = await appKit.getBalance({
chainId: sepolia.chainId,
tokenAddress: "0x0000000000000000000000000000000000000000",
decimals: 18,
});

account를 명시하면 연결과 독립적으로 조회할 수 있습니다.

명시적 계정으로 조회
const solanaDevnet = appKit
.getSupportedNetworks()
.find((network) => network.chainId === "solana:devnet")!;

const balance = await appKit.getBalance({
chainId: solanaDevnet.chainId,
tokenAddress: "{SPL_MINT_ADDRESS}",
account: "{OWNER_ADDRESS}",
decimals: 9,
});

3. createBalanceReader()로 직접 조회

서버 사이드나 배치 작업처럼 AppKit 인스턴스 없이 잔액만 읽어야 할 때는 createBalanceReader()를 사용합니다. 이 리더는 백엔드 GET /api/v1/connect/balancesX-App-Key를 붙여 호출합니다.

BalanceReader 생성과 사용
import { createBalanceReader, getNetworkInfo } from "@scope-connect/appkit";

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

// AppKit 인스턴스가 없으므로 내장 네트워크 정보 조회 함수로 체인 ID를 가져옵니다.
const ethereum = getNetworkInfo("eip155:1");

const balance = await reader.getBalance({
chainId: ethereum.chainId,
tokenAddress: "{ERC20_CONTRACT_ADDRESS}",
account: "{OWNER_ADDRESS}",
decimals: 6,
});

createBalanceReader()baseUrl, appKey, fetchImpl을 받습니다. baseUrl이나 appKey가 없거나 fetch를 사용할 수 없으면 INVALID_CONFIG가 발생합니다.

4. 최소 단위 잔액을 화면에 표시

raw는 최소 단위 정수이고, decimals는 그 값을 표시용으로 해석하는 데 쓰는 자릿수입니다. 응답에 들어 있는 decimals를 기준으로 화면 값을 계산합니다.

표시값 계산
const whole = balance.raw / 10n ** BigInt(balance.decimals);
const fraction = balance.raw % 10n ** BigInt(balance.decimals);

표시 형식은 호출자 책임입니다. 정밀도 손실을 피하려면 bigint 연산이나 정밀 소수 라이브러리를 사용합니다.

5. 오류 처리

잔액 조회는 실패하면 AppError를 던집니다.

  • INVALID_CONFIGcreateBalanceReader()baseUrl/appKey가 없거나 fetch를 사용할 수 없음(AppKit.getBalance()clientId가 필수 옵션이라 이 사유로 실패하지 않음)
  • NOT_CONNECTEDaccount를 생략했는데 연결이 없음
  • UNSUPPORTED_CHAIN — 백엔드가 지원하지 않는 체인
  • RPC_ERROR — 전송 실패, 비정상 응답, 응답 파싱 실패
오류 처리
import { AppError, AppResultCode } from "@scope-connect/appkit";

const ethereum = appKit
.getSupportedNetworks()
.find((network) => network.chainId === "eip155:1")!;

try {
await appKit.getBalance({
chainId: ethereum.chainId,
tokenAddress: "0x0000000000000000000000000000000000000000",
decimals: 18,
});
} catch (error) {
if (error instanceof AppError && error.code === AppResultCode.UNSUPPORTED_CHAIN) {
// 지원하지 않는 체인입니다.
}
throw error;
}

다음 단계