본문으로 건너뛰기

Agent 지갑

프라이빗 키를 다루지 않고도 트랜잭션을 시작할 수 있는 계정을 AI Agent에 제공합니다.

한 문장으로 요약하면: API를 한 번 호출해 ERC-4337 스마트 계정 주소를 받습니다. 프라이빗 키는 서비스에서 보관하고 배포 가스비를 부담합니다. 이후 송금 가스비는 계정에서 부담합니다.

이 장은 자체 시스템을 BOT Chain Agent 지갑 서비스와 연동하는 개발자를 위한 문서입니다. 지갑 생성, 컨트랙트보다 주소가 먼저 존재하는 이유, 토큰 전송, 수신자 제한 방법을 설명합니다.

이 장을 먼저 읽고 Agent 신원을 읽으세요. 신원 등록에는 이미 생성된 지갑이 필요합니다.

이 장의 모든 경로는 다음 기본 URL 중 하나를 기준으로 합니다.

환경URL
BOT 메인넷https://wallet-api.botchain.ai
Bohr 테스트넷https://wallet-api.bohr.life

API 키는 환경에 연결됩니다. 테스트넷 키로 메인넷에 요청하면 거부되며, 다른 환경으로 자동 전환되지 않습니다.

1. 개요​

질문답변
무엇을 받나요?Agent가 트랜잭션을 시작할 ERC-4337 스마트 계정 주소
프라이빗 키를 보관해야 하나요?아니요. 서비스에서 보관하고 사용하며, 연동 시스템은 서명하지 않습니다
가스비는 누가 내나요?배포 가스비는 서비스에서 부담합니다. 생성 시 Agent에 잔액이 없어도 됩니다. 이후 송금 가스비는 계정에서 부담합니다
주소를 바로 사용할 수 있나요?입금은 가능합니다. 출금하려면 먼저 계정을 배포해야 합니다
어떻게 송금하나요?POST /wallets/{id}/transfers로 UserOperation을 제출합니다
수신자를 제한할 수 있나요?가능하며 필수입니다. 실행 전에 허용 목록을 검증합니다
지원하는 체인은 무엇인가요?BOT 메인넷(chain ID 677)과 Bohr 테스트넷(chain ID 968)

다음을 기억하세요.

지갑을 생성하는 순간 주소는 존재하지만, 해당 주소의 컨트랙트는 나중에 배포됩니다. 둘 다 정상입니다. 코드가 없는 주소도 입금받을 수 있습니다.

2. 인증 정보 받기​

지갑과 신원 서비스는 인증 정보를 공유합니다. 각 연동 주체는 프로젝트이며, 프로젝트 ID와 서비스에서 발급한 하나 이상의 API 키를 가집니다.

ak_<environment>_k_<key_id>.<secret>

가운데 점을 포함한 전체 문자열을 X-API-Key 헤더에 넣으세요. secret은 생성 시 한 번만 반환되며 이후 다시 조회할 수 없습니다. 즉시 시크릿 관리 시스템에 저장하세요.

키는 환경별로 구분됩니다. 테스트넷 키는 메인넷에서 사용할 수 없습니다.

3. 지갑 생성​

POST /v1/wallets
Content-Type: application/json
X-API-Key: ak_…
{
"external_agent_id": "자체-agent-id",
"chain_id": 677,
"idempotency_key": "create-agent-42",
"callback_url": "https://your-service.example/webhooks/wallet"
}
필드필수의미
external_agent_id예자체 시스템의 Agent 식별자
chain_id예메인넷 677, 테스트넷 968
idempotency_key예Idempotency-Key 헤더로도 전달 가능
identity_id아니요기존 신원 연결. 보통 신원은 다음 단계에서 등록하므로 생략
callback_url아니요상태 변경 알림을 받을 주소

owner 주소와 계정 주소는 제공할 필요가 없습니다. 서비스에서 둘 다 생성해 반환합니다. 연동 시스템에서 생성할 것은 없으며 키 자료도 시스템 경계를 넘지 않습니다.

202를 반환합니다.

{
"wallet": {
"wallet_id": "wal_de43139cef869dc9c42ae3",
"project_id": "launchpad",
"external_agent_id": "자체-agent-id",
"chain_id": 677,
"owner_address": "0x5f5802fA78d5eE2A3B4fD67F49CD502430C3E88C",
"account_address": "0xd9DC4F46d9da603A2A077A7190BAa85f4e2f05fe",
"deployment_status": "PREDICTED",
"status": "ACTIVE",
"created_at": "2026-09-07T09:37:40Z"
},
"replayed": false
}

응답의 두 주소는 구분해서 사용해야 합니다.

필드의미
account_addressAgent의 주소. 이곳으로 입금하며, 온체인 발신자로도 표시됩니다
owner_address계정을 제어하는 주소. 신원 등록에 사용하며, 이곳으로 입금하지 마세요

이후 모든 API에서 사용하므로 wallet_id를 저장하세요.

읽기와 쓰기 응답 구조가 다릅니다

쓰기 API는 객체를 wallet 필드로 감싸지만, 읽기 API는 객체 자체를 반환합니다. 생성 후에는 response.wallet.account_address, GET 후에는 response.account_address를 사용하세요.

동일한 멱등성 키로 재요청하면 200, "replayed": true, 기존 지갑이 반환됩니다. 타임아웃 후 재시도해도 두 번째 계정이 생성되지 않습니다.

자체 식별자로 조회하면 서비스의 ID를 따로 저장하지 않아도 됩니다.

GET /v1/wallets/{wallet_id}
GET /v1/wallets/by-external-id/{chain_id}/{external_agent_id}

프로젝트에 속하지 않는 지갑은 모두 404를 반환합니다. '내 지갑이 아님'과 '존재하지 않음'의 응답은 같습니다.

4. 주소는 컨트랙트보다 먼저 존재합니다​

deployment_status의 초기값은 PREDICTED입니다. 기다리기만 하면 끝나는 중간 상태가 아니라 ERC-4337 계정의 기본 동작 방식입니다.

CREATE2가 owner와 salt로 주소를 계산하므로 미리 알 수 있고 변경되지 않습니다. 컨트랙트는 나중에 이 주소에 배포됩니다. 배포 전에는 다음과 같습니다.

작업PREDICTED에서 가능?
네이티브 토큰 또는 ERC-20 수신가능. 코드가 없는 주소로의 입금은 정상입니다
사용자에게 주소 표시가능
토큰 전송불가능
상태의미
PREDICTED주소 계산 완료, 온체인 컨트랙트는 아직 없음
DEPLOYING배포 제출 완료, 온체인 반영 대기
DEPLOYED컨트랙트 준비 완료, 출금 가능
배포는 명시적으로 호출해야 합니다

deployment_status가 DEPLOYED가 아니면 전송이 거부됩니다. 전송이 자동으로 배포를 시작하지 않으며, 409 WALLET_NOT_READY만 반환합니다. 초기화 과정에 배포를 포함하고 첫 결제 시 자동 배포될 것으로 기대하지 마세요.

POST /v1/wallets/{wallet_id}/deploy
{ "idempotency_key": "deploy-agent-42" }

202와 UserOperation을 반환합니다. deployment_status가 DEPLOYED가 될 때까지 지갑을 폴링하세요. 그 이후에 전송 요청이 허용됩니다.

배포에 계정 잔액은 필요하지 않습니다. 가스비는 서비스에서 부담합니다.

5. 토큰 전송​

POST /v1/wallets/{wallet_id}/transfers
Content-Type: application/json
X-API-Key: ak_…
{
"receiver": "0x1111111111111111111111111111111111111111",
"token_address": "0x2222222222222222222222222222222222222222",
"amount": "1000000",
"request_source": "API",
"idempotency_key": "payout-2026-09-08-001"
}
필드필수의미
receiver예수신 주소. 해당 지갑의 허용 목록에 있어야 함
token_address예ERC-20 컨트랙트 주소. 네이티브 토큰은 리터럴 BOT_NATIVE 사용
amount예최소 단위, 숫자 문자만 허용. 소수점 6자리 USDT의 경우 "1000000"은 1 USDT
request_source예API, CONSOLE, ADMIN 중 하나. 전송 요청 출처 기록
idempotency_key예재시도에 따른 중복 전송 방지

다음 네 가지 경우는 즉시 거부됩니다.

  • token_address는 생략할 수 없습니다. 네이티브 토큰은 문자열 BOT_NATIVE를 사용하며, 빈 값이나 제로 주소를 사용하지 마세요.
  • amount는 숫자 문자만 허용합니다. 소수와 지수 표기법은 반올림하지 않고 거부합니다. "1.5"와 "1e6"은 모두 실패합니다. 부동소수점 변환은 정밀도 손실로 실제 전송량을 바꿀 수 있으므로 이러한 형식을 허용하지 않습니다.
  • request_source는 필수이며 위의 세 값 중 하나여야 합니다.
  • 수신자는 지갑 자신의 계정 주소일 수 없습니다. 자기 자신에게 보내도 온체인 실행과 가스 소비가 발생하고 잔액은 변하지 않으므로 여기서 차단합니다.

amount는 숫자가 아닌 문자열입니다. 토큰 수량은 JavaScript가 정확히 표현할 수 있는 수치 범위를 넘을 수 있습니다.

202와 UserOperation을 반환합니다. 전송은 제출되었지만 아직 최종 확정되지 않았습니다.

{
"user_op_hash": "0x…",
"wallet_id": "wal_de43139cef869dc9c42ae3",
"chain_id": 677,
"op_type": "TRANSFER",
"status": "BUILT",
"sender": "0xd9DC4F46d9da603A2A077A7190BAa85f4e2f05fe",
"attempt": 0,
"created_at": "2026-09-08T07:20:11Z"
}

전송 추적​

GET /v1/user-operations/{user_op_hash}
상태의미최종 상태?
BUILT구성 완료, 서명 전아니요
SIGNED서명 완료, 제출 전아니요
SUBMITTEDbundler에 제출 완료아니요
INCLUDED블록에 포함됨, 영수증 확인 전아니요
SUCCEEDED완료. tx_hash와 block_number가 채워짐예
REVERTED온체인 실행 실패, 가스 소비됨예
FAILED체인에 도달하지 못함. 원인은 last_error 참고예

REVERTED와 FAILED는 다르게 처리해야 합니다. REVERTED는 체인에 도달한 뒤 잔액 부족이나 토큰 컨트랙트의 거부 등으로 실패한 상태입니다. FAILED는 그 단계까지 도달하지 못한 상태입니다. FAILED를 새 멱등성 키로 재시도하는 것은 합리적이지만, 아무것도 바꾸지 않고 REVERTED를 재시도하면 다시 revert됩니다.

attempt와 last_error는 서비스가 이미 수행한 시도를 보여줍니다.

6. 수신자 허용 목록​

허용 목록은 필수입니다

지갑은 허용 목록에 등록된 주소로만 전송할 수 있습니다. 빈 허용 목록은 어떤 주소로도 전송할 수 없다는 뜻이며 '제한 없음'이 아닙니다. 각 수신자를 첫 전송 전에 등록하지 않으면 전송이 거부됩니다.

이는 의도된 설계입니다. Agent가 전송 시점을 결정하므로 전송 가능한 대상은 Agent가 받은 지시에서 추론하지 않고 미리 정해야 합니다. 검증은 작업 구성 전에 서버에서 수행하므로, 시스템이 완전히 침해되더라도 공격자가 새로운 주소로 자금을 보낼 수 없습니다.

POST   /v1/wallets/{wallet_id}/allowlist
GET /v1/wallets/{wallet_id}/allowlist
DELETE /v1/wallets/{wallet_id}/allowlist/{entry_id}
{
"chain_id": 677,
"address": "0x1111111111111111111111111111111111111111",
"label": "정산 계정",
"allowed_actions": "TRANSFER",
"created_by": "ops@your-company.example",
"idempotency_key": "allow-payout-1"
}
필드필수의미
chain_id예항목은 체인별로 분리됩니다. 968의 항목으로 677의 전송을 허용할 수 없음
address예수신 주소
allowed_actions예TRANSFER, PAYMENT, ALL 중 하나
created_by예감사를 위해 주소 승인자를 기록
label아니요알아보기 쉬운 이름
idempotency_key예

항목은 (wallet_id, chain_id, address)로 일치 여부를 확인하며, 상태가 ACTIVE이고 allowed_actions에 전송이 포함되어야 합니다. 거부 응답은 항목 없음, 항목 철회, 전송 권한 없음 중 어떤 이유인지 알려줍니다. 각각 다른 해결이 필요한 문제이므로 주소를 무조건 다시 추가하지 말고 거부 이유를 읽으세요.

초기화 순서가 중요합니다. 먼저 수신 주소를 추가하고 나서 입금하세요. 잔액이 있어도 허용 목록이 비어 있으면 아무에게도 지급할 수 없습니다.

7. 지갑 일시 중지​

POST /v1/wallets/{wallet_id}/status
{ "status": "PAUSED" }

PAUSED는 새 작업을 중지하며 서비스에서 해제할 수 있습니다. REVOKED는 영구적입니다.

이 API는 의도적으로 재개 기능을 제공하지 않습니다. 중지된 계정의 재가동은 운영상의 결정이며 단순히 중지 작업을 반대로 실행하는 것이 아닙니다. 재개가 필요하면 문의하세요.

8. 콜백(Webhook)​

callback_url을 지정하면 상태 변경을 X-Callback-Token 헤더가 포함된 POST로 전송합니다. 수신자는 네 가지 규칙을 따라야 합니다.

  1. 먼저 X-Callback-Token을 검증하세요. 상수 시간 비교로 확인한 뒤 본문을 처리합니다.
  2. 신속하게 2xx를 반환하고 실제 처리는 비동기로 수행합니다.
  3. 중복 전달을 처리해야 합니다. 객체 ID와 updated_at을 멱등성 키로 사용하세요.
  4. 재시도로 순서가 바뀔 수 있습니다. 저장된 레코드보다 updated_at이 오래된 페이로드는 무시하세요.

콜백은 폴링을 최적화하는 수단이며 대체하지 않습니다. 수신 서버가 중단되어도 폴링하면 현재 상태를 즉시 받을 수 있습니다.

9. 오류 처리​

오류 응답에는 프로그램에서 판별할 수 있는 코드가 포함됩니다.

{ "error_code": "WHITELIST_DENIED", "error_message": "…" }

오류 문구를 비교하지 말고 error_code로 분기하세요.

HTTPerror_code의미재시도?
400INVALID_REQUEST요청 형식 오류 또는 필드 검증 실패아니요. 요청 수정
401—API 키 누락 또는 무효아니요
403WHITELIST_DENIED수신 주소에 유효한 허용 목록 항목이 없음아니요. 먼저 등록
403TOKEN_NOT_ALLOWED이 지갑에서 해당 토큰을 전송할 수 없음아니요
404NOT_FOUND지갑이 없거나 다른 프로젝트 소속아니요
409WALLET_NOT_READY미배포, 일시 중지 또는 철회됨상황에 따라 다름. 아래 참고
409IDEMPOTENCY_KEY_CONFLICTS동일 멱등성 키를 다른 내용에 사용아니요
422INSUFFICIENT_BALANCE잔액 부족아니요. 먼저 입금
501NOT_IMPLEMENTED이 환경에서 해당 기능이 활성화되지 않음아니요
5xxINTERNAL_ERROR서비스 측 오류예. 백오프 후 재시도

WALLET_NOT_READY는 세 가지 경우를 포함하며, error_message의 status=와 deployment=로 구분합니다.

  • deployment=PREDICTED 또는 DEPLOYING → 일시적 상태이므로 배포 완료 후 재시도합니다.
  • status=PAUSED → 지갑 재개 후 재시도합니다.
  • status=REVOKED → 영구 실패이므로 재시도하지 마세요.

모든 409를 영구 충돌로 처리하면 배포 중인 지갑을 실패로 잘못 판단해 포기하게 됩니다.

10. 환경​

항목BOT 메인넷Bohr 테스트넷
Chain ID677968
RPChttps://rpc.botchain.aihttps://rpc.bohr.life
블록 탐색기https://scan.botchain.aihttps://scan.bohr.life
Bundlerhttps://bundler.botchain.ai/rpchttps://bundler.bohr.life/rpc
EntryPoint0x0000000071727De22E5E9d8BAf0edAc6f37da032메인넷과 동일
계정 유형Kernel v0.3.3메인넷과 동일

EntryPoint는 표준 ERC-4337 v0.7 싱글턴 컨트랙트이므로 기존 ERC-4337 도구를 수정하지 않고 사용할 수 있습니다.

다음 단계: 신원​

지갑은 Agent에 계정을 제공합니다. 신원은 Agent를 검증 가능하게 합니다. 누구나 체인에서 직접 소유자와 사용하는 지갑을 조회할 수 있습니다.

신원을 등록하려면 방금 만든 지갑의 다음 세 값이 필요합니다.

신원 API 필드출처
wallet_idwallet.wallet_id
wallet_addresswallet.account_address
owner_addresswallet.owner_address