훅과 안티 스나이프
Uniswap v4 훅은 스왑의 정해진 시점에 PoolManager가 호출하는 컨트랙트입니다.
StockFunHook의 현재 구현 컨트랙트는 여섯 가지 권한을 사용합니다: beforeInitialize,
beforeAddLiquidity, beforeSwap, afterSwap, 그리고 몫을 떼어 갈 수 있게 해 주는 두 가지 델타
권한입니다. 2026-10-02부터 훅의 주소에는 v4 권한 14개가 모두 담겨 있으며, 현재 구현 컨트랙트가
사용하지 않는 콜백은 아무 처리 없이 넘어갑니다.
주소가 권한을 인코딩합니다
v4는 훅을 언제 호출할지 알기 위해 훅 주소의 하위 비트를 읽습니다. 따라서 주소는 고르는 것이 아닙니다: 선언된 권한과 비트가 일치하는 값이 나올 때까지 CREATE2로 채굴합니다.
2026-10-02부터 채굴하는 주소는 프록시의 주소이며, 권한 비트 14개가 모두 설정되어 있습니다. 업그레이드는 그 주소 뒤의 구현 컨트랙트를 주소를 옮기지 않고 교체하므로, 다시 채굴할 필요가 결코 없으며, 이후의 구현 컨트랙트는 어떤 콜백이든 사용할 수 있습니다. 현재 구현 컨트랙트는 사용하지 않는 콜백을 아무 처리 없이 넘깁니다: 이 콜백들은 자신의 셀렉터를 반환하고, 델타가 필요한 곳에서는 0을 반환합니다. 프록시는 초기화될 때 자신의 주소에 모든 비트가 담겨 있는지 확인합니다.
실질적인 결과: 채굴한 주소는 프록시의 바이트코드와, 구현 컨트랙트의 주소를 담은 프록시의
생성자 인자에 따라 달라집니다. 새로 배포할 때는 다시 채굴해야 합니다. foundry.toml이
bytecode_hash = "none"을 설정하는 이유도 이것입니다 — 이 설정이 없으면 채굴한 주소가 배포된
컨트랙트와 더 이상 일치하지 않습니다.
2026-10-01에 추가된 beforeAddLiquidity 권한은 비트 자체를 바꾸었습니다: 주소는 다시
채굴되었고, 그 날짜 이전에 이루어진 배포는 모두 구버전입니다. 2026-10-02의 프록시가 비트를 다시
바꾸었습니다: 그 날짜 이전에 이루어진 배포도 모두 구버전입니다.
수수료 징수
매수 시에는 스왑이 일어나기 전에 beforeSwap에서 들어오는 ETH로부터 수수료를 뗍니다. 훅은
PoolManager에서 자신의 몫을 인출하고, 이를 매수자에게 부과하는 델타를 반환합니다. StockFun
라우터와 유동성 락은 스왑 전에 매수자의 ETH를 PoolManager에 지불하므로, 이들을 거치는 매수는
PoolManager가 이미 보유한 ETH를 결코 끌어다 쓰지 않습니다.
그 밖의 모든 v4 라우터도 같은 택스 요율을 냅니다. 하지만 v4-periphery의 V4Router가 기본
인코딩으로 그러듯이 스왑 후에 매수자의 ETH를 정산하는 라우터는, PoolManager가 이미 보유한
ETH에서 택스가 떼어지며, 택스가 그보다 크면 매수가 실패합니다. 예를 들어 테스트넷의
PoolManager처럼 ETH를 거의 보유하지 않은 PoolManager에서 이런 일이 일어날 수 있습니다. 연동
개발자는 공식 라우터처럼 스왑 전에 매수자의 ETH를 지불해야 합니다. 매도는 영향을 받지 않습니다.
2026-10-01 보안 파이프라인이 이 제약을 문서화했습니다. 대신 택스를 PoolManager 클레임으로
받으면 모든 라우터에서 이 제약이 사라지지만, 트레저리의 2 %가 거래 중에 지급되는 대신 적립된 뒤
나중에 지급되게 됩니다: 2026-10-05에 오너는 택스를 지금 받는 방식 그대로 유지하기로 결정했습니다.
매도 시에는 금액이 확정된 뒤 afterSwap에서 나가는 ETH로부터 뗍니다.
두 경우 모두 훅은 즉시 배분합니다: 트레저리, 크리에이터, 팀, 바이백. 거래 중에 나가는 것은 트레저리 항목뿐이며, 마켓의 볼트로 전송됩니다. 나머지 세 항목은 훅에 적립되며, 거래와 별도로 지급됩니다.
2026-10-05부터는 트레저리 항목을 거부하는 볼트가 더 이상 거래를 실패하게 만들지 않습니다: 훅은 그
금액을 그 볼트에 지급해야 할 것으로 보관하며(treasuryOwed, 이벤트 TreasuryOwed), 모든 매수와
매도는 계속됩니다. 누구나 payTreasury로 그 볼트에 채무를 지급할 수 있으며, 볼트가 계속 거부하는
동안 이 호출은 실패하고 채무는 유지됩니다; 키퍼는 매 주기 이를 시도합니다. 안티 스나이프 초과분도
같은 규칙을 따릅니다. 그 전까지 전송은 실패를 그대로 드러냈습니다: 예를 들어 잘못된 업그레이드
이후 받을 수 없게 된 볼트는, 매도를 포함해 그 마켓의 모든 거래를 실패하게 만들었습니다.
크리에이터 몫은 청구 방식입니다: 훅에 마켓별로 쌓이며, 크리에이터는 원할 때 마켓마다 트랜잭션
하나로 청구합니다(claimCreatorFees). 2026-10-05부터는 ETH를 직접 받을 수 없는 크리에이터, 즉
ETH를 받을 방법이 없는 컨트랙트는 다른 주소로 청구합니다(claimCreatorFeesTo); 이는 크리에이터만
할 수 있습니다.
2026-10-01부터 팀과 바이백 몫도 같은 방식으로, 각각 하나의 잔액으로 적립되며,
claimTeamFees()와 claimBuybackFees()로 지급됩니다. 누구나 이 함수들을 호출할 수 있으며, 지급
대상은 청구 시점에 팩토리가 지정한 팀 지갑과 바이백 지갑뿐입니다. BuybackBurner는 매 소각을
시작할 때 자신의 잔액을 직접 가져옵니다. ETH를 거부하는 지갑은 자신의 지급만 늦출 뿐입니다: 그
청구는 실패하고 잔액은 훅에 남습니다.
그 전까지 이 두 몫은 거래 중에 전송되었고, 전송이 실패하면 에스크로 폴백이 적용되었습니다.
2026-09-29 보안 감사는 전송을 받아들인 뒤 PoolManager를 호출하는 지갑이 모든 풀의 거래를 멈출
수 있음을 보여 주었습니다. 에스크로는 사라졌습니다.
훅이 거래 사이에 보유하는 것은 누군가에게 지급해야 할 것입니다: 크리에이터들의 잔액, 팀과 바이백의
잔액, 그리고 볼트들에 대한 채무. PoolManager가 아닌 누군가가 훅에 보낸 ETH는 잘못 들어온
자금(strayEth)으로 따로 계산됩니다. 2026-10-05부터 StockFun의 오너는 잘못 들어온 자금을 꺼낼 수
있습니다(rescue): 많아야 그 금액만큼의 ETH, 그리고 훅은 토큰을 결코 보유하지 않으므로 어떤
토큰이든 꺼낼 수 있습니다. 훅이 지급해야 할 것은 어느 것도 그 방법으로 나갈 수 없습니다. 호출 없이
강제로 들어온 ETH는 계산되지 않으며, 업그레이드를 기다립니다.
락만 유동성을 추가합니다
2026-10-01부터 beforeAddLiquidity는 StockFun 풀에 대한 모든 유동성 추가를 거부하며, 예외는
유동성 락의 추가뿐입니다.
현재 가격 바로 옆에 놓인 포지션은 지정가 주문처럼 작동합니다: 트레이더의 스왑이 그 포지션을 통과하면서 이를 전환하고, 택스는 트레이더가 냅니다. 반면 포지션 소유자는 5 %도, 첫 10개 블록에서는 안티 스나이프 택스도 전혀 내지 않고 포지션을 추가하고 제거합니다. 2026-09-29 보안 감사가 이를 재현했습니다. 풀은 기본적으로 LP 수수료를 부과하지 않으므로, 정당한 용도 중 서드파티 포지션이 필요한 것은 없습니다.
락 자체의 수수료 수취는 유동성 델타가 0이며, 현재 구현 컨트랙트가 아무 처리 없이 넘기는 유동성 제거 경로를 거칩니다: 영향을 받지 않습니다. 같은 경로로 포지션을 빼내는 종료 모드의 회수도 마찬가지입니다: 두 포지션 런칭을 참고하십시오.
감소형 안티 스나이프
v4 풀은 초기화되는 순간 바로 활성화됩니다. 보호 장치가 없으면 생성 직후의 첫 블록들은 봇이 가져갈 것입니다. 2026-09-28부터 이 블록들에는 더 무거운 택스가 붙으며, 이 택스는 매수와 매도 모두에서 블록마다 낮아집니다. 기본 설정에서는 다음과 같습니다:
| 런칭 이후 블록 | 택스 |
|---|---|
| 1 | 80 % |
| 2~10 | 72, 64, 56, 48, 40, 32, 24, 16, 8 % |
| 11+ | 정상, 5 % |
오프닝 블록에서 매수하는 봇은 한 번에 80 %를 냅니다: 스나이핑은 손해입니다.
세 가지는 설명이 필요합니다.
크리에이터의 런칭 매수에는 신원 확인이 필요 없습니다. LiquidityLock이 수행하는 유일한
스왑은 자체 생성 콜백 안에서 이루어지는 크리에이터의 선택적 매수입니다. 따라서 "호출자가
락이다"는 "생성 트랜잭션 안에 있다"를 의미하며, 이 매수는 정상 5 %를 냅니다.
초과분에는 별도의 목적지가 있습니다. 정상 5 %는 평소의 배분을 따릅니다. 그 위의 부분은
마켓의 트레저리로 가며, 따라서 다음 에어드랍 때 홀더에게 갑니다. $STOCKFUN 마켓에서는 훅에
있는 팀의 잔액으로 가며, claimTeamFees()로 지급됩니다.
화이트리스트에는 신원 확인이 필요하며, 이것이 미묘한 부분입니다. 훅은 매수자가 아니라
라우터를 봅니다. 그래서 StockFun 라우터는 호출자의 주소를 훅 데이터에 담아 전달하고, 훅은
호출자가 공식 라우터인 경우에만 그 필드를 신뢰합니다. 공식 라우터는 팩토리에 기록된
라우터이며, 오너가 언제든 변경할 수 있습니다(2026-09-28에 수용됨). 이 프로토콜 어디에서도
tx.origin은 사용하지 않습니다.
문서화된 제약: 첫 10개 블록 동안 서드파티 애그리게이터를 통해 라우팅하는 화이트리스트 주소는 면제되지 않습니다.
화이트리스트
마켓의 크리에이터가 설정합니다. 목록에 있는 주소는 안티 스나이프 블록 동안 정상 5 %를 냅니다. 2026-09-28까지 이 목록은 오너가 관리하고 모든 마켓이 공유했으며, 이는 바로 내부자 특혜가 되지 않게 하기 위해서였습니다. 이제 크리에이터는 자신의 지갑을 면제할 수 있습니다. 2026-09-28에 검증되었습니다: 목록은 생성 트랜잭션에서 고정되고, 공개되며, 변경할 수 없고, 기본값으로 최대 20개 주소로 제한됩니다.
$STOCKFUN 마켓
감소형 택스도 적용됩니다. $STOCKFUN에는 외부 크리에이터가 없습니다: 초과분은 팀의 청구 가능한
수수료로 가며, 그 화이트리스트는 런칭 시 오너가 설정합니다.
설정
2026-10-05부터 이 페이지의 모든 수치는 훅에서 setTaxSettings로 바꾸는 StockFun 오너의
설정입니다: 택스, 기본값 5 %; 그 항목, 런칭된 마켓에서 2 / 2 / 0.5, $STOCKFUN에서 2 / 2.5이며
나머지는 바이백; 안티 스나이프의 오프닝 택스, 80 %; 블록당 감소폭, 8포인트; 생성 블록을 포함한
지속 블록 수, 10; 그리고 화이트리스트 최대 크기, 20개 주소.
변경은 아직 안티 스나이프 블록 안에 있는 풀을 포함해, 모든 풀에 다음 스왑부터 적용됩니다: 감소는 풀의 런칭 블록부터, 현재 유효한 설정으로 계산됩니다. 설정이 어떻든 안티 스나이프는 결코 택스보다 적게 부과하지 않습니다. 이 setter는 100 %를 넘는 요율과 항목들의 합이 택스를 넘는 구성을 거부합니다. 화이트리스트 상한은 마켓이 생성될 때 검사됩니다: 이미 설정된 목록은 그대로 유지됩니다.
업그레이드
2026-10-02부터 StockFun의 오너는 훅을 업그레이드할 수 있으며, 업그레이드는 즉시 효력을 가집니다.
이 페이지에서 설명한 택스, 그 배분, 안티 스나이프, 화이트리스트는 현재 구현 컨트랙트의 것입니다.
업그레이드는 훅의 주소를 유지하며, 같은 PoolManager를 유지해야 합니다. 훅이 읽고, 누가 훅을
업그레이드할 수 있는지를 결정하는 팩토리는 구현 컨트랙트에 고정되어 있습니다.