에이전트 결제의
권한 레이어
실명이 검증된 사람이 소프트웨어에 범위를 정해 지출 권한을 넘긴다. 범위는 결제 순간마다 컨트랙트가 강제하고, 취소는 즉시 반영되며, 사고는 실명까지 역추적된다.
01요약
x402로 에이전트가 스스로 결제하는 경로는 이미 열렸다. 막힌 것은 권한을 넘기는 방법이다. 지금 선택지는 개인키를 통째로 주거나(한도·수취인·기한을 걸 수 없다) 수탁 서비스를 믿는 것 둘뿐이다.
마패는 세 번째 길이다. 위임 메커니즘 자체는 발명하지 않았다 — ERC-7710/7715와 x402를 채택하고, 어디에도 없던 조건 하나를 기여한다: 위임자의 신원.
| 컨트랙트 | 8 | GIWA Sepolia 배포 · 전수 소스 검증 |
| 테스트 | 153 | 포크·불변식·3언어 바이트 패리티 포함, 정적 분석 심각·중대 0 |
| 라이브 트랜잭션 | 20+ | 정상 결제와 모든 거부 유형, 사유가 디코딩된 채로 |
| 백엔드 서버 | 0 | 권한의 유효성은 체인만 답한다 |
02문제
에이전트에게 결제를 맡기는 현재의 방법은 둘 다 같은 결함을 공유한다. 범위가 없다. 개인키를 넘기면 에이전트가 곧 지갑이 되고, 수탁에 맡기면 온체인 결제를 하려고 다시 중개인을 세우는 셈이다.
그리고 한도만으로는 부족하다. 사고가 났을 때 누가 이 권한을 줬는지가 남지 않으면 책임 주체를 특정할 수 없고, 가맹점은 에이전트 결제를 받을 이유가 없다. 이 마지막 조각이 기와에만 있다 — 다른 체인에는 조건으로 걸 신원 자체가 없다.
03아키텍처
조건을 서명
가스 0 · 트랜잭션 0
자율 결제
| 컨트랙트 | 역할 |
|---|---|
MapaeDelegationManager | ERC-7710 위임 실행. 서명·사슬·비활성 검사 후 조건을 평가하고, 통과해야만 계정에 실행을 위임 |
MapaeAccount | 자금과 위임 상태를 보유. owner는 immutable |
MapaeAccountFactory | CREATE2 배포 + 소유자 동의 서명(EIP-712) 검증 + 등록부 |
DojangVerifiedEnforcer | 기여물. 위임자의 실명 증명을 결제 조건으로 건다 |
AllowedPayeeEnforcer | 이체 calldata의 수취인을 검사 — 타겟이 아니라 |
ERC20PeriodTransferEnforcerTimestampEnforcer | MetaMask 감사본을 무수정 vendoring — 호환성의 실증 |
VerifiedCodeEnforcer | 사람 확인 티어. 살아 있는 오프체인 확인이 있을 때만 통과 |
04신원과 자금의 분리
도장은 컨트랙트에 붙지 않는다. 거래소는 KYC를 통과한 사람의 지갑 주소에 발급하지, 방금 배포된 컨트랙트에 발급하지 않는다. 그래서 위임자 주소를 그대로 검증하도록 설계하면 프로덕션에서 도장이 없고, 데모는 자기 발급 attestation으로만 통과한다 — 논지가 무너진다.
그래서 주소를 둘로 나눴다. 자금과 위임 상태는 계정이, 신원은 그 계정의 소유자인
사람이 가진다. 조건은 계정이 아니라 owner를 평가한다.
이 분리는 위조 경로를 하나 연다. 아무나 컨트랙트를 배포해 owner()가 남의
검증된 주소를 가리키게 만들 수 있다 — 훔치는 것은 없지만 책임 사슬을
위조하는 것이며, 그 사슬이 이 시스템이 보증하는 전부다. 두 겹으로 막는다.
① 계정 생성에 소유자 본인의 EIP-712 동의 서명을 요구한다
팩토리가 서명을 검증하고 등록한다. 동의 없이는 그 주소를 owner로 하는 계정이 존재할 수 없다.
② 조건은 등록 여부를 먼저 확인한 뒤에만 owner()를 신뢰한다
등록되지 않은 컨트랙트의 owner()는 공격자가 정하는 값이다. 순서가 뒤바뀌면 방어가 무의미해진다.
부수 효과로 리스크 하나가 소멸한다 — 소유자 EOA가 도장을 직접 발급받으므로 "컨트랙트가 attestation을 받을 수 있는가"라는 가정 자체가 불필요해진다.
05결제가 지나는 경로
redeemDelegations 호출beforeAll → before를 말단에서 뿌리 방향으로after → afterAll을 뿌리에서 말단 방향으로06조건 — 무엇을 서명하나
조건은 {enforcer, terms, args}다. terms는 위임자가
서명하고, args는 사용 시점에 사용자가 낸다. 위임 해시는
signature와 args를 제외하고 계산되므로, 사용자가 낸 값이 서명을
바꿀 수 없다.
terms는 전부 고정 오프셋으로 tightly-pack되며, 길이가 틀리면 잘라내는 게 아니라 revert한다. 조건들은 논리곱이다 — 한도의 마지막 1원까지 쓸 수 있고, 1원도 넘을 수 없다.
| 조건 | 길이 | 레이아웃 |
|---|---|---|
| 신원 | 52 | attesterId(32) ‖ principal(20) |
| 기간 한도 | 116 | token(20) ‖ periodAmount(32) ‖ periodDuration(32) ‖ startDate(32) |
| 수취인 | 20×N | 패킹된 주소 목록. 빈 목록은 전체 허용이 아니라 에러 |
| 기한 | 32 | afterThreshold(uint128) ‖ beforeThreshold(uint128) |
07기여물 — 신원 조건
배포된 위임 프레임워크들은 거의 모든 지출 조건을 이미 표현한다. 금액·기간·스트리밍· 타겟·메서드·calldata·시간 범위·호출 횟수. MetaMask 프레임워크 하나만 해도 감사된 조건이 38종이다.
그중 위임을 신원에 걸리게 하는 것은 없다 — 어떤 배포된 프레임워크에도, 어떤 체인에도. 조건으로 걸 온체인 신원이 없었기 때문이다.
설계 결정 넷
① 에이전트가 아니라 위임자의 신원을 건다
감사인·거래상대·보험사가 묻는 것은 "이 소프트웨어가 검증됐나"가 아니라 "어떤 실명이 이 지출을 허락했나"다.
② 발급자는 가정하지 않고 서명한다
발급자 식별자가 서명된 terms 안에 있다. 업비트로 범위가 정해진 위임은 자가 발급 attestation으로 만족될 수 없다 — 라이브로 증명됨. 발급자가 배포 상수가 아니라 서명 시점의 선택이라, 도장 생태계가 자랄수록 컨트랙트 변경 없이 넓어진다.
③ liveness는 캐시하지 않고 사용 시점에 읽는다
Dojang의 리졸버 자체가 "attestation은 사용 시점에 검증되어야 하며 인덱서는 liveness의 진실 소스가 아니다"라고 명시한다. 이 설계를 그대로 따랐고, 그래서 취소와 만료가 트랜잭션 없는 즉시 킬스위치가 된다.
④ boolean 조회 먼저, uid 조회는 나중
uid 게터는 미검증 주소에 revert한다. boolean 읽기가 부재·만료·취소를 전부 false로 접어주므로, 결제자는 항상 행동 가능한 에러 하나를 받는다.
이 컨트랙트는 무상태이고 실행 calldata를 파싱하지 않는다. 그래서 어떤 호출 형태와도 조합되고, 훅을 직접 불러 오염시킬 회계도 없다.
두 번째 빈칸 — 수취인 조건
작업 중에 격차가 하나 더 드러났다. 타겟 allowlist는 토큰 컨트랙트를
검사하는데, ERC-20 결제의 실제 수취인은 calldata 바이트 [4:36]에 있고 배포된
것 중 이걸 보는 게 없다. AllowedPayeeEnforcer가 "이 가맹점들에만 지불
가능"을 표현 가능하게 만든다.
08킬스위치 — 2×2 직교
두 개의 독립적인 스위치가 있고, 각각 되돌릴 수 있다.
각 축을 다른 축이 온전한 상태에서 시험했다. 도장이 살아 있는 채로 위임만 끄고, 위임이 켜진 채로 도장만 취소한다. 그래서 결과가 매니저의 체크 순서라는 가정에 의존하지 않는다. 되돌리는 것도 각각 증명했다 — 다시 켜면 쓰던 한도 그대로 재개되지, 리셋되지 않는다.
09재위임 — 좁히기만 가능
재위임은 ERC-7710의 일급 연산이다. 에이전트가 자기 권한의 일부를 다른 에이전트에게 넘길 수 있다. 사슬의 모든 링크의 조건이 평가되므로, 자식은 받은 것을 좁힐 수만 있고 넓힐 수 없다.
뿌리를 비활성화하면 상위에서 존재를 모르는 하위 에이전트까지 함께 정지한다. 이 성질을 지키는 것이 사슬 테스트 스위트의 존재 이유다.
10x402 정산
x402 v2의 exact 스킴은 EVM에서 세 가지 자산 이전 방식을 정의한다. 그중 둘은 토큰 레이어에서 승인하며, 그 승인은 nonce 한 번으로 소진된다. 세 번째 erc7710은
위임 매니저를 시뮬레이션해서 검증하며, 하나의 승인으로 여러 번
정산할 수 있는 유일한 방식이다.
마패는 이 세 번째 방식을 구현한다. 정산자는 정책도 자금도 갖지 않는다 — 모든 한도·수취인·기한·신원 검사가 온체인에서 돌고, 정산자의 키는 가스만 낸다. 이것은 우회가 아니라 설계 목표다. 정책 판정이 전부 온체인에 있으므로 정산자는 정책을 알 필요가 없고, 알 수도 없다.
GIWA에는 EIP-3009 토큰이 없어 다른 두 방식은 애초에 불가능하다. 라이브 증명에서는 같은 서명 payload로 정산이 두 번 일어나고, 세 번째는 브로드캐스트 전에 거부된다.
11에이전트 연동
npx mapae-mcp. MCP는 AI 에이전트에 외부 도구를 꽂는 표준 규격이고, 마패는 이
규격의 서버로 npm에 배포되어 있다. 클로드 코드·코덱스 같은 에이전트에 설치 한 줄이면
붙고, 그다음부터는 전부 자연어다.
| 도구 | 하는 일 |
|---|---|
list_permissions | 보유한 마패 목록과 각각의 조건 |
check_budget | 이번 기간의 사용액과 잔여 한도 |
pay | 결제 — 수취인·토큰은 서명된 정책에서 나온다 |
agent_status | 에이전트 주소·가스 잔량·남은 결제 여력 |
request_permission | 사람에게 보낼 권한 요청 링크 생성 (ERC-7715) |
load_context | 발급받은 마패를 대화 안에서 바로 장착 |
redelegate | 더 좁은 범위로만 재위임 |
12제품
익스플로러는 정적 SPA다. 서버가 없다 — 브라우저가 GIWA를 직접 읽는다. 세 화면이 있다.
발급
조건을 조합하고 자기 언어의 한 문장으로 읽은 뒤 서명한다. 가스 0, 트랜잭션 0. 문장과 바이트가 같은 구조에서 생성되므로, 읽은 것과 서명한 것이 어긋날 수 없다 — 코덱이 서로의 역함수임을 테스트가 강제한다.
권한 관리
발급한 것, 한도 대비 쓴 것, 그리고 끄는 스위치. 발급 사실은 브라우저가, 일어난 일은 매 마운트마다 체인이 답한다. 어긋나면 체인이 이기고, UI가 어느 쪽인지 말한다.
추적
결제 해시를 넣으면 위임 사슬 전체를 조건과 함께 펼친다.
블록 익스플로러와 다른 점 둘. 거부가 일급이다 — 거부된 결제에는 로그가 없으므로 위임을 calldata에서 복원하고, 블록 직전 상태에 재생해 거부 사유를 되살린다. 그리고 시제가 정직하다 — 당시엔 유효했던 결제는 두 사실을 다 보여준다. 게이트 이벤트가 증명하는 그때의 유효함과, 라이브 조회가 말하는 지금의 취소됨.
13검증
테스트 153개 전부 통과. 층위별로 무엇을 증명하는지가 개수보다 중요하다.
| 층 | 개수 | 증명하는 것 |
|---|---|---|
| 포크 테스트 | 10 | 실제 도장 배포본 상대로 — 진짜 업비트 KYC 주소가 통과하고, 발급자가 구별되고, 취소·만료가 즉시 닫힌다 |
| 인코딩 적합성 | 16 | 타입해시, 해시 제외 필드, ERC-7579 모드워드 — 리터럴 상수로 고정, 재계산 금지 |
| 위임 사슬 | 16 | 자식이 부모 한도를 넓힐 수 없음, 위조 접합 거부, 뿌리 차단이 하위까지 관통 |
| 매니저 API | 16 | 킬스위치 권한, 잘못된 배치, 훅 재진입 불가, 훅 순서 고정 |
| Enforcer · 계정 | 77 | 위조 경로, 실행 형태 거부, 팩토리 동의 바인딩 |
| 통합 | 13 | 전 구간 시나리오, 배치 원자성, 2×2 킬스위치 행렬 |
| 불변식 | 5 | 1000회 × 256콜. 독립 유령 원장 대비 — 신원이 죽으면 결제 없음, 한도 유지, 토큰 보존 |
| 3언어 바이트 패리티 | 31 | 같은 위임을 Solidity·TypeScript·Go가 각각 인코딩해 바이트 단위로 일치 |
| 정적 분석 | 0 / 0 | Slither 심각·중대 0. 예외 5건은 전부 근거 주석과 함께 |
| 소스 검증 | 8 / 8 | Blockscout API의 검증 플래그를 직접 읽는다 |
검증이 실제로 잡은 버그
- 모드워드 패킹 오류가 실행 형태 가드 둘을 조용히 무력화하고 있었다. all-zero 테스트가 공허하게 통과하는 동안 숨어 있다가 비-zero 왕복에 걸렸다.
- 빌드 설정 하나가 아무것도 브로드캐스트하지 않으면서 배포 성공을 보고했다. 체인에서 코드를 되읽어 발견했다.
- 계정 생성 서명이 잘 만들어졌는데 온체인에서 거부됐다. 팩토리의 EIP-712 다이제스트가 아니라 EIP-191로 서명하고 있었다.
14만들지 않은 것
| 지갑 | 기와 월렛의 자리다. ERC-7715 요청 경로를 미리 구현해 두었다 — 월렛이 표준을 켜면 통합이 아니라 스위치다 |
| 스테이블코인 | 원화 스테이블이 나오면 주소만 바꾼다. 테스트 토큰은 어떤 표준 확장도 일부러 구현하지 않은 맨몸 ERC-20이고, 그 위에서 전부 동작한 것이 자산 불가지론의 증명이다 |
| 위임 표준 | 발명하지 않고 채택했다. 감사된 조건들이 무수정으로 우리 매니저에서 돌고, 우리 신원 조건도 그들 매니저에 꽂힌다 |
| 온체인 레지스트리 | 발급 시점 저장을 강요하면 "발급은 오프체인 서명, 트랜잭션 없음"이라는 모델이 깨진다. 게이트 이벤트 한 줄이 역추적 고리를 닫는다 |
| ERC-8004 | 퍼미션리스 자가 등록은 책임 사슬에 아무것도 보태지 않는다. 에이전트 신원은 기와 방식으로 — 검증된 코드 조건으로 |
| 자체 백엔드 | 권한이 유효한지는 체인만 답해야 한다. 조회를 빠르게 할 인덱서는 로드맵에 있지만, 그것은 가공과 캐시이지 진실의 원본이 아니다 |
15신뢰 가정과 위협 모델
이 시스템이 무엇을 신뢰하고 무엇을 신뢰하지 않는지를 명시한다. 신뢰 경계를 적지 않은 보안 주장은 검증할 수 없다.
신뢰하는 것
| 대상 | 가정 |
|---|---|
| 도장 발급자 | 거래소가 실명 확인을 정확히 수행하고, 취소 사유가 생기면 취소한다. 마패는 발급 정확성을 검증하지 않는다 — 다만 어떤 발급자를 믿을지는 위임자가 서명으로 고른다 |
| EAS · DojangScroll | attestation의 만료·취소 상태를 정확히 반영한다. 조건은 이 값을 매 결제마다 읽는다 |
| 체인 | GIWA의 합의와 재구성 내성. 이 문서의 모든 강제는 온체인 실행에 의존한다 |
| vendoring한 코드 | 기간 한도·기한 조건은 MetaMask 감사본을 무수정으로 쓴다. 감사 결과를 신뢰하되, 실제 실행은 우리 테스트가 재확인한다 |
신뢰하지 않는 것
| 대상 | 이유와 방어 |
|---|---|
| 에이전트 | 침해되었다고 가정한다. 서명된 조건 밖으로는 지출할 수 없고, 수취인은 인자가 아니라 정책에서 나온다. 에이전트 키 유출의 최대 피해는 남은 한도까지, 허용된 수취인에게다 |
| 정산자 (x402) | 정책도 자금도 갖지 않는다. 정산자가 악의적이어도 조건을 우회할 수 없다 — 판정이 전부 온체인이다. 매니저 주소를 하나로 고정해, 공격자가 지정한 매니저로 수수료 지불자를 소모시키는 경로를 막는다 |
| 프런트엔드 | 백엔드가 없고, 화면의 값은 브라우저가 체인에서 직접 읽는다. 서명 문장과 서명 바이트가 같은 구조에서 생성되며 코덱이 서로의 역함수임을 테스트가 강제한다 — 표시와 서명이 어긋나는 경로를 코드 수준에서 닫는다 |
| 임의의 컨트랙트 | 등록되지 않은 컨트랙트의 owner()는 공격자가 정하는 값이므로, 조건은 팩토리 등록 여부를 먼저 확인한다 |
| 재위임 하위 사슬 | 사슬의 모든 링크의 조건이 평가되므로 자식은 부모를 넓힐 수 없다. 뿌리 비활성화가 사슬 전체를 관통한다 |
알려진 한계
- 발급 시점의 신원은 보증하지 않는다. 조건은 결제 순간의 liveness를 읽을 뿐, 거래소가 잘못 발급한 attestation을 걸러내지 못한다. 이는 도장 계층의 책임이다.
- 가스는 에이전트가 낸다. 잔액이 떨어지면 결제가 실패한다. 대납(paymaster)은 기와에 번들러 인프라가 서면 검토한다.
- 공개 RPC의 읽기 일관성. 로드밸런싱된 엔드포인트가 서로 다른 높이의 상태를 반환할 수 있다. 강제는 온체인이라 안전에는 영향이 없지만, 조회 UI는 재시도로 대응한다.
16배포 주소
GIWA Sepolia (91342). 전부 소스 검증되어 있다.
MapaeDelegationManager | 0xfd0fCCCcF8071852783b5133b3CC47461f33e6Cd |
MapaeAccountFactory | 0x157aF4D7b3f52685c817d5558b3468caD9b61299 |
DojangVerifiedEnforcer | 0xb2906a5079702B82C2973423d8cf91e8B41e6371 |
AllowedPayeeEnforcer | 0x7eF0f193B721B1749d890F1e231C8074670f1bD1 |
ERC20PeriodTransferEnforcer | 0xE33ba891fa502A075D3E422258723eF4cB6AC892 |
TimestampEnforcer | 0x2911cB5D4aeBCa3e42FAaa5488b6e04df3C9cc02 |
VerifiedCodeEnforcer | 0x1C640E0A70b1E18B120bB20952e81Df8F6b8650e |
MockKRW | 0x8bd74916E3427B4eF8Bed3D2F49241056E5e4F2B |
17다음
감사된 조건 38종 전부 탑재
지금은 결제에 필요한 5종이 올라가 있다. 표준 호환이라 나머지는 수정이 아니라 검증의 일이다 — 건당 상한, 누적 총액, 호출 횟수, 허용 함수 셀렉터, 결제를 목적에 묶는 주문 해시. 구독·법인 경비 같은 시나리오 프리셋으로 조합을 제품화한다.
기와 월렛 연동 · 도장 세분화
ERC-7715 요청 경로는 구현되어 있어 월렛 쪽 스위치만 남았다. 그리고 거래소별·등급별 도장이 늘수록 걸 수 있는 조건이 코드 변경 없이 촘촘해진다 — 발급자는 배포 상수가 아니라 서명 안의 선택이기 때문이다.
조회 인프라
GIWA에는 도장을 조회할 오프체인 API가 없다. 실 발급자 attestation과 마패 사용 기록을 인덱싱해 생태계가 쓸 조회 계층을 연다. 진실은 체인이 쥐고, 속도는 인덱서가 낸다.
EOA 직접 위임 — EIP-7702
GIWA에서 활성화되어 있음을 행동 테스트로 확인했다. 소유자 EOA가 직접 위임자가 되면 계정 컨트랙트 없이 검증된 주소 자체가 위임을 보유한다. 조건은 이 형태를 이미 받아들인다.
18참고 표준
| ERC-7710 | 스마트 컨트랙트 위임 — redeemDelegations 인터페이스와 조건 훅 |
| ERC-7715 | 지갑에 실행 권한을 요청하는 표면. 요청 경로를 구현해 두었다 |
| ERC-7579 | 모듈형 계정의 실행 인코딩 — 모드워드와 단일 실행 레이아웃 |
| EIP-712 · ERC-1271 | 구조화 데이터 서명, 컨트랙트 서명 검증 |
| EIP-7702 | EOA에 코드를 부여하는 경로. 기와에서 활성 확인, 로드맵 |
| x402 v2 | HTTP 402 결제 프로토콜. exact 스킴의 erc7710 방식을 구현 |
| EAS | Ethereum Attestation Service. 도장이 이 위에 서고, 만료·취소를 결제 시점에 읽는다 |
| MetaMask delegation-framework | 구조·해싱의 바이트 호환 기준. 조건 2종은 감사본을 무수정 사용 (MIT AND Apache-2.0) |