All private work

Private work 04 / 05

mineUP

집중 유동성 LP 자동 리밸런싱 봇 + 실시간 대시보드

Status
일시 정지
Stack
TypeScript · Node.js · viem · Fastify · React · Vite · Vitest · pnpm
Access
코드 비공개 · 요청 시 열람 가능열람 요청은 아래 연락처로
CL 레인지 모니터의 풀 레이더: 상단 지표와 풀별 TVL·거래량·배출 APR (가상 토큰·가상 데이터)

Overview

집중 유동성 풀에서는 현재 가격이 들어 있는 밴드의 스테이킹 유동성만 gauge 배출을 받습니다. 밴드를 좁힐수록 배출 지분은 커지지만 가격이 금방 밴드를 벗어나고, 그때마다 언스테이크 → 유동성 회수 → 비율 맞추기 → 재민팅 → 재스테이크를 해야 합니다. 리밸런스가 잦을수록 가스·스왑 수수료·슬리피지가 배출 수익을 갉아먹고, 체인에 서브그래프가 없어 풀 목록과 손익을 직접 모아야 했습니다.

Features

  • 상태머신 기반 자동 리밸런싱: 밴드 이탈 시 언스테이크 → 회수 → 스왑 재조정 → 새 밴드 민팅 → 스테이킹을 단계별 트랜잭션으로, 실패 지점부터 재시도
  • 온체인 우선 상태 복원: 로컬 상태는 캐시로만 쓰고 기동할 때마다 온체인 포지션·스테이킹 상태와 대조
  • 실시간 대시보드: 풀 레이더, 게이지 분석, 내 포지션, 봇 현황, 수동 작업(미리보기 후 실행)
  • 손익 분해 회계: 포지션마다 가격 효과·비영구적 손실·배출·비용을 나눠 "봇 성과"를 시장 방향과 분리해 판단
  • 비용 방어선과 알림: 시퀀스 전 비용 상한, 레인지 이탈 디바운스, 롤링 비용 예산, 비용/배출 경보, 텔레그램 킬스위치

Architecture

아키텍처: pnpm 모노레포와 체인 연동
Robinhood Chain의 풀·포지션 매니저·게이지·라우터와 연결된 core, bot, dashboard server, web 패키지, 상태 파일·로그·텔레그램 구조도
다이어그램 원문 (Mermaid)
flowchart TB
  subgraph Chain["Robinhood Chain (L2)"]
    POOL["Slipstream CL Pool"]
    NPM["Position Manager (NFT)"]
    GAUGE["CL Gauge (배출)"]
    ROUTER["Swap Router"]
  end
  subgraph Mono["pnpm 모노레포 (TypeScript)"]
    CORE["packages/core<br/>viem 클라이언트 · ABI/주소 맵<br/>BigInt 틱·유동성 수학<br/>tx 파이프라인 · 가드"]
    BOT["apps/bot<br/>상태머신 엔진<br/>planner · sequencer · defenses"]
    SERVER["apps/dashboard/server<br/>Fastify + WebSocket<br/>풀·게이지 인덱싱 · 손익 집계"]
    WEB["apps/dashboard/web<br/>React + Vite"]
  end
  STATE[("state/<br/>bot-state.json · KILL")]
  LOGS[("logs/*.jsonl<br/>구조화 로그")]
  TG["Telegram<br/>알림 · 조회 · 킬스위치"]
  POOL -- "WS newHeads / Swap 로그<br/>(끊기면 HTTP 폴링)" --> CORE
  CORE --> BOT
  CORE --> SERVER
  BOT -- "서명 tx (가드 통과 시)" --> NPM
  BOT --> GAUGE
  BOT --> ROUTER
  BOT <--> STATE
  BOT --> LOGS
  LOGS --> SERVER
  STATE --> SERVER
  SERVER -- "스냅숏 (localhost)" --> WEB
  WEB -- "킬스위치 · 미리보기/실행" --> SERVER
  SERVER --> TG
원본 크기로 보기
리밸런스 상태머신
IDLE에서 민팅 계획, 스왑, 민팅, 스테이킹, 모니터링을 거치고 레인지 이탈 시 언스테이크·회수 후 다시 계획, 킬스위치나 연속 실패 시 HALT
다이어그램 원문 (Mermaid)
stateDiagram-v2
  [*] --> IDLE
  IDLE --> PLAN_MINT: 포지션 없음
  PLAN_MINT --> SWAP: 비율 재조정
  SWAP --> MINT
  MINT --> TOPUP: 잔여 자산 보충
  TOPUP --> STAKE
  MINT --> STAKE
  STAKE --> MONITOR
  MONITOR --> UNSTAKE: 레인지 이탈 (디바운스 후)
  UNSTAKE --> EXIT: decrease + collect
  EXIT --> PLAN_MINT
  MONITOR --> CLAIM: 주기적 배출 수령
  CLAIM --> MONITOR
  MONITOR --> HALT: 킬스위치 · 연속 실패
  HALT --> [*]: 수동 재개
원본 크기로 보기

Key decisions

"Slipstream ≠ Uniswap V3"를 절대 규칙으로

이 DEX의 풀은 fee가 아니라 tickSpacing으로 식별하고 수수료는 거버넌스가 바꿀 수 있어 V3용 SDK를 쓰지 않았습니다. 컨트랙트 주소와 ABI는 Blockscout verified 소스와 온체인 팩토리 조회로 교차 검증한 뒤 출처를 남겼고, 수수료는 매번 풀에서 읽습니다.

인덱서 없이 직접 인덱싱하고, 온체인을 유일한 진실로

팩토리 이벤트 스캔과 로그 조회로 풀 목록과 거래량을 직접 만들고, 재시작·네트워크 단절·불확실한 트랜잭션이 있어도 기동 시 온체인과 대조해 복원합니다. 금액과 틱 계산은 BigInt로 격리된 수학 모듈에서 하고 단위 테스트로 고정했습니다.

지표를 "APR"이 아니라 "봇 성과"로 정의

봇이 흑자여도 토큰 가격 하락 때문에 총손익이 적자인 구간이 반복돼, 손익을 가격 효과·IL·배출·비용으로 분해해 보여 주도록 바꿨습니다. 한 시점의 스냅숏만 보고 풀을 전환하면 반복해서 실패했다는 점도 설계 문서에 교훈으로 남겼습니다.

Security

  • 봇 전용 핫월렛: 메인 지갑 키를 재사용하지 않고 운용 자금과 가스비만 둠
  • 개인키는 소유자만 읽을 수 있는 .env에서만 읽고, 로그·에러·대시보드·커밋에 키나 서명 원문이 나가는 경로를 두지 않음
  • 모든 트랜잭션이 하나의 가드 파이프라인: deadline, minOut, 가스비 상한, 시뮬레이션 선행, 필요한 만큼만 approve
  • 킬스위치 파일이 있으면 어떤 경로로도 새 트랜잭션을 보내지 않음, 텔레그램에서는 켜기만 가능
  • 수동 작업은 미리보기 digest를 받은 뒤 짧은 유효 시간 안에 같은 digest로만 실행, fail-closed와 수동 재개
  • 대시보드 서버는 127.0.0.1에만 바인딩

Status

일시 정지

읽기 전용 대시보드, 수동 조작, 자동화까지 구현하고 소액 실자금 자동 운용으로 동작을 검증했습니다. 지금은 킬스위치를 켜 두고 멈춘 상태이며, 배출률 대비 가격 변동성을 다시 조사해 재가동 여부를 검토하고 있습니다.

07 — Contact

Let's
talk.

Email

이메일 주소는 스팸 수집을 막기 위해 JavaScript로 표시합니다. GitHub로도 연락할 수 있습니다.

GitHub

github.com/znan2