본문으로 건너뛰기
이 페이지는 영문에서 기계 번역되었으므로 오역이나 어색한 표현이 있을 수 있습니다. 따라서 정확한 정보는 영어 원문을 참조하시기 바랍니다. 또한 잦은 업데이트로 인해 일부 콘텐츠는 영문이 그대로 남아있을 수 있습니다. Crowdin에서 이 페이지의 번역을 개선하는 데 동참하여 도움을 주세요. (Crowdin translation page, Contributing guide)

안전 API 키트

일몰 공고

safe.kaia.io2026년 8월 31일에 서비스가 종료됩니다. 앞으로는 app.safe.global에서 Kaia Network용 Safe Wallet을 사용하여 계정을 관리해 주시기 바랍니다. 기존의 ‘Safe Accounts’는 ‘Safe Wallet’과 자동으로 호환됩니다.

API 키트를 사용하면 안전 거래 서비스와 안전하게 연동할 수 있습니다. 유효한 서명자는 거래를 제안 및 공유하고, 오프체인에서 서명을 수집하며, Safe 정보(이력, 보류 중인 거래, 모듈, 가드 등)를 조회할 수 있습니다.

Safe의 호스팅형 트랜잭션 서비스는 두 Kaia 네트워크를 모두 지원하므로, 체인 ID와 API 키만 있으면 되며 별도의 엔드포인트 설정은 필요하지 않습니다.

네트워크체인 ID
카이아 메인넷8217
카이로스 테스트넷1001

Quickstart

이 가이드를 마치면, 서비스에 거래를 제안하고 실행을 위해 소유자의 서명을 받을 수 있게 됩니다.

Prerequisites

  1. Node.js 및 npm—이 예제에서는 내장 함수 process.loadEnvFile()을 사용하므로 Node 20.12 이상이 필요합니다.
  2. Kairos(또는 메인넷)에서 여러 명의 서명자가 있는 금고
  3. 안전한 API 키

API 키 받기

Safe의 트랜잭션 서비스에 대한 요청에는 API 키가 필요합니다. Safe 개발자 대시보드에 로그인한 후, API 키를 열고 새 키를 생성하세요. 핵심은 JWT입니다. 만료 시간을 설정할 수 있으며, 언제든지 이를 취소할 수 있습니다.

인증되지 않은 액세스는 초당 2회, 월 5,000회로 제한되며, 이는 탐색 목적으로만 사용됩니다. 키가 없는 요청은 401 Unauthorized를 반환하며, 할당량을 초과하면 429 Too Many Requests를 반환합니다.

키를 소스 제어 시스템에 포함시키지 마십시오. 이 가이드에서는 RPC URL 및 서명자 키와 함께 이 정보를 .env 파일에서 읽어옵니다. 자세한 내용은 6단계를 참조하세요.

Set up environment

1단계: 프로젝트 디렉터리를 생성합니다.


mkdir kaiasafe-api-kit
cd kaiasafe-api-kit

2단계: npm 프로젝트 초기화하기


npm init -y

3단계: 종속성 설치


npm install @safe-global/api-kit @safe-global/protocol-kit @safe-global/types-kit

노트

이제 타입은 @safe-global/types-kit에 있습니다. 기존의 @safe-global/safe-core-sdk-types 패키지의 이름이 변경되었습니다. 기존 프로젝트를 업그레이드하는 경우, 임포트 문구를 업데이트하십시오.

4단계: ES 모듈 활성화

아래 예제에서는 import 구문을 사용하므로, package.json에 다음 내용을 추가하세요:


{
"type": "module"
}

5단계: 종속성 가져오기

app.js 파일을 생성하고 다음 내용을 추가하세요:


'@safe-global/api-kit'에서 SafeApiKit을 가져옵니다.
'@safe-global/protocol-kit'에서 Safe를 가져옵니다.
'@safe-global/types-kit'에서 { OperationType } 을 가져옵니다.

6단계: 설정 구성

소유자가 최소 두 명이고, 서명 임계값을 2로 설정하여 여러 명의 서명이 필요하도록 금고를 사용하십시오.

프로젝트 루트 디렉터리에 .env 파일을 생성합니다:


# 카이아 카이로스 테스트넷 — https://chainlist.org/?search=kaia&testnets=true
RPC_URL=https://public-en-kairos.node.kaia.io
CHAIN_ID=1001
EXPLORER_TX_URL=https://kairos.kaiascan.io/tx/
# https://developer.safe.global에서 가져온 API 키
SAFE_API_KEY=
# Kairos(https://app.safe.global)에 배포된 2-of-2 Safe
SAFE_ADDRESS=
# 소유자 1은 제안을 하고 실행도 하므로, 가스 비용을 충당할 테스트용 KAIA가 필요합니다.
OWNER_1_ADDRESS=
OWNER_1_PRIVATE_KEY=
# 소유자 2는 오프체인에서 서명만 하므로 잔액이 필요하지 않습니다.
OWNER_2_PRIVATE_KEY=
# 1 위(wei) 샘플 트랜잭션의 수신자 (기본값은 OWNER_1_ADDRESS)
# TO_ADDRESS=

위험

.env 파일에는 개인 키가 저장되어 있습니다. 첫 커밋을 하기 전에 .gitignore 파일에 이를 추가하고, 이 예제에서는 실제 자금이 들어 있는 키를 절대 사용하지 마십시오.

app.js에 이를 불러오고, 누락된 항목이 있으면 즉시 오류를 발생시키도록 하세요:


// .env 파일을 process.env에 불러옵니다(Node 20.12 이상 / 21.7 이상에 내장되어 있으며, 별도의 의존성이 필요하지 않음)
process.loadEnvFile()
const {
RPC_URL,
CHAIN_ID,
SAFE_API_KEY,
SAFE_ADDRESS,
OWNER_1_ADDRESS,
OWNER_1_PRIVATE_KEY,
OWNER_2_PRIVATE_KEY,
EXPLORER_TX_URL
} = process.env
const REQUIRED = [
'RPC_URL',
'CHAIN_ID',
'SAFE_API_KEY',
'SAFE_ADDRESS',
'OWNER_1_ADDRESS',
'OWNER_1_PRIVATE_KEY',
'OWNER_2_PRIVATE_KEY'
]
const missing = REQUIRED.filter((key) => !process.env[key])
if (missing.length > 0) {
console.error(`.env에 필수 환경 변수가 누락되었습니다: ${missing.join(', ')}`)
process.exit(1)
}
// 1 wei를 수령하는 샘플 트랜잭션의 수신자 주소
const TO_ADDRESS = process.env.TO_ADDRESS || OWNER_1_ADDRESS

Use API Kit

Step 1: Initialize API Kit

체인 ID와 API 키를 전달해 주세요. Safe가 트랜잭션 서비스 엔드포인트를 자동으로 해결해 주므로, Kaia나 Kairos에서는 txServiceUrl이 필요하지 않습니다.


const apiKit = new SafeApiKit({
chainId: BigInt(CHAIN_ID), // Kairos의 경우 1001, Kaia 메인넷의 경우 8217
apiKey: SAFE_API_KEY
})

자체 트랜잭션 서비스 인스턴스를 실행하는 경우 txServiceUrl을 계속 사용할 수 있으며, 이 값을 설정할 때는 apiKey를 지정할 필요가 없습니다.

Step 2: Initialize Protocol Kit


const protocolKitOwner1 = await Safe.init({
provider: RPC_URL,
signer: OWNER_1_PRIVATE_KEY,
safeAddress: SAFE_ADDRESS
})

3단계: 서비스에 트랜잭션을 제안합니다.


const safeTransactionData = {
to: TO_ADDRESS,
value: '1', // 1 wei
data: '0x',
operation: OperationType.Call
}
const safeTransaction = await protocolKitOwner1.createTransaction({
transactions: [safeTransactionData]
})
const safeTxHash = await protocolKitOwner1.getTransactionHash(safeTransaction)
const signature = await protocolKitOwner1.signHash(safeTxHash)
try {
await apiKit.proposeTransaction({
safeAddress: SAFE_ADDRESS,
safeTransactionData: safeTransaction.data,
safeTxHash,
senderAddress: OWNER_1_ADDRESS,
senderSignature: signature.data
})
} catch(err) {
console.log(err)
}

Step 4: Retrieve pending transaction


const transaction = await apiKit.getTransaction(safeTxHash)
// 동일한 인스턴스에서 사용할 수 있는 다른 읽기 기능:
// const transactions = await apiKit.getPendingTransactions(SAFE_ADDRESS)
// const transactions = await apiKit.getIncomingTransactions(SAFE_ADDRESS)
// const transactions = await apiKit.getMultisigTransactions(SAFE_ADDRESS)
// const transactions = await apiKit.getModuleTransactions(SAFE_ADDRESS)
// const transactions = await apiKit.getAllTransactions(SAFE_ADDRESS)

Step 5: Confirm the transaction

Protocol Kit로 서명하고 confirmTransaction을 통해 서명을 제출하세요.


const protocolKitOwner2 = await Safe.init({
provider: RPC_URL,
signer: OWNER_2_PRIVATE_KEY,
safeAddress: SAFE_ADDRESS
})
const signature2 = await protocolKitOwner2.signHash(safeTxHash)
const signatureResponse = await apiKit.confirmTransaction(
safeTxHash,
signature2.data
)

Step 6: Execute the transaction

Safe Wallet, Protocol Kit, Safe CLI 또는 기타 호환 가능한 도구를 통해 실행하십시오.


const safeTxn = await apiKit.getTransaction(safeTxHash);
const executeTxReponse = await protocolKitOwner1.executeTransaction(safeTxn)
const receipt = await executeTxReponse.transactionResponse?.wait();
console.log('트랜잭션 실행됨:');
console.log(`${EXPLORER_TX_URL || 'https://kairos.kaiascan.io/tx/'}${receipt?.transactionHash}`);

app.js의 전체 예제:


import SafeApiKit from '@safe-global/api-kit'
import Safe from '@safe-global/protocol-kit'
import {
OperationType
} from '@safe-global/types-kit'
// .env를 process.env로 불러옵니다(Node >= 20.12 / 21.7에 내장되어 있으며, 별도의 종속성이 필요하지 않음).
process.loadEnvFile()
const {
RPC_URL,
CHAIN_ID,
SAFE_API_KEY,
SAFE_ADDRESS,
OWNER_1_ADDRESS,
OWNER_1_PRIVATE_KEY,
OWNER_2_PRIVATE_KEY,
EXPLORER_TX_URL
} = process.env
const REQUIRED = [
'RPC_URL',
'CHAIN_ID',
'SAFE_API_KEY',
'SAFE_ADDRESS',
'OWNER_1_ADDRESS',
'OWNER_1_PRIVATE_KEY',
'OWNER_2_PRIVATE_KEY'
]
const missing = REQUIRED.filter((key) => !process.env[key])
if (missing.length > 0) {
console.error(`.env에 필수 환경 변수가 누락되었습니다: ${missing.join(', ')}`)
process.exit(1)
}
// 1 wei를 수령하는 샘플 트랜잭션의 수신자 주소
const TO_ADDRESS = process.env.TO_ADDRESS || OWNER_1_ADDRESS
const apiKit = new SafeApiKit({
chainId: BigInt(CHAIN_ID),
apiKey: SAFE_API_KEY
})
const protocolKitOwner1 = await Safe.init({
provider: RPC_URL,
signer: OWNER_1_PRIVATE_KEY,
safeAddress: SAFE_ADDRESS
})
// 1. 트랜잭션 생성
const safeTransactionData = {
to: TO_ADDRESS,
value: '1', // 1 wei
data: '0x',
operation: OperationType.Call
}
const safeTransaction = await protocolKitOwner1.createTransaction({
transactions: [safeTransactionData]
})
const safeTxHash = await protocolKitOwner1.getTransactionHash(safeTransaction)
const signature = await protocolKitOwner1.signHash(safeTxHash)
// 2. 서비스에 트랜잭션 제안
try {
await apiKit.proposeTransaction({
safeAddress: SAFE_ADDRESS,
safeTransactionData: safeTransaction.data,
safeTxHash,
senderAddress: OWNER_1_ADDRESS,
senderSignature: signature.data
})
} catch(err) {
console.log(err)
}
console.log("트랜잭션 해시는 "+safeTxHash)
const transaction = await apiKit.getTransaction(safeTxHash)
// 동일한 인스턴스에서 확인할 수 있는 기타 읽기 정보:
// const transactions = await apiKit.getPendingTransactions(SAFE_ADDRESS)
// const transactions = await apiKit.getIncomingTransactions(SAFE_ADDRESS)
// const transactions = await apiKit.getMultisigTransactions(SAFE_ADDRESS)
// const transactions = await apiKit.getModuleTransactions(SAFE_ADDRESS)
// const transactions = await apiKit.getAllTransactions(SAFE_ADDRESS)
// 3. 소유자 2의 확인
const protocolKitOwner2 = await Safe.init({
provider: RPC_URL,
signer: OWNER_2_PRIVATE_KEY,
safeAddress: SAFE_ADDRESS
})
const signature2 = await protocolKitOwner2.signHash(safeTxHash)
// Safe 트랜잭션 확인
const signatureResponse = await apiKit.confirmTransaction(
safeTxHash,
signature2.data
)
console.log(signatureResponse)
// 4. 트랜잭션 실행
const safeTxn = await apiKit.getTransaction(safeTxHash);
const executeTxReponse = await protocolKitOwner1.executeTransaction(safeTxn)
const receipt = await executeTxReponse.transactionResponse?.wait();
console.log('Transaction executed:');
console.log(`${EXPLORER_TX_URL || 'https://kairos.kaiascan.io/tx/'}${receipt?.transactionHash}`);

실행 가능한 전체 프로젝트(app.js, .env.example, package.json)는 kaia-safe-api-kit에서 확인할 수 있습니다. 전체 메서드 목록은 API 키트 참조를 참조하십시오.

페이지를 개선해 주세요