<!-- depin · v0.3 · updated 2026-08 · locale ko · https://paper.wellbianlabs.io/depin -->

IAQ / OAQ Data Ecosystem - Whitepaper & XRPL Technical Specification

☰
EN
◐

     IAQ / OAQ Data Ecosystem
    Whitepaper & XRPL Tech Spec v0.3

## 백서 1 개요

현대인은 하루의 90% 이상을 실내에서 생활하며, 실내 날씨과 기후 환경은 개인의 건강과 직결된다. 그러나 대부분의 실내 공조 가전(공기청정기, 제습기, 에어컨 등)은 출하 시 설정된 표준 알고리즘에 의존해 작동한다.

본 프로젝트는 전국 단위의 IoT 실내 날씨 측정망을 통해 사용자의 실제 거주 환경 데이터를 실시간으로 수집하고, 이를 가전 제조사에 제공하여 기술 발전과 초개인화 마케팅을 지원하는 '실내 기후 데이터 플랫폼 생태계'를 구축하는 것을 목적으로 한다.

    90%+현대인의 하루 중 실내 체류 비중
    0제조사가 확보한 출하 후 실거주 환경 데이터
    6+7종노드가 상시 측정하는 지표 - 실내 IAQ 6종 · 실외 OAQ 7종

    문서 구성

Part 1(백서)은 생태계의 사업 구조를 기술한다. Part 2~3(기술 개요 · 날씨 데이터 사양)과 Step 1~6 · 운영 섹션은 이 구조를 XRPL 위에 구현하기 위한 데이터 스키마, 라이선스·보상 설계, 비식별화 파이프라인, API 과금 프로토콜, 그리고 단계별 구현 지침을 다룬다. 백서의 각 장은 대응하는 기술 섹션으로 연결된다.

## 백서 2 문제 제기 및 시장의 한계

      Problem 01
      제조사의 실데이터 부재
      가전 제조사는 제품 판매 이후 실제 소비자의 가정 내 날씨 변화, 계절별 기기 사용 패턴,
        주거 환경(평형, 위치, 채광)에 따른 공조 효율 데이터를 확보하기 어렵다.

      Problem 02
      획일화된 제품 개발 및 마케팅
      지역별·주거 형태별로 요구되는 제습량이나 공기 정화 능력이 다름에도 불구하고,
        천편일률적인 스펙 경쟁과 광범위한 마케팅에 의존하고 있다.

    데이터 공백이 만드는 비용

제조사는 실환경 데이터가 없어 최악 조건 기준으로 과설계하거나, 반대로 특정 주거 형태에서 성능이 미달하는 제품을 출하한다. 마케팅 측면에서는 "제습이 필요한 가구"를 식별할 수단이 없어 전 고객 대상 브로드캐스트에 의존하며, 이는 곧 리드 획득 단가 상승으로 이어진다. 본 생태계는 이 두 공백을 각각 R&D 데이터와 코호트 API로 메운다.

## 백서 3 플랫폼 생태계 구조

본 생태계는 세 주체로 구성되며, 데이터의 수집–정제–활용이 선순환을 이룬다.

DATA PROVIDER 데이터 기여자 가정 · 사무실 · 상업시설 IAQ 측정 IoT 디바이스 설치 기여도 기반 리워드 획득 PLATFORM HUB 플랫폼 허브 원시 데이터 온체인 무결성 보장 개인정보 비식별화 처리 메타데이터 가공 · 품질 평가 XRPL + 클라우드 인프라 DATA CONSUMER 가전 제조사 에어컨 · 제습기 · 공기청정기 환기 시스템 · 스마트홈 플랫폼 구매 또는 구독형 API 원시 측정값 정제 메타데이터 데이터 이용료 기여 보상 실선 = 데이터 흐름 · 점선 = 가치 흐름 보상은 가전 구매 할인 · 필터 구독 결제로 재유입되어 생태계 내에서 순환 Fig 0. 3주체 선순환 구조 ### 3.1 데이터 기여자 (Data Provider) - 가정·사무실·상업시설에 실내(IAQ) 측정기를, 옥상·가로변·학교 등에 실외(OAQ) 측정기를 설치한 사용자. - 실시간 환경 데이터를 플랫폼에 제공하고, 기여도에 따라 리워드(보상)를 획득한다. ### 3.2 플랫폼 허브 (Data Platform Hub) - 수집된 원시 데이터(Raw Data)를 블록체인 및 클라우드 인프라를 통해 위변조 없이 안전하게 저장한다. - 개인정보를 비식별화(익명화) 처리한 후, 제조사가 활용할 수 있는 형태의 메타데이터로 가공한다. ### 3.3 데이터 수요자 (Appliance Manufacturers) - 에어컨, 제습기, 공기청정기, 환기 시스템 등을 제조하는 기업. - 정제된 데이터를 구매하거나 구독형 API 형태로 제공받아 R&D 및 마케팅에 활용한다. 기술 대응 기여자 자격 증명 → XLS-20 라이선스 NFT · 원시 데이터 무결성 → 오라클 배치 머클루트 · 비식별화 → 5단계 파이프라인 · 수요자 과금 → x402 데이터 상품.

## 백서 4 수집 데이터 및 핵심 활용 방안

수집된 데이터는 수요기업의 목적에 맞게 기술적 데이터(R&D)와 마케팅 데이터(CRM/Sales)로 분류되어 제공된다.

### 4.1 실내(IAQ) 지표 - 가전 R&D · 마케팅

    | 수집 지표 | 연관 가전제품 | 기술적 활용 방안 (R&D) | 마케팅 활용 방안 (Sales) |

      | 온·습도 (상대습도 · 절대습도 환산) | 제습기, 에어컨 | 주거 형태별 최적 제습 알고리즘 개발, 결로 방지 예측 모델링 구축 | 장마철 고습도 유지 가구 타겟팅 제습기 프로모션, 건조 가구 대상 가습기 교체 시기 알림 |

      | 미세먼지·초미세먼지 (PM10 / PM2.5) | 공기청정기, 청소기 | 필터 수명 예측 모델 고도화, 오염도 급증 시 팬 속도 자동 제어 로직 설계 | 대기오염도와 실내 오염도가 비례하는 가구 추출, 대용량 프리미엄 공기청정기 타겟 광고 |

      | 이산화탄소 (CO₂) | 환기 청정기 (전열교환기) | 밀폐 공간 내 CO2 농도 변화율 기반 AI 환기 시점 예측 기술 개발 | 환기 부족 다중이용시설 및 영유아 가구 대상 스마트 환기 시스템 도입 제안 |

      | TVOC (총휘발성유기화합물) | 새집증후군 제거기 | 건축 자재별 배출 가스 패턴 분석, 센서 감도 최적화 | 신축 아파트 입주 및 인테리어 시공 가구 대상 특화 필터 마케팅 |

      | 온도 / 체감온도 | 에어컨, 난방기기 | 체감 기반 목표온도 제어 로직 설계, 계절별 냉난방 부하 프로파일 구축 | 혹서·혹한 체감 구간이 지속되는 가구 대상 냉난방 기기 교체 제안 |

### 4.2 실외(OAQ) 지표 - 환경 서비스 · 외기 연동 제어

실외 지표는 수요자 구성이 다르다. 실내 지표가 가전 제조사 한 축이라면, 실외는 지자체·환경 서비스가 1차 수요자이고 가전 제조사는 "외기 조건에 반응하는 제어"라는 형태로 2차 수요자가 된다.

    | 수집 지표 | 주 활용 주체 | 기술적 활용 방안 (R&D) | 서비스 활용 방안 |

      | 미세먼지·초미세먼지 (PM10 / PM2.5) | 가전 제조사 · 지자체 | 격자별 침투율(I/O ratio) 산출, 외기 연동 선제 급기·차단 제어 알고리즘 개발 | 외기 악화 예보 기반 창문 닫기·공기청정기 예약 가동 알림 |

      | 오존 (O₃) | 지자체 · 보건 | 광화학 생성 패턴 분석, 환기 금지 시간대 판정 모델 구축 | 오존주의보 구간 환기 억제 권고 - 환기형 청정기에 제어 신호 전달 |

      | 이산화질소 (NO₂) | 지자체 · 교통 | 도로변 배출 기여도 분리, 가로변 노출 지도 작성 | 통학로·보행로 저노출 경로 안내 |

      | 아황산가스 (SO₂) | 산업단지 · 환경 | 산업 배출원 영향 반경 추정, 풍향별 확산 모델 검증 | 인근 주거지 대상 배출 이벤트 알림 |

      | 일산화탄소 (CO) | 지자체 · 안전 | 교통 정체 구간 축적 패턴 분석 | 밀집 구역 단기 노출 경보 |

      | 풍속 · 풍향 | 전 주체 | 오염물질 이동·확산 방향 추정, 배출원 역추적 | 인접 격자로부터의 오염 유입 예측 |

    두 표를 잇는 값은 PM2.5다

4.1과 4.2에서 PM2.5만 양쪽에 등장한다. 같은 물질을 같은 광산란 방식으로 실내외에서 동시에 측정하기 때문이다. 나머지 지표는 한쪽에만 존재하므로 단독 상품이지만, PM2.5는 두 관측망을 곱해 침투율이라는 파생 지표를 만든다. 이 파생 지표가 Phase 3에서 가장 비싼 데이터가 되는 이유이며, 실외를 기상 관측으로 두었다면 존재하지 않았을 값이다.

    마케팅 활용의 전제 조건

위 표의 마케팅 항목은 모두 "특정 조건을 만족하는 가구를 추출"하는 형태다. 이는 개인을 직접 식별하지 않고 코호트 단위 카운트만 반환하는 API로 구현해야 하며, 실제 광고 도달은 플랫폼이 중개하고 제조사에는 대상자 목록을 넘기지 않는다. 구체적 설계는 비식별화 파이프라인과 코호트 API 절에서 다룬다.

## 백서 5 데이터 보상 메커니즘

광범위하고 유의미한 데이터를 지속적으로 확보하려면 사용자의 자발적인 IoT 디바이스 운영이 필수적이다.

      Earn
      기여 보상
      24시간 끊김 없이 양질의 데이터를 전송하는 노드(측정기)에 매일(에폭 = 1일, KST 자정 기준) 정량으로 Wellbian 토큰(WLBN) 리워드를 지급한다. 일일 리워드는 기본 7개 × 0.78^(단계−1) × 품질 등급(0~1.2)으로 수량만 정의되며, 가치는 정의하지 않는다.

      Spend
      생태계 소비
      획득한 보상은 가전제품 구매 할인, 필터 교체 구독 서비스 결제,
        혹은 플랫폼 내 제휴 마켓에서 사용 가능하도록 설계하여 이탈을 방지한다.

    "양질의 데이터"를 정량화해야 하는 이유

제출 건수에만 비례해 보상하면 센서를 방치하거나 조작해 건수만 채우는 것이 최적 전략이 된다. 본 사양은 전송률 게이트와 가동률·이상치율·인근 노드 상관·캘리브레이션 경과일을 합산한 품질 점수(Quality Score)로 품질 등급(0 · 1.0 · 1.2)을 매기고, 에폭 예산을 고정한 뒤 등급별 정량으로 지급하며 미지급분은 재분배하거나 소각한다. 방출 총량은 단계 게이트와 10단계 누적 상한(26.65억개)으로 사전 확정된다.

    법적 고지

WLBN과 라이선스 NFT는 회사 지분·배당·이익분배·원리금 상환을 청구할 권리를 표창하지 않는다. 보상은 검증된 데이터 기여의 품질에 따라 배분되는 유틸리티 토큰이며, 지급 수량·가치·환금성은 보장되지 않는다. 본 문서의 모든 수치는 설계·검증용 가정이며 어떤 경우에도 수익에 대한 예측이나 약속이 아니다. 기기 구매는 데이터 수집 장비의 구매이며 투자 계약이 아니다.

## 백서 6 비즈니스 수익 모델

      Revenue 01
      B2B 데이터 API 구독료
      가전 제조사 및 스마트홈 플랫폼(SmartThings, ThinQ 등)을 대상으로
        지역별 실내 날씨 통계 API를 월간/연간 구독 형태로 제공.

      Revenue 02
      타겟팅 광고 수수료
      가전 제조사가 플랫폼의 데이터를 기반으로 특정 환경 요건을 갖춘 사용자에게
        맞춤형 제품을 광고할 때 발생하는 리드(Lead) 당 수수료 부과.

      Revenue 03
      인사이트 리포트 판매
      건설사 및 인테리어 기업을 대상으로 평면 및 자재에 따른
        실내 공조 효율성 분석 리포트 판매.

세 수익원은 모두 x402 데이터 상품으로 매핑된다 - 구독료는 선불 크레딧, 리드 수수료는 코호트 API 건당 과금, 리포트는 LLM 추론 건당 과금이다. 과금 수단이 하나의 프로토콜로 통일되므로 별도 빌링 시스템 없이 정산이 가능하다. 여기에 수요자가 먼저 금액을 예치하고 데이터를 요청하는 역방향 채널로 데이터 바운티가 더해진다.

## 백서 6+ 데이터 바운티 - 생명을 연장하는 데이터

현대인은 하루의 약 90%를 실내에서 보내며, WHO는 실내 공기 오염에 따른 조기 사망을 연간 수백만 명 규모로 추산한다. 실내 날씨 측정은 편의 기능이 아니라 호흡기·심혈관 질환의 예방이 시작되는 지점이다. 노드 운영자가 매일 쌓는 1분 단위 데이터는 "측정되지 않아 개입되지 못했던" 생활 공간을 관리 가능한 공간으로 바꾸는 공중보건 인프라이며, 본 생태계의 보상은 단순한 채굴 개념이 아니라 그 기여에 대한 대가다. 데이터 바운티는 이 기여를 필요로 하는 곳과 데이터 제공자를 직접 연결하는 장치다.

      Bounty 01
      선입금 에스크로
      데이터가 필요한 기업·기관(가전 제조사, 공조·환기 솔루션사, 연구기관, 지자체)이
        XRP 또는 RLUSD를 먼저 예치하고 바운티를 개설한다.
        온체인 입금이 확인된 바운티만 노드에게 공개되어 지급 불이행 위험을 구조적으로 낮춘다.

      Bounty 02
      배타적 데이터 사용권
      누구나 구매 가능한 공개 API와 달리, 예치를 마치고 바운티에 가입한
        스폰서에게만 전용 접근 키가 1회 발급되며 이 키로만 참여 노드들의
        바운티 기간 데이터에 접근할 수 있다. 조회 범위는 동의한 참여 노드 ×
        바운티 기간 × 노드별 참여 시점 이후로 한정되고, 참여 노드는 기본 에폭 보상 위에
        바운티 보상을 추가로 수령한다.

      Bounty 03
      청정 솔루션 프로모션
      참여 시 마케팅 수신에 동의(옵트인)한 노드에게는 스폰서의
        실내 날씨(날씨) 개선 솔루션(공기청정기·환기 시스템·필터 구독 등) 프로모션이 제공될 수 있다.
        동의는 바운티 단위이며 언제든 철회 가능하다.

    바운티가 완성하는 수익 순환

백서 6의 세 수익원이 플랫폼이 만든 상품을 판매하는 순방향 흐름이라면, 데이터 바운티는 수요자가 금액을 걸고 데이터를 요청하는 역방향 흐름이며, 그 대가는 단순 API 호출권이 아니라 가입 스폰서에게만 귀속되는 기간 한정 데이터 사용권이다. 스폰서 예치금은 XRPL 메인넷 온체인에서 투명하게 확인되고, 바운티 기간 종료 시 참여 기여도에 따라 정산된다. 측정하는 사람은 더 벌고, 필요한 곳은 검증된 실측 데이터를 얻고, 그 위에서 청정 솔루션이 필요한 가정에 정확히 닿는다 - 데이터가 건강 개입으로 이어지는 완결 구조다. 바운티 보드는 실행 플랫폼 wellbian.io/data에서 운영된다.

## 백서 7 향후 발전 로드맵

      PHASE 1

        초기 IoT 인프라 구축 및 얼리 어답터 대상 데이터 수집망 확보 - 완료 (2026-08 XRPL 메인넷 가동)
        디바이스 라이선스 NFT 발급 체계, 텔레메트리 수집 파이프라인, 품질 점수 기반 보상 엔진을 XRPL 메인넷에서 가동 중.
          기술 사양의 Step 1~4에 해당한다.

      PHASE 2

        메이저 가전 제조사와의 MOU 체결 및 데이터 API 상용 연동 (진행 중)
        비식별화 파이프라인 검증, 코호트 API·집계 API 개설, x402 과금 게이트웨이(RLUSD) 연동.
          Step 5~6 및 B2B 상품 설계에 해당한다.

      PHASE 3

        실외 OAQ 관측망과 결합해 실내외 연동 스마트홈 제어 솔루션으로 확장
        같은 격자의 실외 날씨과 실내 날씨을 함께 확보해 예측 기반 선제 공조 제어를 제공.
          두 관측망은 동일한 라이선스·보상·과금 인프라를 공유하며 NFTokenTaxon으로만 구분된다.

    Phase 3의 기술적 의미

실외 OAQ는 선행 지표, 실내 IAQ는 결과 지표다. 특히 PM2.5는 두 관측망이 같은 물질을 측정하므로, 같은 격자에서 두 시계열을 함께 확보하면 "외기 PM이 X일 때 이 주거 형태의 실내 PM이 Y로 변한다"는 침투율(I/O ratio) 전이 함수를 직접 학습할 수 있다. 이는 실외를 기상 관측으로 두었을 때는 얻을 수 없는 값이며, 제조사가 살 수 있는 가장 비싼 형태의 데이터가 된다. 본 기술 사양이 두 관측망을 하나의 원장·하나의 과금 체계 위에 올린 이유다.

## 기술 개요 플랫폼 개요

백서의 3주체 구조를 온체인으로 구현하면 두 개의 경제 루프가 하나의 원장 위에서 연결된다. 공급 측은 IAQ 노드가 실내 환경 데이터를 올리고 토큰 보상을 받는 DePIN 루프이고, 수요 측은 가전 제조사와 AI 에이전트가 데이터 API를 호출하며 토큰을 지불하는 x402 루프다. 두 루프는 동일 원장의 AMM 풀을 통해 유동성이 연결된다.

      Supply Loop · DePIN
      데이터를 올리면 토큰을 받는다
      인가된 IoT 기기(NFT 라이선스 보유)가 IAQ 측정값을 전송하면,
        품질 점수를 산출해 1일(KST) 에폭 단위로 적립하고, 24시간 홀드 후 Hot Wallet(운영 지갑)이 WLBN을 지급한다.
        노드 확산 = 관측망 밀도 증가.

      Demand Loop · x402
      데이터를 쓰면 토큰을 낸다
      제조사·AI 에이전트가 집계·코호트·리포트 API를 호출하면 HTTP 402로 결제를 요구하고,
        온체인 트랜잭션 해시 검증 후 응답을 반환한다. 호출량 = 토큰 수요.

### 두 관측망, 하나의 인프라

실내 IAQ 관측망과 실외 OAQ 관측망은 센서 구성과 데이터 스키마만 다를 뿐, 라이선스 발급·보상 지급·데이터 과금 경로는 완전히 동일하다. 구현상으로는 NFTokenTaxon과 텔레메트리 스키마 버전으로만 구분되며, 지갑·토큰·AMM·x402 게이트웨이는 단일 인스턴스를 공유한다.

    | 구분 | 실내 IAQ 관측망 | 실외 OAQ 관측망 |

      | 측정 지표 | PM2.5/10, CO₂(NDIR), TVOC, 온·습도(체감온도 유도) | PM2.5/10, O₃, NO₂, SO₂, CO, 온·습도, 풍속·풍향 |

      | 설치 주체 | 일반 가구 · 사무실 · 상업시설 | 건물 옥상 · 가로변 · 학교 · 산업단지 인근 |

      | NFTokenTaxon | 3026 | 2026 |

      | 주 수요자 | 가전 제조사 · 스마트홈 플랫폼 | 지자체 · 환경 서비스 · AI 에이전트 |

      | 공유 인프라 | Issuer/Hot 지갑 · WLBN·RLUSD · AMM 풀 · x402 게이트웨이 · 소각 배치 | 동일 (단일 인스턴스 공유) |

### 메인넷에서 검증된 5가지 명제

    | # | 명제 | 검증 수단 | XRPL 기능 |

      | 1 | 기기 신원을 온체인으로 위조 불가능하게 증명할 수 있다 | verifyDeviceLicense() 온체인 조회 | XLS-20 NFT |

      | 2 | 데이터 기여에 대한 마이크로 보상을 무인 자동화할 수 있다 | 에폭 정산 → 배치 Payment (24h 클레임 홀드) | Issued Asset (IOU) |

      | 3 | 토큰과 스테이블코인 간 스왑이 원장 내에서 가능하다 | Mainnet AMM 풀에서 RLUSD ↔ WLBN 실거래 스왑 | XLS-30 AMM |

      | 4 | AI 에이전트가 사람 개입 없이 API 사용료를 결제할 수 있다 | 402 응답 → 결제 → 재요청 → 200 | Payment + tx 조회 |

      | 5 | 개인을 식별하지 않고도 제조사가 쓸 만한 코호트를 추출할 수 있다 | k-익명성 미달 시 응답 거부, 카운트만 반환 | 오프체인 (온체인 미기록) |

    왜 XRPL인가

마이크로페이먼트가 성립하려면 수수료가 결제 금액보다 압도적으로 작아야 한다. XRPL의 기본 트랜잭션 비용은 10 drops(0.00001 XRP)이고 확정(finality)까지 3~5초다. 0.002 RLUSD 단위의 집계 API 과금과, 수만 개 노드에 대한 에폭 배치 보상 지급이 수수료에 잡아먹히지 않고 성립하는 이유이며, NFT·IOU·AMM이 스마트컨트랙트 없이 네이티브 트랜잭션 타입으로 제공되어 감사 대상 코드 표면이 작아 운영 리스크가 낮다.

## 기술 개요 시스템 아키텍처

오프체인 3계층(기기 · 게이트웨이 · 소비자)과 온체인 1계층(XRPL 원장)으로 구성된다.

PHYSICAL BACKEND CONSUMER LEDGER 실내 IAQ 노드 PM·CO₂·TVOC·온습도 실외 OAQ 노드 PM·O₃·NO₂·SO₂·CO Device Wallet NFT 라이선스 보유 DePIN Oracle API GET api.wellbianlabs.io/v1/c license → quality → reward x402 Gateway /api/data/* · /api/v1/* 402 → verify tx → 200 집계 · 코호트 · LLM 비식별화 · k-익명성 게이트 인사이트 리포트 생성 가전 제조사 · AI Agent x402 자율 결제 클라이언트 스마트홈 플랫폼 RLUSD → WLBN 스왑 Hot Wallet(운영 지갑) rhTTtx8…web1 - 보상 지급 · 결제 수취 · 소각 배치 XRP LEDGER XLS-20 NFT 기기 라이선스 WLBN / RLUSD Issued Asset + TrustLine XLS-30 AMM WLBN ↔ RLUSD 풀 Issuer (Cold) 발행 · 소각 수취 기기 서명 verify NFT reward / burn swap payment Fig 1. 4계층 아키텍처 - 파란선 = DePIN 공급 루프, 녹색선 = x402 수요 루프 ### 지갑 역할 분리 | 지갑 | 역할 | 키 보관 | 주요 트랜잭션 | | Issuer (Cold)rDJz8WJhsKgydqJzSZXMpsJot3eRmSkR5 | Wellbian(WLBN) 발행 주체, NFT 발행자, 소각 수취처. RLUSD는 Ripple 발행(rMxCKbEDwqr76QuheSUMdEGf4B9xJ8m5De)을 TrustLine으로 수용. | 오프라인 / HSM 가정 | AccountSet, NFTokenMint, 최초 Payment | | Hot Wallet(운영 지갑)rhTTtx8YyKzcmPDsoroYXuHdGMThrnweb1 | 에폭 보상 지급, x402 결제 수취, 소각 배치 실행 | 서버 환경변수 (KMS 권장) | Payment, AMMDeposit | | IoT Device (ARC-600DA) | 기기 지갑, 라이선스 NFT 보유자 | 기기 시큐어 엘리먼트 가정 | TrustSet, NFTokenAcceptOffer | | AI Agent · 제조사 클라이언트 | x402 클라이언트, API 사용료 지불자 | 에이전트 로컬 | TrustSet, Payment | Cold / Hot 분리 원칙 Issuer 키가 유출되면 무제한 발행이 가능하므로, 런타임 서버는 절대 Issuer 시드를 로드하지 않는다. 발행은 1회성 부트스트랩 스크립트에서만 수행하고, 이후 모든 상시 트랜잭션은 Hot Wallet(운영 지갑)이 담당한다. 이 경계는 코드 레벨(모듈 분리 + 별도 키 주입)로 강제되며, 운영 서버는 Issuer 시드를 로드하지 않는다.

## 기술 개요 기술 스택 & 네트워크

    LanguageTypeScript / Node.js 20+
      Vercel Runtime에서 실행된다. 본 문서는 TS 기준으로 기술한다.
    XRPL SDKxrpl.js v3+
      WebSocket Client, Wallet, autofill/sign/submitAndWait 사용.
    APINext.js 15 App Router (Vercel)
      x402 게이트웨이는 Route Handler 미들웨어로 구현. 배치·에폭 정산은 Vercel Cron.

### 네트워크 파라미터

    | 항목 | 값 | 비고 |

      | WebSocket | wss://xrplcluster.com | Mainnet 공용 클러스터 노드 |

      | JSON-RPC | https://xrplcluster.com | 단발 조회용 |

      | 계정 활성화 | Payment (외부 입금) | 계정 활성화 · Base Reserve 이상 XRP 사전 입금(Faucet 없음) |

      | Explorer | livenet.xrpl.org | tx / account 시각 검증 |

      | Base Reserve | 1 XRP | Mainnet 기준 계정 유지 최소 잔액 |

      | Owner Reserve | 0.2 XRP / 오브젝트 | TrustLine · NFT Offer · AMM 등 각각 차감 |

      | Finality | 3~5초 | 비동기 대기 및 폴링 설계 필수 |

### 통화 코드 인코딩

XRPL의 currency 필드는 3자리 ASCII이거나 40자리 hex(160-bit)여야 한다. WLBN·RLUSD 모두 3자를 초과하므로 hex 인코딩이 필수다. 이 값을 상수로 고정해두지 않으면 TrustSet과 Payment의 통화가 불일치해 tecNO_LINE / tecPATH_DRY가 발생한다.

  src/config/currency.tstypescript
```

// ASCII → 20-byte hex (right-padded with zeros)
export const toCurrencyHex = (code: string): string => {
  if (code.length

### 환경 변수

  .env.exampledotenv
```

XRPL_ENDPOINT=wss://xrplcluster.com
XRPL_NETWORK=mainnet

# Cold - bootstrap script only. Never inject into the runtime server
ISSUER_SEED=sEd..............................

# Hot - API server runtime
HOT_SEED=sEd..............................
HOT_WALLET_ADDRESS=r..............................

# Counterparties
TREASURY_ADDRESS=rPZFY2yLgqjmBrxtxWHXnB1doBt7gu7xin
RLUSD_ISSUER_ADDRESS=rMxCKbEDwqr76QuheSUMdEGf4B9xJ8m5De

# Pricing policy (RLUSD)
X402_PRICE_AGGREGATE_RLUSD=0.002
X402_PRICE_COHORT_RLUSD=0.05
X402_PRICE_REPORT_RLUSD=0.5
X402_DEST_TAG=402001

# Reward policy
REWARD_BASE_DAILY_WLBN=7
PHASE_DECAY=0.78
EXCELLENT_BONUS_WLBN=1.4
NODE_DAILY_CAP_WLBN=10.5
BUDGET_PER_NODE_WLBN=7.14
CLAIM_HOLD_HOURS=24
BURN_RATIO=0.5

```

    시드 취급

.env는 반드시 .gitignore에 등록한다. 운영(Mainnet) 시드를 커밋하면 저장소가 노출되는 순간 서명 권한이 유출된다. 운영 전환 시에는 .env 대신 KMS/Vault에서 런타임 주입하고, 서명은 Hot Wallet 키에 한정한다.

## 기술 개요 모듈 구조

각 XRPL 기능을 독립 모듈로 분리해 단계별 테스트와 교체가 가능하도록 한다.

  src/
├─ config/
│   ├─ env.ts            # env loading + validation (zod)
│   └─ currency.ts       # WLBN / RLUSD hex constants
├─ xrpl/
│   ├─ client.ts         # Client singleton · reconnect · graceful shutdown
│   ├─ wallet.ts         # Step 1 - wallet create / load / fund
│   ├─ token.ts          # Step 2 - AccountSet · TrustSet · IOU Payment
│   ├─ nft.ts            # Step 3 - Mint / Offer / Accept / verifyDeviceLicense
│   ├─ amm.ts            # Step 5 - AMMCreate · AMMDeposit · path swap
│   └─ submit.ts         # shared autofill→sign→submitAndWait + tes check
├─ services/
│   ├─ oracle.ts         # Step 4 - telemetry intake · validation · reward
│   ├─ x402.ts           # Step 6 - 402 challenge · tx verification
│   ├─ llm.ts            # IAQ insight report generation (0.5 RLUSD)
│   └─ burn.ts           # Step 6 - 50% burn batch job
├─ api/
│   ├─ route.ingest.ts   # GET /v1/c?<base64>  (Supabase Edge Function proxy)
│   ├─ route.data.ts    # GET /api/data/aggregates (x402)
│   └─ middleware.x402.ts# 402 gate middleware
├─ scripts/
│   ├─ 01-bootstrap-wallets.ts
│   ├─ 02-issue-tokens.ts
│   ├─ 03-mint-license.ts
│   ├─ 05-create-amm.ts
│   └─ x402-client.ts    # reference x402 client
└─ server.ts

### 공통 제출 헬퍼

모든 트랜잭션이 tesSUCCESS인지, 그리고 validated: true인지 한 곳에서 검증한다. 이 래퍼를 강제하지 않으면 "제출은 됐지만 원장에 반영되지 않은" 상태를 성공으로 오인하게 된다.

  src/xrpl/submit.tstypescript
```

import { Client, Wallet, SubmittableTransaction, TxResponse } from 'xrpl';

export class TxError extends Error {
  constructor(public code: string, public hash?: string) {
    super(`XRPL tx failed: ${code}${hash ? ` (${hash})` : ''}`);
  }
}

/** autofill → sign → submitAndWait → verify tesSUCCESS + validated */
export async function submit(
  client: Client, wallet: Wallet, tx: SubmittableTransaction,
): Promise {
  const prepared = await client.autofill(tx);
  const signed = wallet.sign(prepared);
  const res = await client.submitAndWait(signed.tx_blob);

  const meta = res.result.meta;
  const code = typeof meta === 'object' && meta !== null
    ? (meta as { TransactionResult: string }).TransactionResult
    : 'UNKNOWN';

  if (code !== 'tesSUCCESS') throw new TxError(code, res.result.hash);
  if (res.result.validated !== true) throw new TxError('NOT_VALIDATED', res.result.hash);
  return res;
}

```

## 날씨 데이터 사양 센서 · 텔레메트리 스키마

백서 4장의 지표를 실제 노드가 측정 가능한 물리량과 페이로드 형식으로 확정한다. 실내(IAQ)와 실외(OAQ) 스키마는 지표 목록만 다르고 동일한 봉투(envelope) 구조를 공유하며, 각각 버전 필드를 갖는다.

### 측정 지표 사양

    | 지표 | 필드 | 센서 방식 | 측정 범위 | 목표 정확도 |

      | 미세먼지 | pm25 pm10 | 광산란 (PMS) | 0 ~ 1000 µg/m³ | ±10% (≥100 구간) |

      | 이산화탄소 | co2 | NDIR | 400 ~ 5000 ppm | ±(50 ppm + 3%) |

      | 총휘발성유기화합물 | tvoc | MOX 반도체식 | 0 ~ 60000 ppb | 상대 지수 (절대값 비보증) |

      | 온도 | tempC | 디지털 온습도 복합 | −10 ~ 60 °C | ±0.3 °C |

      | 상대습도 | rh | 정전용량식 | 0 ~ 100 %RH | ±2 %RH |

      | 체감온도 유도 | feelsLikeC | 온도 + 상대습도에서 계산 | −10 ~ 60 °C | 입력 오차 전파 |

      | 절대습도 유도 | ah | 온도 + 상대습도에서 계산 | 0 ~ 60 g/m³ | 입력 오차 전파 |

### 실외 OAQ 센서 사양

실외 노드는 대기환경기준 물질을 측정한다. PM2.5·PM10은 실내 노드와 같은 물질을 같은 방식으로 측정하므로, 같은 격자의 실내외 값을 직접 비교해 침투율(I/O ratio)을 산출할 수 있다.

    | 지표 | 필드 | 센서 방식 | 측정 범위 | tier |

      | 미세먼지 | pm25 pm10 | 광산란 (PMS) | 0 ~ 1000 µg/m³ | oaq-standard |

      | 오존 | o3 | 전기화학식 | 0 ~ 500 ppb | oaq-standard |

      | 이산화질소 | no2 | 전기화학식 | 0 ~ 500 ppb | oaq-standard |

      | 온도 · 상대습도 | tempC rh | 디지털 복합 | −30 ~ 60 °C / 0 ~ 100 %RH | oaq-standard |

      | 아황산가스 | so2 | 전기화학식 | 0 ~ 200 ppb | oaq-pro |

      | 일산화탄소 | co | 전기화학식 | 0 ~ 50 ppm | oaq-pro |

      | 풍속 · 풍향 | wind windDir | 초음파식 | 0 ~ 60 m/s / 0 ~ 360° | oaq-pro |

    실외 센서는 보정 부담이 훨씬 크다

전기화학식 가스 센서(O₃·NO₂·SO₂·CO)는 온도·습도에 강하게 교차 반응하고 수명이 1~2년으로 짧다. 실내 노드보다 드리프트가 빠르므로 품질 점수의 보정(calibration, 15점)·상관(correlation, 20점) 가중을 실외에 더 크게 적용하고, 인근 국가 측정망(에어코리아) 값과의 상관을 보정 계수 산출에 함께 쓴다.

실내 1단계 기본 리워드는 일 7개(정상 등급)이며, 실외 라인은 기존 홀더에게 우선 제안된다. 실외 리워드 수량은 실외 모델 출시 시 공표한다.

### 절대습도는 왜 유도해야 하는가

백서 4장이 첫 번째 지표로 절대습도를 명시한 것은 정확한 선택이다. 상대습도는 온도 종속량이라 제습 부하 계산에 직접 쓸 수 없다. 같은 60 %RH라도 20 °C에서는 약 10.3 g/m³, 30 °C에서는 약 18.2 g/m³로 실제 수분량이 1.8배 차이난다. 제습기가 뽑아내야 할 물의 양은 후자가 압도적으로 많다. 따라서 노드는 상대습도를 전송하되, 서버는 수신 즉시 절대습도를 유도해 저장한다.

  src/services/iaq/humidity.tstypescript
```

/**
 * Absolute humidity (g/m³) via the Magnus approximation.
 * Dehumidification algorithms and condensation prediction take this, not relative humidity.
 */
export function absoluteHumidity(tempC: number, rh: number): number {
  const es = 6.112 * Math.exp((17.67 * tempC) / (tempC + 243.5));  // saturation vapor pressure, hPa
  const e = es * (rh / 100);                                        // actual vapor pressure, hPa
  return (216.7 * e) / (tempC + 273.15);                            // g/m³
}

/** Dew point (°C) - direct input to the condensation-prevention model */
export function dewPoint(tempC: number, rh: number): number {
  const gamma = (17.67 * tempC) / (tempC + 243.5) + Math.log(rh / 100);
  return (243.5 * gamma) / (17.67 - gamma);
}

// absoluteHumidity(20, 60) → 10.35    absoluteHumidity(30, 60) → 18.21

```

### 텔레메트리 페이로드

노드는 1분 주기로 측정·전송하며, 서버는 시간 단위 집계(TelemetryHourly)를 생성하고 원시 데이터는 7일 보관한다. 전송 단위마다 기기 키로 서명하며, 서버는 서명자 주소와 deviceAddress가 일치하는지 확인한 뒤 라이선스 NFT를 조회한다. 서명이 없으면 NFT를 보유한 주소를 사칭한 임의 페이로드를 막을 수 없다.

  GET https://api.wellbianlabs.io/v1/c?<base64>json
```

{
  "schema": "wellbian-iaq-telemetry/v1",
  "deviceAddress": "rDeviceWalletAddress...",
  "signature": "3045022100...",
  "batch": [
    {
      "ts": 1786195200,
      "pm25": 11.4, "pm10": 15.8,
      "co2": 812, "tvoc": 143,
      "tempC": 23.4, "rh": 58.1, "feelsLikeC": 24.1
    },
    {
      "ts": 1786195260,
      "pm25": 11.1, "pm10": 15.2,
      "co2": 845, "tvoc": 139,
      "tempC": 23.4, "rh": 58.4, "feelsLikeC": 24.2
    }
  ]
}

```

### 수신 검증 순서

    | # | 검증 | 실패 시 | 비고 |

      | 1 | 서명이 deviceAddress의 공개키와 일치 | 401 BAD_SIGNATURE | 온체인 조회 없음 - 가장 싼 검사를 먼저 |

      | 2 | 배치 내 ts가 단조 증가, 서버 시각 ±10분 이내 | 422 TIMESTAMP_SKEW | 과거 데이터 재전송(리플레이) 차단 |

      | 3 | 각 지표가 물리적 범위 이내 | 422 OUT_OF_RANGE | 센서 고장·조작 1차 필터 |

      | 4 | 직전 값 대비 급변율이 임계 이내 | 이상치 플래그 | 거부하지 않고 품질 점수에 반영 |

      | 5 | 라이선스 NFT 보유 (캐시 60초) | 403 NO_VALID_LICENSE | 가장 비싼 검사를 마지막에 |

      | 6 | 제출 주기 하한 준수 | 429 RATE_LIMITED | 보상 파밍 방지 |

    원시 데이터는 온체인에 올리지 않는다

1분 해상도 × 노드 수만큼의 측정값을 원장에 기록하는 것은 비용·성능 모두 불가능하다. 원시 데이터는 오프체인 저장소에 두고, 정산 주기마다 배치 머클루트만 XRPL 트랜잭션의 Memos 필드에 기록한다. 이후 특정 측정값의 무결성이 다투어지면 머클 증명으로 "그 시점 배치에 이 값이 포함되어 있었다"를 검증할 수 있다. 백서 3.2의 "위변조 없이 저장"은 이 방식으로 달성된다.

## 날씨 데이터 사양 라이선스 계층 · 품질 점수

백서 5장의 "양질의 데이터"를 정량화하고, 리워드를 수량으로만 정의해 방출 총량이 단계 게이트와 10단계 누적 상한(26.65억개)으로 사전 확정되도록 한다.

### Taxon 및 티어 체계

라이선스 NFT는 NFTokenTaxon으로 관측망을 구분하고, 메타데이터의 license.tier로 센서 구성 등급을 표현한다. 티어는 센서 구성과 접근 가능한 데이터 상품을 구분하며, 리워드 수량은 티어가 아니라 단계와 품질 등급으로 정해진다.

    | Taxon | 관측망 | Tier | 필수 센서 | 리워드 |

      | 3026 | 실내 IAQ | iaq-lite | PM2.5/10, 온·습도 | 미정 (출시 시 확정) |

      | 3026 | 실내 IAQ | iaq-standard | PM2.5, PM10, CO₂(NDIR), TVOC, 온·습도 (ARC-600DA) | 1단계 기본 일 7개 (정상 등급 · 지급 보장 아님) |

      | 3026 | 실내 IAQ | iaq-pro | 추가 센서 미정 (출시 시 확정) | 동일 규칙 (단계 × 품질 등급) |

      | 2026 | 실외 OAQ | oaq-standard | PM2.5/10, O₃, NO₂, 온·습도 | 미정 (실외 출시 시 확정) |

      | 2026 | 실외 OAQ | oaq-pro | + SO₂, CO, 풍속·풍향 | 미정 (실외 출시 시 확정) |

### 디바이스 메타데이터 (IPFS)

주거 환경 속성은 백서 2장이 지적한 "평형·위치·채광에 따른 공조 효율" 분석에 필수적이다. 다만 이 속성들의 조합은 그 자체로 가구를 특정할 수 있으므로, 메타데이터 단계에서부터 연속값이 아닌 버킷으로만 기록한다.

  ipfs://Qm…/iaq-device.jsonjson
```

{
  "schema": "wellbian-iaq-device/v1",
  "name": "Wellbian IAQ Node #10427",
  "description": "Indoor Air Quality license",
  "device": {
    "serial": "KW-IAQ-10427",
    "model": "ARC-600DA",
    "sensors": ["pm25", "pm10", "co2", "tvoc", "temp", "rh"],
    "installed_at": "2026-05-02"
  },
  "context": {
    "geohash5": "wydm9",
    "space_type": "residential",
    "area_bucket": "60-85sqm",
    "floor_bucket": "5-10F",
    "build_year_bucket": "2010-2019",
    "orientation": "S"
  },
  "license": { "tier": "iaq-standard", "valid_until": "2029-05-02" }
}

```

    메타데이터는 공개된다

NFT의 URI는 온체인에 기록되고 IPFS 콘텐츠는 누구나 조회할 수 있다. 따라서 정확한 주소·동호수·연속 좌표를 여기에 넣으면 영구히 공개된다. geohash5(약 4.9 km 격자)와 버킷 값만 기록하고, 정밀 주소는 수집·저장하지 않는다. 위치는 동(洞) 단위로만 보관하며 좌표는 반올림해 저장한다.

### 품질 점수 (Quality Score)

백서 5장의 "24시간 끊김 없이 양질의 데이터"를 4개 관측 가능한 지표로 분해한다. 특히 인근 노드 상관계수는 센서를 실외에 방치하거나 값을 합성하는 조작을 잡아내는 핵심 항목이다 - 같은 지역의 다른 노드와 온도·PM 추세가 전혀 맞지 않으면 그 노드의 데이터는 신뢰할 수 없다.

  src/services/iaq/quality.tstypescript
```

export interface QualityInputs {
  uptimeRatio: number;    // actual submissions / expected submissions, last 24h
  outlierRatio: number;   // share of out-of-range + sudden-jump outliers
  neighborCorr: number;   // temp/PM trend correlation with nodes within 2km (-1 to 1)
  calibrationAgeDays: number;  // days since last calibration
}

const clamp01 = (x: number) => Math.min(1, Math.max(0, x));

/** 0 to 1. Weights sum to 1.0 */
export function qualityScore(q: QualityInputs): number {
  const uptime = clamp01(q.uptimeRatio);
  if (uptime

### 고정 예산 배분 (Emission Cap)

노드 1대당 금액을 지급하면 시세에 따라 방출이 요동친다. 대신 수량을 고정한다 - 에폭 예산은 활성 지점 수 × 7.14 × 단계 계수로 정해지고, 각 노드는 품질 등급에 따라 정상 7 · 우수 8.4 · 미달 0을 1차 지급받는다. 미지급분은 가중치 비례로 재분배하되 지점당 10.5개를 상한으로 하고 초과분은 소각한다.

  src/services/iaq/reward.tstypescript
```

import { qualityScore, QualityInputs } from './quality';

export const BASE_DAILY      = 7;      // 정상 등급 1차 지급 (수량 · 시세 무관)
export const EXCELLENT_DAILY = 8.4;    // 품질 상위 10% (+1.4)
export const NODE_CAP        = 10.5;   // 재분배 후 지점당 상한 - 초과분 소각
export const BUDGET_PER_NODE = 7.14;   // 에폭 예산 = 활성 지점 × 7.14 × 단계 계수
export const PHASE_DECAY     = 0.78;   // 단계마다 22% 체감

export type Grade = 0 | 1 | 1.2;
export interface NodeEpochStats { address: string; quality: QualityInputs; txRate: number; licenseHeld: boolean; }

/** 전송률 게이트 → 품질 점수 상위 10% 우수. 게이트 미달은 0 */
export function grade(n: NodeEpochStats, top10Cut: number): Grade {
  if (n.txRate = top10Cut ? 1.2 : 1;
}

/** 에폭 정산: 총 지급 + 소각 = 예산 (항상 성립) */
export function distribute(nodes: NodeEpochStats[], phase: number) {
  const factor = Math.pow(PHASE_DECAY, phase - 1);
  const active = nodes.filter((n) => n.licenseHeld);
  const budget = active.length * BUDGET_PER_NODE * factor;
  const cut = percentile(active.map((n) => qualityScore(n.quality)), 0.9);

  // 1) 1차 지급 - 정상 7 · 우수 8.4 · 미달 0 (80~95% 전송률은 선형 감액)
  const paid = active.map((n) => {
    const g = grade(n, cut);
    const ramp = n.txRate >= 0.95 ? 1 : n.txRate  s + p.amount, 0);

  // 2) 미지급분 재분배 - 가중치 비례, 지점당 상한 10.5 × factor, 초과분 소각
  const w = paid.reduce((s, p) => s + p.g, 0);
  let burn = 0;
  for (const p of paid) {
    if (p.g === 0 || w === 0) continue;
    const target = Math.min(p.amount + unpaid * (p.g / w), NODE_CAP * factor);
    burn += p.amount + unpaid * (p.g / w) - target;
    p.amount = target;
  }
  return { paid, burn, budget };   // Σ paid.amount + burn === budget
}

```

    잔여 리스크 "보상 인플레이션" 해소

이 설계로 잔여 리스크 표의 보상 인플레이션 항목이 구조적으로 닫힌다. 10단계 누적 방출은 26.65억개로 사전 확정되고(노드 리워드 1·2기 46억 이내), 단계 체감 22%로 지점이 늘수록 지점당 수량이 줄어든다. 품질 미달분은 지급하지 않고 소각하므로 품질이 나쁠수록 총 방출이 준다. 단계 진입에는 수요 조건이 병기되어 수요 없이 방출만 늘어나는 경로가 없다.

    배치 지급으로 전환됨

고정 예산 배분은 에폭 종료 시점에 전체 노드의 점수를 알아야 계산할 수 있다. 따라서 Step 4의 요청당 즉시 지급 방식은 IAQ 관측망에서는 사용하지 않는다. 텔레메트리 수신은 202 Accepted로 즉시 응답하고, 보상은 에폭(1일, KST 자정 마감) 배치로 distribute() 결과를 적립하고, 적립분은 24시간 홀드 후 클레임할 수 있다. 이는 Sequence 경합 대응과도 자연스럽게 맞물린다.

## 날씨 데이터 사양 비식별화 파이프라인

백서 3.2의 "개인정보를 비식별화 처리한 후 메타데이터로 가공"을 5단계 변환으로 구체화한다. 실내 데이터는 재실 여부·생활 패턴이 그대로 드러나는 민감 정보이므로, 실외 날씨 데이터보다 훨씬 강한 처리가 필요하다.

    실내 데이터가 노출하는 것

CO₂ 농도 시계열 하나만으로 재실 인원수, 취침·기상 시각, 부재 기간이 상당히 정확하게 추정된다. PM2.5 급등은 조리 시각을, TVOC 패턴은 청소·인테리어 시공을 드러낸다. 이 데이터가 정확한 위치와 결합되면 사실상 특정 가구의 생활 로그가 된다. 따라서 정밀 위치·원시 시간해상도·개별 노드 식별자가 동시에 외부로 나가는 경로는 존재해서는 안 된다.

### 5단계 변환

    | # | 단계 | 원시 | 공개 |

      | P1 | 공간 일반화 | 동(洞) 단위 위치 · 반올림 좌표 | geohash5 - 약 4.9 km 격자 |

      | P2 | 시간 집계 | 1분 해상도 측정값 | 1시간 단위 평균·최대·분산 |

      | P3 | 식별자 치환 | rDeviceAddress… | HMAC(NFTokenID, epochSalt) - 에폭마다 회전 |

      | P4 | k-익명성 게이트 | - | 버킷 내 노드

P3의 에폭마다 회전하는 salt가 중요하다. 고정 salt로 해시하면 가명 ID가 영구 식별자가 되어 장기 추적이 가능해진다. 에폭마다 salt를 교체하면 에폭 간 연결이 끊긴다. 다만 이 경우 장기 시계열 분석이 불가능해지므로, 장기 분석은 가명 ID가 아닌 버킷 단위 집계로만 제공한다.

  src/services/iaq/anonymize.tstypescript
```

export const K_MIN = 5;

export interface NodeSummary { geohash5: string; tier: string; hourly: HourlyStats; }
export interface Bucket { geohash: string; precision: number; n: number; stats: HourlyStats; }

/**
 * P4 - roll up to coarser cells until k-anonymity is satisfied.
 * If geohash3 (~156km) is still short, return null and the API refuses to respond.
 */
export function rollupToKAnonymous(nodes: NodeSummary[], geohash: string): Bucket | null {
  for (let precision = Math.min(5, geohash.length); precision >= 3; precision--) {
    const prefix = geohash.slice(0, precision);
    const members = nodes.filter((n) => n.geohash5.startsWith(prefix));

    if (members.length >= K_MIN) {
      return { geohash: prefix, precision, n: members.length, stats: aggregate(members) };
    }
  }
  return null;
}

/** P3 - derive a pseudonymous ID from a salt rotated each epoch */
export function pseudonym(nftokenId: string, epochSalt: string): string {
  return createHmac('sha256', epochSalt).update(nftokenId).digest('hex').slice(0, 16);
}

```

### 차분 공격 방어

k-익명성만으로는 부족하다. 필터를 조금씩 바꿔가며 반복 질의하면 두 결과의 차이로 개별 가구를 분리해낼 수 있다. 예를 들어 "평형 60-85 & 5-10층" 코호트와 "평형 60-85 & 5-10층 & 남향" 코호트의 차이가 1이면 그 1가구의 속성이 그대로 드러난다.

    | 방어 | 규칙 | 효과 |

      | 카운트 반올림 | 응답 코호트 크기를 5 단위로 반올림 | 차이가 1인 질의쌍이 같은 값을 반환 |

      | 필터 개수 상한 | 동시 적용 가능한 속성 필터 최대 3개 | 조합 폭발로 인한 좁은 코호트 생성 차단 |

      | 질의 로그 · 예산 | 구매자별 일일 질의 수 상한, 유사 질의 반복 탐지 | 반복 차분 시도 자체를 제한 |

      | 최소 코호트 | 반올림 후에도 25 미만이면 거부 | k=5보다 보수적인 실질 하한 |

### 데이터 접근 권한 매트릭스

    | 데이터 | 기여자 본인 | 가전 제조사 | 공개 API |

      | 자기 노드 측정 원시값 (보관 기간 한정) | 전체 | 불가 | 불가 |

      | 동 단위 위치 · 반올림 좌표(정밀 주소 미보관) | 전체 | 불가 | 불가 |

      | 격자 1시간 집계 | 전체 | 구독 | 제한 공개 (지연 24h) |

      | 코호트 카운트 | - | 건당 과금 | 불가 |

      | 대상자 연락처 · 노드 목록 | - | 불가 | 불가 |

    광고 도달은 플랫폼이 중개한다

백서 6장의 "타겟팅 광고 수수료" 모델에서 제조사에 넘어가는 것은 코호트 크기와 광고 소재뿐이다. 대상자 목록은 절대 이전되지 않는다. 제조사는 "조건 X를 만족하는 가구 1,240곳에 이 소재를 노출" 요청을 넣고, 플랫폼이 자체 앱·알림 채널로 도달시킨 뒤 리드 수만 정산한다. 이 경계가 무너지면 생태계 전체가 개인정보 처리 리스크에 직접 노출된다.

    법적 검토 필요

본 절은 기술적 설계이며 법률 자문이 아니다. 국내 서비스 시 실내 환경 데이터의 개인정보 해당 여부, 가명정보 처리 요건, 수집·이용 동의 문구, 제3자 제공과 위탁의 구분, 그리고 보상 지급이 대가성 있는 데이터 판매로 해석될 여지에 대해 착수 전 개인정보 전문 법률 검토가 반드시 선행되어야 한다.

## 날씨 데이터 사양 B2B 데이터 상품 · 과금 설계

백서 6장의 세 수익원을 x402 프로토콜 위의 세 가지 상품으로 매핑한다. 과금 수단이 하나로 통일되므로 별도 빌링 시스템 없이 온체인 정산이 성립한다.

    | 수익원 (백서 6장) | 엔드포인트 | 응답 | 과금 | 참고 단가 |

      | API 구독료 | GET https://wellbian.io/api/data/aggregates | 격자·시간대별 지표 집계 | 선불 크레딧 차감 | 0.002 RLUSD / call |

      | 타겟팅 광고 수수료 | POST https://wellbian.io/api/data/cohort (planned) | 조건 만족 코호트 크기만 | 건당 결제 | 0.05 RLUSD / query |

      | 인사이트 리포트 | POST https://wellbian.io/api/data/report (planned) | LLM 분석 리포트 | 건당 결제 | 0.5 RLUSD |

      | (R&D 데이터셋) | POST https://wellbian.io/api/data/dataset (planned) | 비식별 학습셋 스냅샷 URL | 건당 결제 (고액) | 협의 · 계약 기반 |

### 코호트 API - 개인을 노출하지 않는 타겟팅

백서 4장의 마케팅 활용은 전부 이 엔드포인트 하나로 수렴한다. 핵심은 응답에 노드 목록이 없다는 것이다. 크기와 대표 통계만 반환한다.

  POST /api/data/cohortjson
```

{
  "region": "wydm",
  "window": { "from": "2026-08-01", "to": "2026-08-31" },
  "filters": [
    { "metric": "ah", "op": "gte", "value": 14.0, "duty": 0.6 },
    { "metric": "area_bucket", "op": "in", "value": ["60-85sqm", "85-135sqm"] }
  ]
}

```

  200 OK - monsoon high-humidity households (WP ch.4 dehumidifier scenario)json
```

{
  "cohortSize": 1240,
  "rounding": 5,
  "kAnonymity": { "min": 5, "satisfied": true, "effectiveMin": 25 },
  "profile": {
    "ah_p50": 15.8, "ah_p90": 19.2,
    "hours_above_threshold_p50": 431,
    "top_area_bucket": "60-85sqm"
  },
  "reachable": true,
  "note": "No subject list is provided. Ad delivery is brokered by the platform."
}

```

  422 - cohort too narrowjson
```

{
  "error": "COHORT_TOO_NARROW",
  "minimum": 25,
  "hint": "Relax the filters or reduce region precision. The actual size is not returned."
}

```

### 구독형을 x402 위에 올리는 법 - 선불 크레딧

원 x402는 호출 1건 = 결제 1건이다. 집계 API처럼 초당 수십 회 호출되는 상품에 이를 그대로 적용하면 매 호출마다 3~5초의 원장 확정을 기다려야 해 성립하지 않는다. 따라서 402 응답에 건당 결제와 선불 크레딧 두 가지 scheme을 동시에 제시하고, 구독 고객은 후자를 택하게 한다.

  HTTP/1.1 402 - two payment schemes offeredjson
```

{
  "x402Version": 1,
  "resource": "/api/data/aggregates",
  "accepts": [
    {
      "scheme": "xrpl-payment",
      "network": "xrpl-mainnet",
      "payTo": "rhTTtx8YyKzcmPDsoroYXuHdGMThrnweb1",
      "asset": { "currency": "RLUSD", "issuer": "rMxCKbEDwqr76QuheSUMdEGf4B9xJ8m5De", "value": "0.002" },
      "destinationTag": 402010,
      "maxTimeoutSeconds": 120
    },
    {
      "scheme": "xrpl-credit",
      "network": "xrpl-mainnet",
      "payTo": "rhTTtx8YyKzcmPDsoroYXuHdGMThrnweb1",
      "topUpMinimum": { "currency": "RLUSD", "issuer": "rMxCKbEDwqr76QuheSUMdEGf4B9xJ8m5De", "value": "100" },
      "destinationTag": 402011,
      "unitPrice": "0.002",
      "authScheme": "x402-credit .."
    }
  ]
}

```

크레딧 충전은 온체인 Payment 1건으로 이뤄지고, 이후 호출은 서명으로만 인증된다. 서명 대상에 nonce를 포함시켜 재전송을 막고, nonce는 단조 증가를 강제한다.

  src/services/x402.credit.tstypescript
```

import { verify } from 'ripple-keypairs';
import { verifyPayment } from './x402';

interface CreditAccount { balance: number; currency: 'RLUSD' | 'WLBN'; lastNonce: number; publicKey: string; }
const credits = new Map();   // use a persistent store in production

/** Top-up - reuses the existing on-chain verification logic as-is */
export async function topUp(txHash: string) {
  const res = await verifyPayment(txHash);          // replay + delivered-amount checks already applied here
  if (!res.ok) return res;

  const acc = credits.get(res.payer) ?? { balance: 0, currency: res.currency, lastNonce: 0, publicKey: res.publicKey };
  acc.balance += Number(res.value);
  credits.set(res.payer, acc);
  return { ok: true as const, balance: acc.balance };
}

/** Call - signature + balance only, no on-chain round trip (~1ms) */
export function spend(header: string, method: string, path: string, unitPrice: number) {
  const [payer, nonceStr, signature] = header.split('.');
  const acc = credits.get(payer);
  if (!acc) return { ok: false as const, reason: 'NO_CREDIT_ACCOUNT' };

  const nonce = Number(nonceStr);
  if (!Number.isInteger(nonce) || nonce

    두 scheme의 역할 분담

xrpl-payment는 저빈도·고단가 상품(코호트 질의, 인사이트 리포트)에, xrpl-credit은 고빈도·저단가 상품(집계 API)에 쓴다. 크레딧 잔액이 소진되면 서버가 다시 402를 반환하므로, 구매자 에이전트 입장에서는 충전과 사용이 동일한 프로토콜 흐름 안에서 처리된다.

### 리워드 소비와 결제 구조

백서 5장의 "생태계 소비"(가전 구매 할인·필터 구독 결제·제휴 마켓)는 제휴 마켓 개설 시 별도 사양으로 공표한다. 결제 구조는 공표 정책을 따른다 - 기업 고객은 원화·RLUSD, AI 에이전트는 자동 결제(x402), 프로토콜 내부는 매출 일부로 WLBN을 매입해 소각한다. 고객에게 토큰을 사서 결제하라고 요구하지 않으며, 배분 비율은 사전 공표한다.

### 가치 흐름 요약

  fund flow over one epochlog
```

[IN ]  Enterprise    → Hot           RLUSD / KRW  (aggregate credit · cohort · report · dataset)
[IN ]  AI agent      → Hot           RLUSD / WLBN (per-call x402 payment)
[OUT]  Hot           → points        WLBN   7 × 0.78^(phase−1) × grade   ← quantity, never value
[BURN] Hot           → Issuer        WLBN   quality shortfall (redistribution excess)
[BURN] Treasury      → Issuer        WLBN   scheduled 2.5B in 4 tranches · buyback from revenue (2.465B)
[LP  ] Treasury      → AMM           WLBN/RLUSD   20% of participation fees on sale

```

## STEP 01 XRPL 기반 & 지갑 셋업

Mainnet(wss://xrplcluster.com) 클라이언트 모듈과 3개 지갑(Issuer 콜드 · Hot · Treasury)을 준비하고, 각 계정에 Reserve용 XRP를 사전 예치한다.

#### 산출물

    - xrpl/client.ts - 재사용 가능한 연결 싱글턴

    - xrpl/wallet.ts - 시드 기반 로드 + 신규 생성/펀딩

    - scripts/01-bootstrap-wallets.ts - 실행 시 .env에 붙여넣을 값 출력

  src/xrpl/client.tstypescript
```

import { Client } from 'xrpl';
import { env } from '../config/env';

let client: Client | null = null;

export async function getClient(): Promise {
  if (client?.isConnected()) return client;
  client = new Client(env.XRPL_ENDPOINT, { connectionTimeout: 10_000 });
  client.on('disconnected', (code) => console.warn('[xrpl] disconnected', code));
  await client.connect();
  return client;
}

export async function closeClient(): Promise {
  if (client?.isConnected()) await client.disconnect();
  client = null;
}

```

  src/scripts/01-bootstrap-wallets.tstypescript
```

import { Wallet } from 'xrpl';
import { getClient, closeClient } from '../xrpl/client';
import { env } from '../config/env';

const ROLES = ['ISSUER', 'HOT', 'TREASURY'] as const;
const MIN_RESERVE_XRP = 5;
// ISSUER(cold) rDJz8WJhsKgydqJzSZXMpsJot3eRmSkR5
// HOT          rhTTtx8YyKzcmPDsoroYXuHdGMThrnweb1
// TREASURY     rPZFY2yLgqjmBrxtxWHXnB1doBt7gu7xin

async function main() {
  const client = await getClient();

  for (const role of ROLES) {
    // Mainnet: 시드로 로드하고 잔액을 확인한다 (Faucet 없음)
    const wallet = Wallet.fromSeed(env[`${role}_SEED`]);
    const info = await client.request({
      command: 'account_info', account: wallet.classicAddress, ledger_index: 'validated',
    });
    const balance = Number(info.result.account_data.Balance) / 1_000_000;
    if (balance  { console.error(e); process.exit(1); });

```

    Reserve 계산

각 계정은 Base Reserve 1 XRP를 묶어두고, TrustLine·NFT Offer·AMM 오브젝트마다 0.2 XRP씩 추가로 잠긴다. Hot 지갑은 WLBN + RLUSD TrustLine 2개 = 0.4 XRP가 추가로 필요하므로, 각 계정에 최소 5 XRP 이상을 사전 예치하고 대량 기기 등록 시에는 사전 잔액 점검 로직을 넣는다.

## STEP 02 Wellbian 토큰(WLBN) 발행 (Issued Asset)

Issuer(콜드)가 Wellbian 토큰(WLBN)을 발행하고, 보유 지갑들이 WLBN 및 실제 RLUSD TrustLine을 설정한다.

### 발행 절차

    - Issuer 계정 설정 - AccountSet으로 asfDefaultRipple 활성화, TransferRate(선택) 설정

    - 보유자 TrustLine - Hot·Treasury·사용자 지갑이 TrustSet으로 한도 설정

    - 초기 발행 - Issuer → Treasury/Hot으로 Payment를 보내면 그 금액만큼 토큰이 존재하게 됨 (IOU는 별도 mint 트랜잭션이 없음)

    Default Ripple은 발행자에서 켠다

XRPL에서 IOU는 항상 발행자 계정을 경유해 이동한다. 따라서 발행자 계정은 asfDefaultRipple을 ON으로 설정해야 한다. 이 플래그가 꺼져 있으면 보유자 간 전송(Hot → 사용자 지갑)이 tecPATH_DRY로 실패하고, Step 4의 보상 지급과 Step 5의 AMM이 모두 성립하지 않는다.

의도치 않은 유동성 전이 방지는 보유자 측 TrustLine에 tfSetNoRipple을 적용해 달성한다. 이 조합은 "발행자를 경유한 정상 전송"은 허용하고 "보유자 계정을 브릿지로 삼는 제3자 경로"는 차단한다.

  src/xrpl/token.tstypescript
```

import { Client, Wallet, AccountSetAsfFlags, TrustSetFlags, IssuedCurrencyAmount } from 'xrpl';
import { submit } from './submit';

/** Issuer account setup - DefaultRipple ON so IOUs can move between holders */
export async function configureIssuer(client: Client, issuer: Wallet) {
  await submit(client, issuer, {
    TransactionType: 'AccountSet',
    Account: issuer.classicAddress,
    SetFlag: AccountSetAsfFlags.asfDefaultRipple,
    Domain: Buffer.from('wellbian.io').toString('hex').toUpperCase(),
  });
}

/** Holder TrustLine - NoRipple blocks third-party bridge routing */
export async function setTrustLine(
  client: Client, holder: Wallet, issuerAddress: string,
  currency: string, limit = '1000000000',
) {
  await submit(client, holder, {
    TransactionType: 'TrustSet',
    Account: holder.classicAddress,
    LimitAmount: { currency, issuer: issuerAddress, value: limit },
    Flags: TrustSetFlags.tfSetNoRipple,
  });
}

/** IOU transfer - from the Issuer it is issuance; between holders it is circulation */
export async function sendIOU(
  client: Client, from: Wallet, destination: string,
  amount: IssuedCurrencyAmount, destinationTag?: number,
) {
  return submit(client, from, {
    TransactionType: 'Payment',
    Account: from.classicAddress,
    Destination: destination,
    Amount: amount,
    ...(destinationTag !== undefined ? { DestinationTag: destinationTag } : {}),
  });
}

```

### 토큰 정의

    | 토큰 | Currency (hex) | 역할 | 초기 공급 |

      | WLBN | 574C424E0000… | DePIN 보상 · API 사용료 결제 수단 | 10,000,000,000 (총 발행) |

      | RLUSD | 524C5553440000… | 스테이블 페어 · 기관 결제 유입 통화 (Issuer rMxCKbEDwqr76QuheSUMdEGf4B9xJ8m5De) | 외부 발행 (Ripple) - 자체 발행 없음 |

공급 총 발행량은 100억 WLBN이다. XRPL IOU에는 총량 필드가 없으므로, 공급량은 Issuer가 내보낸 채무(gateway_balances의 obligations)의 합으로 정의된다. 2026-09-01 기준 배분은 트레저리 99억 9,995만, 핫월렛(보상 재원) 약 5만, 유통 약 5이다.

### 방출 스케줄 - 단계 체감

총 발행은 100억이지만 한 번에 유통시키지 않는다. 방출 기준치는 실내 1단계 지점당 일 7 WLBN · 월 213 · 연 2,555(정상 등급)이며, 단계가 오를 때마다 22% 체감한다(7 × 0.78^(단계−1)). 이는 계획값이며 지급을 약속하는 수치가 아니다. 실제 수령량은 품질 등급과 재분배 결과에 따라 달라진다.

    | 단계 | 일 기본 | 1단계 대비 | 단계 방출량 |

      | 1 국내 실내 (25,000 지점) | 7.00개 | 100% | 0.64억 |

      | 3 | 4.26개 | 61% | 1.87억 |

      | 5 | 2.59개 | 37% | 3.31억 |

      | 7 | 1.58개 | 23% | 3.74억 |

      | 10 | 0.75개 | 11% | 2.73억 |

      | 10단계 누적 |  |  | 26.65억 (노드 리워드 1·2기 46억 이내) |

목표 규모 1단계는 국내 실내 25,000 지점(제네시스 한정 모집)이다. 이후 실내 → 실외로 넓히며 기존 홀더에게 실외 라인을 우선 제안하고, 최종 목표는 실내를 포함한 통합 관측망 100만 지점이다. 단계 진입은 지점 수와 수요 조건을 함께 만족하는 마일스톤 게이트로만 열린다.

배분 총 발행 100억 중 노드 홀더 리워드는 1기 10억(10%)과 1기 소진 후 단계 게이트를 통과하며 열리는 2기 36억(36%)이다. 소각 예정 15억, 전략 라운드 예비 10억, 스테이킹 재원 8억, 팀 6억, 생태계 5억, 전략 투자 5억, 유동성·준비금 5억 - 전체 표는 토크노믹스 정책 절에 있다.

체감 시간 기반 반감기는 쓰지 않는다. 해마다 반으로 줄면 계획 방출 기준이 흔들린다. 대신 지점 수 게이트로 정의된 단계마다 22% 체감하며, 브레이크는 시간이 아니라 게이트(수요 조건)와 10단계 누적 상한이다.

상한 지점당 1차 지급은 등급별 정량(정상 7 · 우수 8.4 · 미달 0)이고, 미지급분 재분배 후에도 지점당 10.5개를 넘지 않는다. 초과분은 지급하지 않고 소각하므로 어떤 경우에도 총 지급 + 소각 = 에폭 예산이다.

참고 실제 RLUSD는 Ripple이 발행하는 스테이블코인이다. 서비스 대금(x402)은 이 RLUSD로만 수취하며, Issuer 주소는 rMxCKbEDwqr76QuheSUMdEGf4B9xJ8m5De로 상수에 고정한다. 자체 발행 모의 RLUSD는 사용하지 않는다.

## STEP 03 IoT 라이선스 NFT (XLS-20)

기기 1대 = NFT 1개. 온체인에서 라이선스 보유 여부를 조회해 데이터 제출 권한을 판정한다.

리딤 4단계 - ① 사전 구매·프로모션 시 코드가 미결합(UNBOUND) 상태로 발급된다 (WLBN-XXXX-XXXX-XXXX). ② 사용자가 wellbian.io/redeem에서 코드와 기기 시리얼을 입력하면 코드가 해당 기기에 결합된다. ③ Hot 지갑이 NFT를 민팅하고(Issuer는 콜드 지갑) NFTokenCreateOffer(Amount=0)를 사용자 주소로 생성한다. ④ 사용자가 NFTokenAcceptOffer로 수령을 확정한다. 지원 지갑은 간편지갑(아이디·비밀번호 또는 구글 로그인), D'CENT 모바일 인앱, Xaman, GemWallet, Crossmark이다.

### 발행 → 전송 흐름

NFTokenMint Hot 서명 · Issuer=콜드(NFTokenMinter) NFTokenCreateOffer Hot · Amount=0 · Destination=사용자 NFTokenAcceptOffer 사용자 지갑 · 수령 확정 account_nfts verifyDeviceLicense() NFTokenID OfferIndex 소유권 이전 Amount=0 판매 오퍼 + Destination 지정 → 리딤으로 확인된 사용자 계정만 수령 가능 (무상 발급) Fig 2. XLS-20 라이선스 발급 시퀀스 src/xrpl/nft.tstypescript ``` import { Client, Wallet, NFTokenMintFlags, NFTokenCreateOfferFlags, convertStringToHex } from 'xrpl'; import { submit } from './submit'; export const TAXON = { OBS_GENESIS: 1001, OBS_REGION: 1002, OBS_BIZ: 1003, // 관측소 NFT (계정당 1개) VOUCHER: 1010, // 사전예약 바우처 (테스트 900010) OAQ: 2026, IAQ: 3026, AI: 4026, RADAR: 5026, SAT: 6026, // 스테이션 NFT (기기당 1개) } as const; /** Mint a license NFT → returns the NFTokenID */ export async function mintLicense( client: Client, hot: Wallet, metadataUri: string, taxon: number = TAXON.IAQ, ) { // 서명자는 Hot 지갑, 발행 주체(Issuer)는 콜드 지갑 - NFTokenMinter로 위임 const res = await submit(client, hot, { TransactionType: 'NFTokenMint', Account: hot.classicAddress, Issuer: env.ISSUER_ADDRESS, URI: convertStringToHex(metadataUri), // ipfs://Qm... NFTokenTaxon: taxon, Flags: NFTokenMintFlags.tfTransferable, }); // meta.nftoken_id is populated by rippled const meta = res.result.meta as { nftoken_id?: string }; if (!meta.nftoken_id) throw new Error('NFTokenID not found in metadata'); return meta.nftoken_id; } /** Create a zero-price sell offer restricted to one recipient */ export async function offerLicenseTo( client: Client, hot: Wallet, nftokenId: string, destination: string, ) { const res = await submit(client, hot, { TransactionType: 'NFTokenCreateOffer', Account: hot.classicAddress, NFTokenID: nftokenId, Amount: '0', Destination: destination, // 리딤으로 확인된 사용자 계정만 수락 가능 Flags: NFTokenCreateOfferFlags.tfSellNFToken, }); const meta = res.result.meta as { offer_id?: string }; if (!meta.offer_id) throw new Error('OfferID not found in metadata'); return meta.offer_id; } /** User wallet accepts the offer → ownership transfers. * D'CENT 펜딩 오퍼 폴백은 설정 플래그 allowPendingOffer(기본 false)로 제어한다. */ export async function acceptLicense(client: Client, user: Wallet, offerId: string) { return submit(client, user, { TransactionType: 'NFTokenAcceptOffer', Account: user.classicAddress, NFTokenSellOffer: offerId, }); } ``` ### 온체인 라이선스 검증 오라클 엔드포인트가 매 요청마다 호출하는 핵심 함수다. Issuer 주소와 Taxon을 함께 확인해야 임의의 제3자가 발행한 위조 NFT를 거를 수 있다.

  src/xrpl/nft.ts (continued)typescript
```

import { env } from '../config/env';

export interface LicenseInfo {
  valid: boolean;
  nftokenId?: string;
  uri?: string;
  taxon?: number;
}

/** Verify on-chain that the address holds a valid license NFT for this network */
export async function verifyDeviceLicense(
  client: Client, walletAddress: string, taxon: number = TAXON.IAQ,
): Promise- { const res = await client.request({ command: 'account_nfts', account: walletAddress, ledger_index: 'validated', }); const hit = res.result.account_nfts.find( (n) => n.Issuer === env.ISSUER_ADDRESS && n.NFTokenTaxon === taxon, ); if (!hit) return { valid: false }; return { valid: true, nftokenId: hit.NFTokenID, taxon: hit.NFTokenTaxon, uri: hit.URI ? Buffer.from(hit.URI, 'hex').toString('utf8') : undefined, }; } ``` #### 메타데이터 스키마 (IPFS) - 실외 OAQ 예시 ipfs://Qm…/oaq-device.jsonjson ``` { "schema": "wellbian-oaq-device/v1", "name": "Wellbian OAQ Node #0042", "description": "Outdoor Air Quality station license", "device": { "serial": "KW-OAQ-0042", "model": "TBD", "sensors": ["pm25", "pm10", "o3", "no2", "temp", "rh"], "installed_at": "2026-03-11" }, "context": { "geohash5": "wydm9", "space_type": "outdoor" }, "license": { "tier": "oaq-standard", "valid_until": "2029-03-11" } } ``` 실내 IAQ 노드의 메타데이터는 주거 환경 버킷을 추가로 포함한다 - 라이선스 계층 · 품질 점수 절의 wellbian-iaq-device/v1 스키마 참고. 두 스키마 모두 정밀 좌표를 넣지 않는다는 규칙을 공유한다. 캐싱과 폐기(Revocation) account_nfts를 요청마다 호출하면 텔레메트리 처리량이 원장 조회에 묶인다. 결과를 주소 단위로 30~60초 TTL 캐싱하고, 라이선스 폐기가 필요하면 NFT를 회수(재오퍼)하거나, tfBurnable로 발행한 뒤 발행 권한을 가진 콜드 Issuer가 NFTokenBurn으로 소각하는 정책을 함께 정의해야 한다. tfTransferable만 설정하면 발행자가 강제 회수할 수 없다는 점에 유의한다. ## STEP 04 DePIN 오라클 & 보상 엔진 텔레메트리 수신 → 라이선스 검증 → WLBN 보상 지급까지의 무인 파이프라인. ### API 명세 항목내용 EndpointGET https://api.wellbianlabs.io/v1/c? (Supabase Edge Function proxy) Body{ schema, deviceAddress, token, batch: [ { ts, pm25, co2, tvoc, tempC, rh, … } ] } 202{ accepted: 5 } - 수신 확인. 보상은 일 1회 에폭 정산에서 적립되며, 24시간 홀드 후 클레임 시 지급 401{ error: "BAD_TOKEN" } - 수집 토큰 불일치 (기기별 토큰으로 전환 중, 페이로드 서명 검증은 로드맵) 403{ error: "NO_VALID_LICENSE" } - 라이선스 NFT 미보유 422{ error: "OUT_OF_RANGE" } - 기기 시각이 10분 초과로 어긋나면 거부하지 않고 서버 수신 시각(KST)으로 보정 후 수용 429{ error: "RATE_LIMITED" } - 제출 주기 위반 src/services/oracle.tstypescript ``` import { getClient } from '../xrpl/client'; import { verifyDeviceLicense } from '../xrpl/nft'; import { env } from '../config/env'; import { absoluteHumidity } from './iaq/humidity'; import { TAXON } from '../xrpl/nft'; export interface IaqReading { ts: number; pm25: number; pm10: number; co2: number; tvoc: number; tempC: number; rh: number; } const RANGES: Record = { ts: [0, Number.MAX_SAFE_INTEGER], pm25: [0, 1000], pm10: [0, 1000], co2: [350, 5000], tvoc: [0, 60000], tempC: [-10, 60], rh: [0, 100], }; const lastSeen = new Map(); const MIN_INTERVAL_MS = 4 * 60_000; // 1-minute transmit cadence; floor keeps slack export async function ingest(deviceAddress: string, batch: IaqReading[]) { // 1) submission interval floor - prevents reward farming const prev = lastSeen.get(deviceAddress) ?? 0; if (Date.now() - prev hi) throw new HttpError(422, 'OUT_OF_RANGE'); } } // 3) on-chain license check (IAQ Taxon, 60s TTL cache) const client = await getClient(); const license = await verifyDeviceLicense(client, deviceAddress, TAXON.IAQ); if (!license.valid) throw new HttpError(403, 'NO_VALID_LICENSE'); // 4) derive absolute humidity, store off-chain - only the batch Merkle root goes on-ledger const enriched = batch.map((r) => ({ ...r, ah: absoluteHumidity(r.tempC, r.rh) })); await saveTelemetry(deviceAddress, enriched, license.nftokenId!); // 5) accrual only - the daily epoch settles accruals; payout happens on claim await accrueForEpoch(deviceAddress, license, enriched); lastSeen.set(deviceAddress, Date.now()); return { accepted: batch.length }; // HTTP 202 } ``` ### 보상 엔진 파라미터 항목정책 값현행 구현 (2026-09) 에폭1일 · KST 자정 경계동일 일일 리워드7 × 0.78^(단계−1) × 품질 등급(0 · 1.0 · 1.2)1단계 정상 7 정액 적립 에폭 예산활성 지점 × 7.14 × 단계 계수지점당 7 상한으로 동일 효과 재분배미지급분 가중치 비례 · 지점당 상한 10.5 · 초과분 소각거버넌스 변수로 반영 예정 품질 등급전송률 95%+ 전액 · 80~95% 선형 · 80% 미만 0 · 품질 점수 상위 10% 우수품질 점수(uptime 40 · outlier 25 · correlation 20 · calibration 15) 산출 중 · uptime  ({ currency: WLBN, issuer: env.ISSUER_ADDRESS, value }); const rlusd = (value: string) => ({ currency: RLUSD, issuer: env.RLUSD_ISSUER_ADDRESS, value }); /** AMM pool deposit 100,000 WLBN : 1,000 RLUSD - 예시 값. 예치 비율은 스왑 교환비 산정용일 뿐 * WLBN의 가치·법정화폐 환산을 의미하지 않는다. */ export async function createAmm(client: Client, lp: Wallet) { return submit(client, lp, { TransactionType: 'AMMCreate', Account: lp.classicAddress, Amount: wlbn('100000'), Amount2: rlusd('1000'), TradingFee: 500, // 0.5% (unit: 1/100,000) }); } /** Two-asset deposit */ export async function depositBoth(client: Client, lp: Wallet, a: string, b: string) { return submit(client, lp, { TransactionType: 'AMMDeposit', Account: lp.classicAddress, Asset: { currency: WLBN, issuer: env.ISSUER_ADDRESS }, Asset2: { currency: RLUSD, issuer: env.RLUSD_ISSUER_ADDRESS }, Amount: wlbn(a), Amount2: rlusd(b), Flags: AMMDepositFlags.tfTwoAsset, }); } /** RLUSD → WLBN swap: fixed target amount + max spend cap (slippage defense) */ export async function swapRlusdForWlbn( client: Client, buyer: Wallet, targetWlbn: string, maxRlusd: string, ) { const paths = await client.request({ command: 'path_find', subcommand: 'create', source_account: buyer.classicAddress, destination_account: buyer.classicAddress, destination_amount: wlbn(targetWlbn), source_currencies: [{ currency: RLUSD, issuer: env.RLUSD_ISSUER_ADDRESS }], } as never); return submit(client, buyer, { TransactionType: 'Payment', Account: buyer.classicAddress, Destination: buyer.classicAddress, // to yourself = currency conversion Amount: wlbn(targetWlbn), SendMax: rlusd(maxRlusd), // slippage cap Paths: (paths.result as { alternatives: { paths_computed: unknown }[] }) .alternatives[0]?.paths_computed as never, Flags: 0, // tfPartialPayment 미사용 }); } /** Query pool state */ export async function getPoolState(client: Client) { const res = await client.request({ command: 'amm_info', asset: { currency: WLBN, issuer: env.ISSUER_ADDRESS }, asset2: { currency: RLUSD, issuer: env.RLUSD_ISSUER_ADDRESS }, } as never); return res.result; } ``` 스왑 시 주의점 자기 자신에게 보내는 Payment가 XRPL의 통화 변환 관용구다. Destination을 본인 주소로 둔다.

      - SendMax를 반드시 지정한다. 없으면 풀 상태에 따라 예상보다 훨씬 많은 RLUSD가 소진될 수 있다.

      - tfPartialPayment를 켜면 목표량 미달로도 성공 처리되므로, 결제 검증 로직에서 실제 전달액(delivered_amount)을 확인해야 한다. Step 6의 검증에서 이 필드를 쓰는 이유다.

      - 양쪽 자산 모두 매수자에게 TrustLine이 설정돼 있어야 한다.

## STEP 06 x402 자율 결제 게이트웨이

메인넷 운영 중인 자율 결제 게이트웨이. 외부 AI 에이전트가 사람 개입 없이 HTTP 402 응답을 읽고, 온체인 결제 후 재요청해 데이터를 얻는다.

AI Agent - x402 Gateway XRPL ① GET /api/data/aggregates (결제 헤더 없음) ② 402 Payment Required + 결제 조건 JSON ③ RLUSD·WLBN 호출당 결제 수취 → Hot Wallet (DestinationTag) ④ tesSUCCESS · tx_hash (3~5s finality) ⑤ 재요청 X-Payment-Tx: ⑥ nonce 중복 확인 후 tx 조회 ⑦ Destination · Amount · tesSUCCESS ⑧ 200 OK + LLM 추론 결과 실패 시 → 409 (원장 미반영, Retry-After) / 402 (조건 불일치 · 해시 재사용) Fig 3. x402 챌린지–결제–검증 시퀀스 ### 1차 요청 - 402 챌린지 HTTP/1.1 402 Payment Requiredhttp ``` HTTP/1.1 402 Payment Required Content-Type: application/json WWW-Authenticate: x402 realm="wellbian-data", network="xrpl-mainnet" { "x402Version": 1, "resource": "/api/data/aggregates", "accepts": [ { "scheme": "xrpl-payment", "network": "xrpl-mainnet", "payTo": "rhTTtx8YyKzcmPDsoroYXuHdGMThrnweb1", "asset": { "currency": "WLBN", "issuer": "rDJz8WJhsKgydqJzSZXMpsJot3eRmSkR5", "value": "" }, // 수량 기준 단가 · RLUSD/법정화폐 환산 아님 "destinationTag": 402001, "maxTimeoutSeconds": 120 }, { "scheme": "xrpl-payment", "network": "xrpl-mainnet", "payTo": "rhTTtx8YyKzcmPDsoroYXuHdGMThrnweb1", "asset": { "currency": "RLUSD", "issuer": "rMxCKbEDwqr76QuheSUMdEGf4B9xJ8m5De", "value": "0.002" }, "destinationTag": 402001, "maxTimeoutSeconds": 120 } ], "description": "Wellbian IAQ 집계 데이터 - 호출당 과금" } ``` ### 2차 요청 - 트랜잭션 검증 src/services/x402.tstypescript ``` import { getClient } from '../xrpl/client'; import { WLBN, RLUSD } from '../config/currency'; import { env } from '../config/env'; const usedHashes = new Set(); // replace with Redis SETNX + TTL in production export type VerifyResult = | { ok: true; currency: 'WLBN' | 'RLUSD'; value: string } | { ok: false; reason: string }; const PRICES = { [WLBN]: { label: 'WLBN' as const, min: Number(env.X402_PRICE_WLBN) }, [RLUSD]: { label: 'RLUSD' as const, min: Number(env.X402_PRICE_RLUSD) }, }; export async function verifyPayment(txHash: string): Promise { // 0) block reuse - stops unlimited calls with the same hash if (usedHashes.has(txHash)) return { ok: false, reason: 'HASH_ALREADY_USED' }; const client = await getClient(); let res; try { res = await client.request({ command: 'tx', transaction: txHash, binary: false }); } catch { return { ok: false, reason: 'TX_NOT_FOUND' }; // may not have propagated yet → prompt a retry } const tx = res.result.tx_json ?? (res.result as never as Record); const meta = res.result.meta as { TransactionResult: string; delivered_amount?: unknown }; // 1) ledger finality + success if (res.result.validated !== true) return { ok: false, reason: 'NOT_VALIDATED' }; if (meta.TransactionResult !== 'tesSUCCESS') return { ok: false, reason: meta.TransactionResult }; if ((tx as { TransactionType: string }).TransactionType !== 'Payment') return { ok: false, reason: 'NOT_A_PAYMENT' }; // 2) recipient matches if ((tx as { Destination: string }).Destination !== env.HOT_WALLET_ADDRESS) return { ok: false, reason: 'WRONG_DESTINATION' }; // 3) check actual delivered amount - trust delivered_amount to block PartialPayment bypass const delivered = meta.delivered_amount as { currency: string; issuer: string; value: string } | string | undefined; if (!delivered || typeof delivered === 'string') return { ok: false, reason: 'XRP_NOT_ACCEPTED' }; const price = PRICES[delivered.currency]; if (!price) return { ok: false, reason: 'UNSUPPORTED_CURRENCY' }; const EXPECTED_ISSUER = { [WLBN]: env.ISSUER_ADDRESS, [RLUSD]: env.RLUSD_ISSUER_ADDRESS }; if (delivered.issuer !== EXPECTED_ISSUER[delivered.currency]) return { ok: false, reason: 'WRONG_ISSUER' }; if (Number(delivered.value) ### 게이트웨이 미들웨어 src/api/middleware.x402.tstypescript ``` import { RequestHandler } from 'express'; import { verifyPayment } from '../services/x402'; import { buildChallenge } from '../services/x402.challenge'; export const x402Gate: RequestHandler = async (req, res, next) => { const payTx = req.header('x-payment-tx'); // 선불 크레딧은 x-api-key 경로로 별도 처리 const match = payTx?.match(/^([A-F0-9]{64})$/i); if (!match) { res.setHeader('WWW-Authenticate', 'x402 realm="wellbian-data", network="xrpl-mainnet"'); return res.status(402).json(buildChallenge(req.path)); } const result = await verifyPayment(match[1]); if (!result.ok) { // not yet in the ledger → prompt retry; otherwise reissue 402 const retryable = result.reason === 'TX_NOT_FOUND' || result.reason === 'NOT_VALIDATED'; if (retryable) { res.setHeader('Retry-After', '3'); return res.status(409).json(result); } return res.status(402).json({ ...buildChallenge(req.path), error: result.reason }); } res.setHeader('X-Payment-Response', JSON.stringify({ settled: true, ...result })); next(); }; ``` ### AI 에이전트 클라이언트 예제 src/scripts/agent-example.tstypescript ``` import { getClient } from '../xrpl/client'; import { sendIOU } from '../xrpl/token'; import { agentWallet, env } from '../config/env'; import { toCurrencyHex } from '../config/currency'; const API = 'https://wellbian.io/api/data/aggregates'; const query = new URLSearchParams({ taxon: 'IAQ', geohash: 'wydm6', from: '2026-08-31T00:00:00+09:00', to: '2026-09-01T00:00:00+09:00' }); async function call(headers: Record = {}) { return fetch(`${API}?${query}`, { method: 'GET', headers }); } async function main() { // (1) call without payment → receive 402 + payment terms const challenge = await call(); if (challenge.status !== 402) throw new Error(`expected 402, got ${challenge.status}`); const terms = await challenge.json(); const option = terms.accepts.find((a: { asset: { currency: string } }) => a.asset.currency === 'WLBN'); console.log('[agent] terms received:', option.asset.value, option.asset.currency); // (2) pay on-chain - the agent decides and pays on its own const client = await getClient(); const tx = await sendIOU( client, agentWallet, option.payTo, { currency: toCurrencyHex(option.asset.currency), issuer: option.asset.issuer, value: option.asset.value }, option.destinationTag, ); const hash = tx.result.hash; console.log('[agent] payment settled:', hash); // (3) retry with tx_hash attached (retries absorb propagation delay) for (let i = 0; i setTimeout(r, 3000)); } throw new Error('verification timed out'); } main().catch((e) => { console.error(e); process.exit(1); }); ``` ### 소각 정책 - 예정 15% · 매출 연동 매입 소각 · 매월 공개 예정 소각은 총 발행량의 15%(15억)이며 분할 방식은 별도로 공지한다. 매입 소각은 매출에 연동한다 - 기기 판매액의 10%, AI 팩토리 이용권 판매액의 10%, 데이터 매출의 50%로 시장에서 WLBN을 매입해 소각한다. 여기에 품질 미달 재분배 초과분과 게이트 미통과로 열리지 않은 2기 리워드가 더해진다. 소각은 IOU를 Issuer로 되돌리는 방식이며, 매월 집행하고 매입 수량·가격·트랜잭션을 공개한다. src/services/burn.tstypescript ``` import { getClient } from '../xrpl/client'; import { sendIOU } from '../xrpl/token'; import { WLBN } from '../config/currency'; import { env, hotWallet, treasuryWallet } from '../config/env'; export type BurnReason = 'scheduled' | 'buyback' | 'quality-shortfall' | 'round2-unopened'; /** Return WLBN to the Issuer = burn. Quantity and tx hash are published; buyback timing/price are not. */ export async function burn(amountWlbn: string, reason: BurnReason) { if (Number(amountWlbn) XRPL의 "소각"은 스마트컨트랙트 burn과 다르다 IOU는 발행자의 부채 기록이다. 보유자가 발행자에게 되돌려 보내면 TrustLine 잔액이 상계되어 유통량이 실제로 감소한다. 별도의 burn 함수는 필요 없다. 다만 Issuer 계정이 여전히 DefaultRipple과 발행 권한을 갖고 있으므로 "재발행 불가"를 담보하려면 별도의 blackhole 계정(regular key를 rrrrrrrrrrrrrrrrrrrrrhoLvTp로 설정하고 마스터키 비활성화)을 쓰는 것이 더 강한 보증이다. 현재는 Issuer 반환에 의한 상계 방식을 사용하며, blackhole 계정 전환 여부는 별도로 고지한다. ## 운영 토크노믹스 정책 정량 지급 · 총량 100억 · 소각 예정 15% + 매출 연동 매입 소각. 본 절은 tokenomics.wellbianlabs.io에 공표된 정책과 동일하며, 수량만 정의하고 가치는 정의하지 않는다. ### 온체인 지급 - 시험 지급과 DEX 풀 개설 원칙 적립된 WLBN은 매일 정산되어 사이트 계정에 쌓이고, 클레임하면 온체인으로 지급된다. 2026년 8~9월에는 초기 노드를 대상으로 시험 지급(클레임 8건, 78.23 WLBN)을 온체인으로 실행했으며, 그 기록은 XRPL에 그대로 남아 있다. 이유 시장 가격은 XRPL DEX(AMM) 풀이 개설된 뒤에 형성된다. 풀 개설 전 지급은 시험 목적의 소량으로 제한해, 유통량이 극히 적은 상태에서 장외 가격이 먼저 만들어지는 것을 막았다. 풀은 2026년 10월 2일에 개설한다. 보장 적립은 중단되지 않으며 쌓인 수량은 사라지지 않는다. 전면 출금은 DEX 풀 개설과 함께 2026년 10월 2일에 개시했고, 적립 후 24시간 보류 기간이 지난 수량은 온체인으로 인출할 수 있다. 우선순위 이 정책은 재단이 정한 최상위 정책으로, 지급·인출에 관한 다른 절의 서술에 우선한다. ### 단 하나의 원칙 - 정량 지급 약속하는 것 하루 7개 개수는 사전 공표한 그대로 지급한다. 시세가 오르내려도 지급 개수는 변하지 않으며, 총 방출량은 10단계 누적 26.65억개로 확정된다. 약속하지 않는 것 그 7개가 얼마인지 토큰의 가치를 보장하지도 증명하지도 않는다. 가격은 거래소에서 거래하는 참여자들이 정하며, 이 문서 어디에도 목표가·수익률·회수 기간은 없다. 항목금액 기준이었다면정량 지급 (WLBN) 지급 단위「하루 얼마어치」 - 개수가 매일 변동「하루 7개」 - 개수 고정 시세가 내리면지급 개수가 늘어남 - 방출 폭증·악순환개수 그대로 · 방출 불변 시세가 오르면지급 개수가 줄어 참여자 이득 없음개수 그대로 · 참여자 이득 총 방출량예측 불가10단계 누적 26.65억개 확정 대가의 성격가치 보장 암시데이터 제공에 대한 역무의 대가 WLBN 유통 공급량 관측 지점 확산 라이선스 NFT 1장 = 지점 1개 데이터 품질·밀도 ↑ 관측망 커버리지 확대 데이터 매출 ↑ 기업 고객 · AI 에이전트 결제 예정 15억 + 매출 연동 매입 유통량 감소 · 수량으로만 공표 관측 데이터 제출 일 7개 × 0.78^(단계−1) × 품질 등급 - 수량으로만 정의 데이터 상품성 향상 매출 일부로 WLBN 매입 소각 → 유통량 ↓ 보조금 → 고객 매출로 재원 전환 (자립) Fig 4. 공급–수요–소각 순환 · 방출은 단계 체감, 소각은 매입·예정 분할 ### 총량 100억 배분 버킷수량비중해제 조건 · 베스팅 노드 홀더 리워드 1기 (Phase 1~5)10억10%에폭마다 정량 방출 · Phase 1~5 누적 약 9.68억 노드 홀더 리워드 2기 (Phase 6~)36억36%1기 소진 후 단계 게이트 통과 시 개방 · 미개방분은 소각 전환 소각 예정15억15%분할 방식은 별도 공지 전략 라운드 예비10억10%향후 전략 라운드 배정분 · 배정 전까지 잠금 · 배정 시 조건 공개 스테이킹 재원8억8%운영 방식은 추후 공지 팀 · 기여자6억6%12개월 클리프 + 48개월 선형 · 일정 전체 공개 생태계 · 지역 파트너5억5%권역 진입 마일스톤마다 조건부 해제 전략 투자 · 프라이빗5억5%투자 계약에 따른 베스팅 · 시드 라운드는 즉시 10% + 7~24개월 월 5% · 일정 공개 유동성 · 준비금5억5%초기 풀 예치와 추가 예치 합계100억100%제네시스에 100억 전량 발행 완료 · 추가 발행 계획 없음 · Issuer 계정은 blackhole이 아니다 ### 일일 리워드 - 단계마다 22% 체감 reward rulelog ``` daily reward = base 7 × 0.78^(phase − 1) × quality grade (0 ~ 1.2) epoch budget = active points × 7.14 WLBN × phase factor ``` 가격과 무관하게 수량만으로 정의된다. 단계는 연차가 아니라 지점 수와 수요 조건을 함께 만족하는 게이트로 넘어가며, 단계가 오를 때마다 지점당 수량이 22% 줄어든다. 단계일 기본월연1단계 대비단계 방출량참여 시점별 누적 수령 1 국내 실내7.00개213개2,555개100%0.64억10,646개 (제네시스) 34.26개130개1,554개61%1.87억6,098개 (57%) 52.59개79개946개37%3.31억3,331개 (31%) 71.58개48개575개23%3.74억1,647개 (15%) 100.75개23개273개11%2.73억- 10단계 누적26.65억1기 10억(Phase 1~5) + 2기(Phase 6~10) 제네시스의 이득은 수량으로만 말한다 제네시스 홀더는 7단계 참여자의 6.5배 수량을 받는다. 참여 기간이 길어서만이 아니라 단계마다 지급 수량 자체가 줄기 때문이다. 그 수량이 나중에 얼마가 될지는 말할 수 없다. ### 품질 게이트 - 예산은 고정, 배분은 경쟁 quality gatelog ``` first pass normal 7 · excellent 8.4 · failing 0 redistribute unpaid share pro rata by weight · cap 10.5 per point · excess burned ``` 전송률 미달률유효 지점미지급분정상 최종우수 최종소각총 지급 0%1000개7.00개8.40개0개714개 10%9071.4개7.78개9.33개0개714개 20%80142.8개8.75개10.50개 (상한)0개714개 40%60285.6개10.50개10.50개84개630개 100지점 예시(예산 714 = 100 × 7.14). 모든 행에서 총 지급 + 소각 = 예산이다. 품질이 나쁠수록 총 방출이 줄어든다. 항목규칙 지급 조건전송률 95%+ 전액 · 80~95% 선형 감액 · 80% 미만 무지급 우수 보너스데이터 품질 상위 10%에 +1.4개 - 공기가 좋은 곳이 아니라 측정이 정확한 곳 품질 등급 산출전송률 게이트를 통과한 지점에 대해 품질 점수(가동률·이상치·인근 상관·보정)를 매기고, 상위 10%가 우수(1.2), 나머지가 정상(1.0), 게이트 미달이 0 부정 방지설치 사진은 진입 관문 · 지속 검증은 CO₂ 일주기 패턴 구현 상태 (2026-09) 현행 정산 엔진은 1단계 기준으로 지점당 일 7개를 적립하고 24시간 홀드 후 클레임한다. 우수 보너스(8.4)·미지급분 재분배·단계 계수 0.78은 본 정책의 확정 사항으로, 엔진의 거버넌스 변수로 순차 반영한다. 반영 전까지 실제 지급은 1차 지급 규칙(정상 7 · 미달 0)만 적용된다. ### 소각 - 예정 15% + 매출 연동 매입 구분수량방식 예정 소각15억총 발행량의 15% · 분할 방식 별도 공지 매입 소각매출 연동기기 판매액 10% · 이용권 판매액 10% · 데이터 매출 50%로 매입 후 소각 집행매월매입 수량 · 가격 · 트랜잭션 공개 원칙내용 매출 연동매입 소각 규모는 매출 비율로 정해진다 - 유통량 목표로 시장에 개입하지 않는다 매입분 전량 소각되사서 다시 파는 구조는 방어가 아니라 자전거래다 품질 미달분 소각재분배 상한을 넘긴 물량은 지급하지 않고 소각한다 2기 미개방분 소각게이트 미통과로 열리지 않은 2기 리워드는 소각 대상으로 전환된다 매월 공개매월 집행하고 매입 수량 · 가격 · 트랜잭션을 공개한다 소각은 「이번 분기 방출 0.9억개 · 소각 1.1억개」처럼 수량으로만 공표한다. 「소각으로 가격이 오른다」는 말은 하지 않는다 - 검증할 수 없는 주장이기 때문이다. ### 마일스톤 게이트 - 연차가 아니라 달성으로 게이트공급 조건수요 조건보상 가중치소각거래소 G0 개시 전등록·검증 시스템유료 고객 1건--- G1 제네시스 개시모집 시작-품질없음- G2 유동성 확보초기 물량 판매-품질없음XRPL DEX 풀 개설 G3 수요 증명5,000장B2B 3건 + 재구매+ 희소성없음유동성 추가 예치 G4 완판25,000장 · 가동률 80% 3개월국내 매출 발생품질 + 희소성없음유동성 추가 예치 G5 해외 1호60,000해외 매출 발생동일없음지역 거래소 검토 G6 실외 라인12만연매출 10억+ 실사용없음- G7 소각 개시22만연매출 20억3요소개시- G8 전환기35만연매출 40억동일본격- G9 자립65만사업 수익이 리워드 재원을 전액 부담동일가속글로벌 거래소 검토 G10 성숙100만잉여 수익 발생동일최대- 수요 조건을 못 채우면 다음 게이트가 열리지 않는다. 미달 시 대응은 단계 진입 보류와 방출 축소다. ### 보조금에서 고객 자금으로 국면지점단계 방출량재원 1~4단계2.5만 → 22만늘어남보조금 - 하드웨어 사업 수익이 부담 5~6단계35만 → 50만정점 후 감소전환기 - 데이터 매출이 상당 부분 부담 7단계~65만 →계속 감소자립 - 고객 매출이 전액 부담 · 잉여는 소각 왜 교차하는가 방출은 단계 체감 22% 때문에 6단계에서 정점을 찍고 줄어드는데 데이터 매출은 지점 수에 비례해 계속 늘어난다. 두 곡선이 만나는 지점부터 신규 참여금이 아니라 고객이 낸 돈으로 리워드를 지급한다. 자립 시점은 데이터 단가에 달려 있고, 그 단가는 아직 검증 중이다. 1단계에서 실제 계약으로 확인한 뒤 재산정하며, 달성을 약속하는 것이 아니라 목표로 제시한다. ### 제네시스 모집 구조 ① 실물 기기 실내 날씨 측정기 PM · CO₂ · VOC · 온습도 측정 · 실물 자산으로 남는다 ② 관측 지점 자격 민간기상스테이션 라이선스 NFT 1장 = 관측 지점 1개 · 권리 분리 판매 불가 ③ 데이터 제공 리워드 일 7개 WLBN 품질 등급 반영 · 단계마다 22% 체감 ④ 구매 순번 후속 프로젝트 우선 구매권 실외 라인 우선 제안 · 수익권이 아닌 순번이다 판매 회차시기가격구매 한도 1차 제네시스 판매2026-09-15 (사전예약 9/7~9/14)450 RLUSD · 750,000원 (정상가 650 RLUSD)계정당 10대 2차 일반 판매2026-10-03 09:00 KST부터 준비 수량 소진 시까지650 RLUSD · 1,083,000원개인 10대 · 대리점 누적 100대 이상 30% 할인 구매 한도는 개인 1인당 10대이며, 누적 100대 이상 구매하는 대리점에는 30% 할인을 적용한다. 기기 NFT가 발행됐으나 아직 설치하지 못한 기간에도 NFT당 일 정액 보상을 지급한다. 설치 후에는 가동 · 품질 기준이 적용된다. ### 운영 원칙과 공개 정책 유동성 투입 우선순위기간수혜 ① 거래 깊이영구거래하는 모두 ② 매입 소각영구남은 홀더 전원 ③ 매수 완충일시그 순간 판 사람 기기 판매액의 10%, AI 팩토리 이용권 판매액의 10%, 데이터 매출의 50%로 WLBN을 매입해 소각한다(매월 집행 · 내역 공개). 결제 구조는 기업 고객 원화·RLUSD, AI 에이전트 자동 결제, 프로토콜 내부는 매출 일부로 WLBN 매입이다. 고객에게 토큰을 사서 결제하라고 요구하지 않으며, 배분 비율은 사전 공표한다. 항목공개비공개 유동성풀 잔고 온체인 상시 · 투입 규칙 · 소각 실적매수 주문 가격 · 매입 시점 베스팅일정 전체 · 언락 수량과 시점- 리워드지급 수량 · 품질 기준 · 재분배 결과- 공개된 매수 주문은 시장에 무료 풋옵션을 주는 것이라, 장기 홀더가 아닌 단기 트레이더에게 재원이 흘러간다. 하지 않는 다섯 가지: 노드 수를 KPI로 삼기 · 수요 없이 다음 단계 열기 · 수요 증명 없이 중앙화 거래소 상장 · 모든 데이터에 같은 보상 · 보조금 영구화. 우리가 보는 지표는 토큰 가격이 아니라 완판 · 가동률 · 데이터 매출이다. 판매가 목표에 미달하면 가격을 방어하는 것이 아니라 방출을 줄인다. ## 운영 방출 시뮬레이션 - 단계 체감 모델 10단계 누적 방출과 자립 교차점을 단계 체감 모델로 계산한다. 모든 수치는 공표된 정책 파라미터(기본 7개 · 체감 0.78 · 게이트별 지점 수)에서 유도되며, 보상 수준이나 수익에 대한 예측·약속이 아니다. 실제 수령량은 품질 등급과 재분배 결과에 따라 달라진다. ### 결론 먼저 Finding 01 방출 정점은 6단계다 지점 수는 늘지만 지점당 수량이 단계마다 22% 줄어, 단계 방출량은 6단계(3.74억 부근)에서 정점을 찍고 감소한다. 데이터 매출은 지점 수에 비례해 계속 늘어난다. Finding 02 제네시스 = 7단계의 6.5배 수량 1단계 참여자의 누적 수령은 10,646개, 7단계 진입자는 1,647개다. 차이는 참여 기간보다 단계별 지급 수량 감소에서 온다. Finding 03 품질이 나쁠수록 총 방출이 준다 에폭 예산은 고정이지만 미지급분 재분배에 지점당 10.5개 상한이 걸려, 미달 지점이 40%면 예산의 12%가 소각된다. 총 지급 + 소각 = 예산. Finding 04 수요가 없으면 방출도 없다 단계 진입은 지점 수와 수요 조건을 동시에 요구한다. 수요 미달 시 단계를 보류하고 방출을 줄이므로, 노드가 먼저 늘어 공급이 앞서는 상황이 구조적으로 막힌다. ### 모델 구조 core model equationslog ``` reward(p, g) = 7 × 0.78^(p − 1) × g g ∈ { 0, 1.0, 1.2 } budget(p, n) = n_active × 7.14 × 0.78^(p − 1) fixed per epoch redistribute = unpaid × w_i / Σw cap 10.5 per point · excess → burn emission(p) = Σ_epochs budget(p, n) − burn(p) Σ p=1..10 = 2.665B ≤ 4.6B (node rewards: round 1 1B + round 2 3.6B) ``` ### 확정 파라미터 항목확정값근거 기본 리워드일 7개 (정상 등급)사전 공표 수량 · 시세와 무관 단계 체감0.78 (22%)10단계 누적을 노드 리워드 1·2기 합계 46억 이내로 맞추는 값 품질 등급0 · 1.0 · 1.2미달 · 정상 · 우수(상위 10%, +1.4개) 재분배 상한지점당 10.5개소수 독점 방지 · 초과분 소각 1단계 지점 수25,000국내 실내 · 1차 제네시스 판매 + 2차 일반 판매 10단계 누적 상한26.65억개1기 10억(Phase 1~5) + 2기(Phase 6~10) 제네시스 가산+20%제네시스 멤버십 보유자가 보유한 기기 NFT 보상에 20%를 가산한다 2기 리워드36억1기 소진 후 단계 게이트 통과 시 개방 · 미개방분 소각 전제를 밝힌다 자립 시점은 데이터 단가에 달려 있고, 그 단가는 아직 검증 중이다. 1단계에서 실제 계약으로 확인한 뒤 재산정한다. 표의 값은 위 식에 게이트별 지점 수를 대입해 얻으며 공표 데크와 동일하다. 달성을 약속하는 것이 아니라 목표로 제시한다. ## 운영 잔여 리스크 구현 중 인지하고 관리해야 할 항목들. 항목리스크대응우선순위 기기 신원 NFT 보유만 검증하면 시드 탈취 시 위조 데이터 무제한 주입 페이로드 서명 검증 + 시계열 이상탐지 + 인근 노드 교차검증권장 오라클 신뢰 오프체인 API가 단일 신뢰점 - 데이터가 원장에 남지 않음 배치 단위 머클루트를 Memos에 기록해 사후 감사 가능성 확보권장 Reserve 고갈 TrustLine·Offer 누적으로 Hot wallet XRP 부족 → 전 트랜잭션 실패 잔액 임계 모니터링 + 미체결 Offer 정리 배치권장 가격 변동받는 수량은 고정이지만 그 수량의 가치는 오르내린다. 하락 위험은 전적으로 보유자가 부담한다정량 지급 · 가치 보장 없음 · 목표가·수익률 미제시인지 사업 미달데이터가 팔리지 않으면 다음 단계가 열리지 않고 리워드 수량이 줄어든다게이트에 수요 조건 병기 · 미달 시 방출 축소필수 가동 의무설치 후 가동하지 않는 지점은 리워드가 지급되지 않는다(설치 전 NFT는 일 정액 지급). 전송률 80% 미만도 무지급이다등급별 차등 · 사전 공표된 기준 · 설치 사진과 CO₂ 일주기 검증필수 초기 거래 여건개시 직후에는 거래가 얕아 적은 거래로도 시세가 크게 움직일 수 있다초기 풀 예치 후 추가 예치 · 대량 거래 분리 · 중앙화 거래소 상장은 게이트 통과 후권장 실내 데이터 민감성 CO₂·PM 시계열에서 재실 인원·취침 시각·부재 기간이 추정됨 5단계 비식별화 + k-익명성 + 차분 공격 방어. 개인정보 법률 검토 선행 필수필수 규제 변화가상자산 규제는 관할마다 다르고 바뀐다권역마다 현지 규제를 검토해 진입 여부와 방식을 정한다 · 유틸리티 성격 유지(지분·배당 없음)필수 핫월렛 키 관리Hot Wallet 시드 유출 시 보상 풀 잔액이 즉시 유출됨KMS 기반 서명 분리 · 잔액 상한 유지 · Issuer(cold) 시드는 런타임 미로드필수 RLUSD 발행자 의존RLUSD 발행자(rMxCK…)의 동결·정책 변경이 결제·정산 경로를 차단할 수 있음TrustLine 상태 모니터링 + 대체 결제 통화 경로 사전 정의권장 아웃박스 중복 지급크론 재시작 시 미확정 트랜잭션을 재제출하면 이중 지급 위험아웃박스 멱등키 + 제출 전 tx 조회 + reconcile 크론 대조필수 클레임 홀드 중 장애24시간 홀드 구간에서 정산 장애가 나면 적립분이 클레임 불가 상태로 남음적립 원장과 온체인 결과를 reconcile 크론이 대조해 미지급분 자동 복구필수 센서 캘리브레이션 드리프트 저가 MOX·광산란 센서는 수개월 단위로 편차가 누적되어 데이터 상품성이 하락 품질 점수의 calibration(15점) 항목 + 인근 노드 상관 기반 자동 보정 계수권장 x402 요청 헤더 본 구현은 X-Payment-Tx: 커스텀 헤더를 쓰며, 선불 크레딧(X-Api-Key · /api/data/topup) 경로를 병행한다 표준 x402는 X-PAYMENT 헤더를 쓴다. 응답 바디는 이미 표준 스키마이므로, 헤더만 교체하면 정합 완료인지 ## 운영 실행 체크리스트 각 Step은 승인 후 다음 단계로 진행한다. 완료 판정 기준을 함께 명시했다. ### Step 1 - XRPL 기반 & 지갑 메인넷 WebSocket(wss://xrplcluster.com) 클라이언트 싱글턴 + 재연결 처리완료 기준: server_info 응답 수신 - Issuer(cold, rDJz8…) / Hot(rhTTt…) / Treasury(rPZFY…) 3개 지갑 준비 및 XRP Reserve 확보 - .env + .env.example 구성, .gitignore 등록, zod 검증 - 공통 submit() 래퍼로 tesSUCCESS + validated 강제 ### Step 2 - Wellbian 토큰 - currency hex 상수 확정 (WLBN / RLUSD) - Issuer AccountSet - asfDefaultRipple 활성화 - Hot·Treasury·사용자 지갑 TrustSet - tfSetNoRipple 적용 - 총 100억 WLBN 발행 완료 확인 (추가 발행 없음)완료 기준: account_lines에서 잔액 확인 ### Step 3 - 라이선스 NFT - IPFS 메타데이터 스키마 확정 및 업로드 (실 URI 고정) - mintLicense → offerLicenseTo → acceptLicense 전체 흐름 - verifyDeviceLicense() - Issuer + Taxon 이중 확인 - 미보유 주소가 valid:false로 판정되는 네거티브 테스트 ### Step 4 - 오라클 & 보상 - GET /v1/c 수집 프록시 구현 + 5단계 수신 검증 - 기기 서명 검증 → 라이선스 조회 순서 준수 (싼 검사 우선) - 라이선스 검증 실패 시 403, 보상 미지급 확인 - 절대습도·이슬점 유도 후 저장완료 기준: absoluteHumidity(20,60)≈10.35, (30,60)≈18.21 - 일 정산 - 총 지급 + 소각 = 에폭 예산(활성 지점 × 7.14 × 단계 계수), 지점당 10.5개 이내완료 기준: 전송률 미달 지점 비율을 0~40%로 바꿔도 총 지급 + 소각 = 예산 - 클레임 API - 24시간 홀드 경과분만 1건의 WLBN Payment로 합산 지급 - reconcile 크론 - 온체인 결과와 적립·지급 원장 대조, 불일치 자동 복구 - 배치 머클루트를 Memos에 기록, 머클 증명 검증 - 핫월렛 아웃박스 패턴 적용 · 크론 중단 후 재개 검증완료 기준: 재개 시 중복 지급 0건, Sequence 오류 0건 ### Step 5 - AMM - AMMCreate WLBN/RLUSD 메인넷 풀 생성 완료 (TradingFee 0.5%, 스왑 전용) - AMMDeposit 추가 유동성 공급 및 LPToken 수령 확인 - path_find + SendMax 기반 RLUSD→WLBN 스왑 - 스왑 전후 amm_info 풀 비율 변화 기록 ### Step 6 - x402 게이트웨이 - 헤더 없는 요청 → 402 + accepts 배열 결제 조건 - verifyPayment() - 수취인·발행자·통화·전달액·tesSUCCESS 검증 - delivered_amount 기반 판정 (PartialPayment 방어) - tx_hash 재사용 차단 nonce 스토어 - 에이전트 E2E(메인넷 실결제): 402 → 결제 → 재요청 → 200완료 기준: 스크립트 1회 실행으로 전 과정 무인 완료 - 소각 배치 잡(예정 · 매입 · 품질 미달분) 실행 및 소각 수량·tx 공표, 유통량 감소 검증 ### IAQ 사양 - 데이터 계층 - IAQ 텔레메트리 스키마 v1 확정 + 기기 서명 규격 - Taxon 분리 (IAQ 3026 / OAQ 2026), 품질 등급(0 · 1.0 · 1.2) 적용 - qualityScore() 4개 항목 산출 - 특히 인근 노드 상관 - 비식별화 P1~P5 파이프라인, 에폭 salt 회전 확인 - k-익명성 롤업 - geohash3에서도 미달 시 응답 거부완료 기준: 노드 4개 격자 질의가 상위 격자로 롤업되거나 거부됨 - 차분 공격 방어 4종 (반올림·필터 상한·질의 예산·최소 코호트) - 코호트 응답에 노드 목록이 포함되지 않음을 스키마 레벨에서 보장 - 선불 크레딧 - 충전·서명 차감·nonce 단조 증가완료 기준: 동일 nonce 재전송이 BAD_NONCE로 거부됨 - 미지급분 재분배 - 지점당 10.5개 상한, 초과분 소각 검증 운영 검증 시나리오 - 미인가 주소로 IAQ 텔레메트리 전송 → 403 (라이선스 없음) - NFT 발급 후 동일 주소 재전송 → 202 Accepted, 절대습도 유도값 확인 - 에폭 배치 실행 → 노드별 품질 점수와 가중치 배분 출력, 총액 = 예산 확인 - 노드 수를 10배로 복제 후 재실행 → 예산 = 활성 지점 × 7.14 × 단계 계수로 비례 증가, 지점당 수량 불변 - 제조사가 코호트 질의 → 402, 결제 후 재요청 → 200 + 크기만 반환 - 필터를 좁혀 재질의 → 422 COHORT_TOO_NARROW (실제 크기 미노출) - 같은 tx_hash로 재호출 → 402 (HASH_ALREADY_USED) - 소각 배치 → Issuer TrustLine 상계로 유통량 감소 확인 ## 운영 용어집 | 용어 | 설명 | | DePIN | Decentralized Physical Infrastructure Network. 물리 인프라(센서·기지국 등)의 구축·운영을 토큰 인센티브로 분산화하는 모델. | | XLS-20 | XRPL의 네이티브 NFT 표준. NFTokenMint 등 전용 트랜잭션 타입을 제공하며 스마트컨트랙트가 불필요하다. | | XLS-30 | XRPL 네이티브 AMM 표준. 오더북과 AMM 유동성이 자동으로 합산 라우팅된다. | | Issued Asset (IOU) | 발행자 계정에 대한 채권 형태의 토큰. 별도 mint 트랜잭션 없이 발행자가 Payment를 보내면 생성된다. | | TrustLine | 보유자가 특정 발행자의 특정 통화를 얼마까지 받을지 선언하는 온체인 관계. 없으면 tecNO_LINE. | | Rippling | 동일 통화의 TrustLine들을 연결해 잔액이 자동 이동하는 XRPL 고유 메커니즘. 발행자 경유 전송의 기반이자 오남용 위험 요소. | | x402 | HTTP 402 Payment Required를 실사용하는 M2M 결제 프로토콜. 서버가 결제 조건을 제시하고 클라이언트가 온체인 결제 증빙을 붙여 재요청한다. | | delivered_amount | 트랜잭션 메타데이터에 기록된 실제 전달 금액. PartialPayment 우회를 막기 위해 검증은 이 값으로만 수행한다. | | Destination Tag | Payment에 붙이는 32비트 정수 식별자. 동일 수취 주소로 들어오는 결제의 용도·고객을 구분한다. | | IAQ | Indoor Air Quality. 실내 날씨(날씨). 본 문서에서는 PM2.5·PM10·CO₂·TVOC·온도·습도 6종 지표의 총칭. | | 절대습도 (AH) | 단위 부피당 실제 수증기 질량(g/m³). 온도 종속인 상대습도와 달리 제습 부하를 직접 나타내므로, 제습 알고리즘·결로 예측의 입력값이 된다. | | TVOC | 총휘발성유기화합물. MOX 센서는 상대 지수를 내므로 절대 농도로 해석하지 않고 추세·급변 탐지에 사용한다. | | geohash | 위경도를 문자열로 인코딩하는 격자 체계. 자릿수가 짧을수록 넓은 영역을 뜻한다 (5자리 ≈ 4.9km, 3자리 ≈ 156km). | | k-익명성 | 어떤 속성 조합으로도 최소 k명 미만으로 좁혀지지 않도록 보장하는 성질. 본 사양은 k=5를 하한, 반올림 후 25를 실질 하한으로 둔다. | | 차분 공격 | 필터를 조금씩 바꾼 반복 질의의 결과 차이로 개별 대상을 분리해내는 공격. 카운트 반올림·필터 개수 상한·질의 예산으로 방어한다. | | 에폭 (Epoch) | 보상 정산 주기. KST 자정을 경계로 하는 1일 고정 단위이며, 이 단위로 에폭 예산(활성 지점 × 7.14 × 단계 계수)을 품질 등급에 따라 지급·재분배한다. | | 선불 크레딧 | 온체인 결제 1건으로 잔액을 충전하고 이후 호출은 서명으로만 차감하는 방식. 고빈도 API에서 원장 확정 지연을 회피한다. | | RLUSD | 본 생태계의 정산 통화. 발행자는 rMxCKbEDwqr76QuheSUMdEGf4B9xJ8m5De이며 WLBN Issuer와 다르다. | | Issuer · Hot · Treasury 지갑 | Issuer(cold, rDJz8WJhsKgydqJzSZXMpsJot3eRmSkR5)는 발행 전용으로 런타임 미로드, Hot(rhTTtx8YyKzcmPDsoroYXuHdGMThrnweb1)은 지급·수취, Treasury(rPZFY2yLgqjmBrxtxWHXnB1doBt7gu7xin)는 보관을 담당한다. | | 클레임 홀드 | 에폭 정산으로 적립된 보상이 클레임 가능해지기까지의 24시간 대기 구간. 홀드 경과분은 1건의 WLBN Payment로 합산 지급된다. | | 아웃박스 패턴 | 핫월렛 트랜잭션을 먼저 아웃박스에 기록하고 단일 워커가 순차 제출하는 방식. 크론이 중단돼도 미완료 항목부터 재개해 이중 지급을 막는다. | | Taxon 체계 | 관측소 1001·1002·1003, 스테이션 IAQ 3026 · OAQ 2026 · AI 인프라 4026 · 레이더 5026 · 위성 6026, 예매권 1010. | | 지원 지갑 | 간편지갑(아이디·비밀번호 / 구글), D'CENT 모바일 인앱, Xaman, GemWallet, Crossmark. | | 단계 (Phase) · 단계 계수 | 지점 수와 수요 조건을 함께 만족하는 게이트로 넘어가는 방출 구간. 단계 계수 0.78^(단계−1)이 지점당 수량에 곱해져 단계마다 22% 체감한다. | | 품질 등급 | 전송률 게이트와 품질 점수로 정해지는 0 · 1.0 · 1.2의 계수. 미달 0, 정상 1.0(일 7개), 상위 10% 우수 1.2(일 8.4개). | | 재분배 | 에폭 예산 중 1차 지급 후 남은 미지급분을 가중치 비례로 나누는 절차. 지점당 10.5개를 상한으로 하고 초과분은 소각해 총 지급 + 소각 = 예산을 유지한다. | | 마일스톤 게이트 | G0~G10의 단계 진입 조건. 공급(지점 수·가동률)과 수요(유료 고객·매출) 조건을 병기하며, 수요 미달 시 단계를 보류하고 방출을 줄인다. | | 매입 소각 | 매출의 정해진 비율로 시장에서 WLBN을 매입해 Issuer로 되돌리는 소각. 매입분은 전량 소각하며 매월 수량·가격·트랜잭션을 공개한다. | | 매출 연동 소각 | 기기 판매액 10%, 이용권 판매액 10%, 데이터 매출 50%로 WLBN을 매입해 소각하는 운영 규칙. 매월 집행하고 내역을 공개한다. | | Blackhole 계정 | Regular Key를 무효 주소로 설정하고 마스터키를 비활성화해 영구히 서명 불가하게 만든 계정. 본 시스템의 Issuer는 blackhole이 아니며, 소각은 Issuer 반환에 의한 상계 방식이다. | 실내외 날씨 데이터 생태계 (IAQ / OAQ)

실내(IAQ)·실외(OAQ) 날씨 데이터를 수집해 가전 제조사의 R&D·마케팅과 환경 서비스에 공급하고, 기여자에게 온체인 보상을 돌려주는 데이터 생태계.

XRP Ledger 메인넷(2026-08-13 가동) · XLS-20 NFT · Issued Asset · XLS-30 AMM · x402

paper.wellbianlabs.io · wellbian.io/docs/device-api.html · wellbian.io/data · wellbian.io/redeem

본 문서의 보상 수치는 계획 기준이며 수익·원금을 보장하지 않고, Wellbian(WLBN)의 법정화폐 환산가치를 제시하지 않는다.
