Skip to main content
이 페이지는 AI 자동 번역입니다. 모든 내용은 영문판을 기준으로 합니다.영문판 보기 →
AMM은 악의적인 코드의 매력적인 대상입니다. LP의 자금이 완전히 공개된 풀에 있고, 모든 스왑이 결정론적으로 가격을 변경합니다. 이 페이지는 어디서든 AMM에 대해 입증된 공격 클래스, Raydium에 어떻게 적용되는지, 그리고 Raydium(및 통합자)이 각각에 대해 어떻게 방어하는지를 정리합니다.

1. 샌드위칭 / MEV 공격

공격

봇이 메모풀/가십 스트림을 감시하다가 사용자의 스왑을 발견하고, 같은 방향의 매수로 앞서 실행(가격 상승)한 후, 사용자의 트랜잭션이 더 나쁜 가격에 실행되도록 한 다음, 반대 방향의 매도로 뒤따라 실행합니다. 봇은 스프레드로 이익을 얻습니다.

노출

  • 가장 노출됨: 낮은 TVL의 CPMM 풀과 AMM v4 풀 — 작은 거래도 가격을 의미 있게 움직입니다.
  • 덜 노출됨: 깊은 CLMM 풀 — 틱 내 거래는 가격을 움직이지 않습니다.
  • 노출 없음: 팜 수확, LP 예치(비율이 강제되며, 같은 방식으로 가격에 민감하지 않음).

방어

  • Jito 번들 (integration-guides/routing-and-mev) — 공개 메모풀에서 트랜잭션을 숨깁니다.
  • 타이트한 슬리피지 — 예상값에 더 가까운 최소 출력이 샌드위칭을 수익성 없게 만듭니다. ~0.3% 이하에서는 대부분의 샌드위칭이 손실을 봅니다.
  • 더 작은 거래 크기 — $100k 스왑을 10개의 $10k로 분할합니다. 각각은 가격을 덜 움직입니다.

Raydium의 입장

Raydium의 핵심 프로그램은 anti-MEV 보호를 강제하지 않습니다 — 프로그램 수준에서 중립적입니다. 보호는 제출 계층(Jito, 지갑의 내장 보호)에서 발생합니다. UI는 슬리피지를 기본값 0.5%로 설정하며, 이는 대부분의 풀에 합리적입니다.

2. 가격 조작

공격

대규모 거래자가 풀의 가격을 일시적으로 움직이고(플래시 론 또는 자체 자금으로), 가격에 따라 달라지는 다운스트림 작업(청산, 오라클 파생 차용, 파생상품 지급)을 트리거한 후, 가격을 정상으로 되돌립니다.

노출

  • 네이티브 Raydium 작업: 노출 없음. 풀 내외 현물 스왑은 왕복 수수료만 발생합니다. 거래자는 손실을 봅니다.
  • 통합 프로그램: Raydium 풀 가격을 순진하게 읽으면 노출됩니다.

방어

  • 현물 가격이 아닌 TWAP 사용 — 조합성의 경우 (security/oracle-and-token-risks 참조).
  • CLMM ObservationState — 지속적인 자본 약속 없이 조작할 수 없는 단기 윈도우 TWAP을 제공합니다.
  • 다중 오라클 합의: 프로그램이 Raydium, Pyth, Jupiter를 읽고 1% 이내에서 동의할 때만 작동하면, 단일 소스의 플래시 론 조작으로는 충분하지 않습니다.

Raydium의 입장

CLMM은 ObservationState TWAP 지원을 제공합니다. 이를 무시하고 현물 가격을 사용하는 통합자는 자신의 책임입니다. Raydium의 프론트엔드는 USD 표시를 위해 여러 가격 소스를 사용합니다.

3. 기부 / 인플레이션 공격

공격

새 풀의 첫 LP가 소량을 예치합니다(예: 6진수 민트 각각 1개 토큰 → 1개 LP 발행). 그 다음 공격자가 SPL 토큰 전송을 통해 풀 볼트에 직접 1,000,000개 토큰을 “기부”합니다. 이제 1개 LP 단위는 각 민트의 500,000개를 나타냅니다. 그 이후 그보다 적게 예치하는 LP는 0개 LP 단위로 반올림되어 예치금을 잃습니다.

노출

  • CPMM / AMM v4: 새로 생성된 저유동성 풀에서 잠재적으로 노출됩니다.
  • CLMM: 노출 없음(공유 LP 민트 없음; 각 포지션은 명시적 유동성 값을 가진 자체 NFT).

방어

CPMM의 initialize 명령어는 최소 LP 금액을 풀에 잠급니다(Uniswap V2의 MINIMUM_LIQUIDITY 패턴에서 영감을 받음). 이는 첫 LP가 sqrt(x × y) - MINIMUM_LIQUIDITY를 받으며, MINIMUM_LIQUIDITY(1000 단위)는 null로 소각됨을 의미합니다. 기부 공격은 공격자가 초기 예치금보다 훨씬 많이 기부해야 하므로 경제적으로 비효율적입니다. 또한 Raydium의 SDK는 초기 예치금이 작을 때 크게 경고하고 사용자를 합리적인 금액으로 안내합니다.

Raydium의 입장

MINIMUM_LIQUIDITY 잠금은 CPMM에 포함되어 있습니다. AMM v4도 유사한 메커니즘을 가지고 있습니다. 풀을 생성하는 사용자는 어쨌든 기부 공격을 경제적으로 비효율적으로 만들기 위해 각 민트의 최소 10,000+ 단위로 시드해야 합니다.

4. Token-2022 전송 훅 악용

공격

민트의 전송 훅은 업그레이드 가능합니다. 공격자가 민트 출시 시 무해한 훅을 배포하고, Raydium에 나열되어 사용자로부터 LP를 축적합니다. 나중에 훅을 업그레이드하여 모든 전송을 차단합니다(사실상 소프트 러그 — 사용자가 출금할 수 없음). 공격자는 풀을 한 방향으로만 거래 가능하게 만들고, LP를 싸게 매수하고, 훅을 잠금 해제하고, 승리합니다.

노출

전송 훅 민트를 포함하는 풀.

방어

  • 프로그램 수준: Raydium 프로그램은 스왑 중에 훅을 호출합니다. 훅이 차단하면 스왑이 되돌려집니다. 이것이 공격을 기계적으로 방지하지는 않습니다.
  • UI 수준: Raydium은 전송 훅 민트가 있는 풀에 플래그를 지정합니다.
  • 통합자 수준: 애그리게이터는 기본적으로 전송 훅 민트를 건너뛰고 검증된 훅만 허용 목록에 추가해야 합니다.

Raydium의 입장

Raydium은 전송 훅 풀을 금지하지 않습니다(합법적인 훅이 존재함), 하지만 명확하게 태그를 지정합니다. tags.includes("TRANSFER_HOOK")으로 필터링하는 애그리게이터는 원하면 제외할 수 있습니다.

5. 조합성 / CPI 익스플로잇

공격

프로그램이 CPI를 통해 Raydium을 조합하고 버그를 도입합니다. 예를 들어, 잘못된 observation_state, CLMM 스왑을 위한 잘못된 틱 배열, 또는 계정 이중 지출을 전달합니다. 공격자가 버그 있는 조합을 식별하고 악용합니다.

노출

  • 버그 있는 통합자 — 일반적으로 버그의 원인.
  • Raydium — Raydium 프로그램 자체에서 의도하지 않은 동작을 트리거하는 경우에만.

역사적 예시

Raydium의 프로그램 중 CPI를 통해 악용된 것은 없습니다 — Raydium의 계정 검증자가 잘못된 형태의 계정을 포착하고 되돌립니다. 더 넓은 생태계의 익스플로잇은 AMM과 조합되었지만 AMM에서 비롯되지 않은 사용자 정의 프로그램 버그를 통해 발생했습니다.

방어

  • 호출 프로그램은 가능할 때 Anchor CPI 헬퍼(손으로 만든 명령어가 아님)를 사용해야 합니다 — 타입 안전성이 대부분의 오용을 포착합니다.
  • 통합 테스트는 메인넷 포크 상태에 대해 조합 사례를 다룹니다.

6. 관리자 / 키 손상

공격

관리자 키(업그레이드 권한, AmmConfig 관리자, 프로토콜 수수료 청구)가 손상됩니다. 공격자가 풀을 드레인하는 악의적인 업그레이드를 배포하거나, AmmConfigs를 수정하여 수수료를 공격자 지갑으로 라우팅하거나, 프로토콜 수수료를 드레인합니다.

노출

security/admin-and-multisig에 문서화된 모든 역할.

방어

  • 3/4 멀티시그 — 업그레이드 권한에는 4명의 독립적인 서명자를 손상시켜야 합니다.
  • 24시간 타임락 — 업그레이드에서 사용자가 악의적인 업그레이드가 활성화되기 전에 풀 해제할 시간을 제공합니다.
  • 운영 모니터링 — Squads의 공개 큐를 통한 모든 멀티시그 활동에 대한 경고.

역사적 사건

AMM v4의 풀 권한 키가 2022년 12월에 손상되었습니다(멀티시그 이전). 수정: 모든 권한을 Squads 멀티시그로 이동. 수정 후 사건 없음.

7. CLMM 틱 수학에 대한 경제적 공격

공격

정교한 공격자가 CLMM 틱 수학의 반올림 또는 수수료 회계 엣지 케이스를 악용합니다. 다른 CLMM 구현(Raydium 아님)에서 발견된 예시:
  • 사용자에게 반올림하는 수수료 성장 회계, 먼지 축적.
  • 잘못된 fee_growth 델타를 크레딧/디빗하는 틱 교차.
  • sqrtPrice * liquidity 곱에서 정수 오버플로우.

노출

복잡한 맞춤형 수학. 감사 및 퍼징이 주요 방어입니다.

Raydium의 입장

CLMM은 두 개의 독립적인 감사(OtterSec + MadShield)와 지속적인 속성 기반 퍼징을 받았습니다. 현재까지 프로덕션에 영향을 미치는 버그가 발견되지 않았습니다. sqrt_price_x64 Q64.64 산술은 경계 틱을 다루는 단위 테스트와 함께 포화 128비트 수학을 사용합니다.

8. 포지션 NFT 혼동

공격

사용자가 CLMM 포지션 NFT를 공격자에게 전송하는 트랜잭션에 서명하도록 속습니다. 공격자가 이제 포지션의 유동성을 소유합니다.

노출

모든 포지션 NFT 보유자.

방어

  • 지갑 UI는 Raydium 포지션 NFT를 인식하고 구별되게 표시해야 합니다(일반 NFT로 “전송”하지 않음).
  • 사용자는 NFT를 전송하는 트랜잭션에 서명할 때 주의해야 합니다.
  • 새 포지션은 V2 오픈 경로를 사용하고 기본 볼트 민트의 동결 권한이 CLMM의 제한된 발급자 목록과 일치할 때만 동결됩니다. 해당 일치 포지션은 전송할 수 없으며 토큰 계정 소유자를 변경할 수 없습니다. 다른 모든 새 포지션은 전송 가능합니다.

Raydium의 입장

포지션 NFT는 Metaplex의 메타데이터 표준을 구현합니다. CLMM 포지션을 이해하는 지갑 앱은 거래 가능한 NFT가 아닌 유동성 포지션으로 표시합니다. 2026년 현재 대부분의 주요 Solana 지갑이 이를 특별히 표시합니다. 제한된 발급자 동결은 대상이 지정되며 일반 전송 가능 포지션을 보호하지 않습니다.

9. 팜 보상 스트림 조작

공격

팜 생성자가 보상 볼트에 자금을 조달하고, 스테이커를 유치한 후, restartRewards를 호출하여 미결제 보상 계산을 이상하게 만들어 수확 가치를 훔칩니다.

노출

악의적인 생성자가 있는 팜. Farm v6은 생성자 권한을 엄격하게 제한합니다. 이 공격은 작동하지 않습니다.

방어

Farm v6의 관리자 명령어(setRewards, restartRewards, addReward)는 비례 배분을 보존합니다 — reward_per_share는 변경 시점에 조정되므로 변경 전 미수 금액이 소급하여 손상되지 않습니다.

Raydium의 입장

OtterSec의 팜 감사는 특히 재시작 보상 시나리오를 테스트했습니다. 익스플로잇이 발견되지 않았습니다.

10. 시뮬레이션 대 실행 차이

공격

공격자가 시뮬레이션에서는 성공하지만 실행 시 되돌려지는 트랜잭션을 구성합니다(또는 그 반대). 시뮬레이션에 의존하는 지갑을 괴롭히는 데 사용됩니다.

노출

시뮬레이션을 기반으로 “X를 받을 것입니다”를 표시하는 지갑.

방어

  • 실제 제출과 동일한 블록해시로 simulateTransaction을 사용합니다.
  • 예상 출력을 정확하지 않고 ”≈“(대략)로 표시합니다.
  • 제출 직전에 다시 시뮬레이션합니다.

Raydium의 입장

CLMM 시뮬레이션은 현재 풀 상태가 주어지면 결정론적입니다. 차이는 시뮬레이션과 실행 사이에 상태가 변경될 때만 발생합니다(정상 사례, 슬리피지 경계를 통해 처리됨).

요약 표

사용자가 할 수 있는 것

  • 기본적으로 타이트한 슬리피지를 사용합니다. 필요할 때만 올립니다.
  • Jito 활성화 지갑 / 스왑 흐름을 사용합니다.
  • LP 전에 민트 확장을 확인합니다.
  • 보류 중인 업그레이드에 대해 Squads 멀티시그를 모니터링합니다.
  • 풀 전체에 다양화합니다. 모든 LP를 하나의 신규 출시 풀에 집중하지 마세요.

통합자가 할 수 있는 것

  • 파생상품 가격 책정을 위해 ObservationState TWAP을 사용합니다.
  • CPI를 통해 조합할 때 계정 제약을 검증합니다.
  • tags 필드로 풀을 필터링합니다(scam, honeypot, 검증되지 않은 전송 훅 건너뛰기).
  • 합리적인 슬리피지 경계를 설정합니다. 사용자 입력에서 0 슬리피지를 수락하지 마세요.
  • simulateTransaction을 주의해서 사용합니다 — 추정값임을 문서화합니다.

포인터

출처: