테스트와 검증

수준

수준 잡아내는 것
Foundry 단위 테스트 컨트랙트별 예상 동작
퍼징 아무도 상상하지 못한 입력
불변식 어떤 순서의 호출 뒤에도 성립해야 하는 속성
포크 테스트 실제 상태에서 실제 컨트랙트를 상대로 한 동작
정형 검증 샘플링이 아니라 모든 경로에서 증명된 속성
정적 분석 알려진 위험 패턴
카피 감사 금지 어휘와 시각적 금지 사항

경제적 불변식

항상 성립해야 하는 등식은 다음과 같습니다:

  1. 네 몫의 합은 정확히 부과된 택스, 기본값 500 베이시스 포인트이며, 손실되는 wei가 없습니다
  2. 누구도 볼트의 자산을 자신이 고른 주소로 인출할 수 없습니다. 가능한 감소는 전환, 홀더 대상 에어드랍, 그리고 오너의 긴급 이동뿐입니다
  3. 바스켓 비중의 합은 100 %이며 절대 바뀌지 않습니다
  4. 두 포지션은 생성 시 예치되며, 공지 후 30일이 지나 종료 모드로만 제거할 수 있습니다
  5. 유동성은 잠겨 있으며, 유일한 출구는 오너가 30일 전에 공지하는 종료 모드입니다
  6. $STOCKFUN 바이백은 매수한 토큰을 소각 주소 외의 어디로도 보낼 수 없습니다
  7. 크리에이터 공급량은 모든 마켓에서 0입니다
  8. 볼트는 자신의 바스켓 자산만 매수할 수 있습니다
  9. 볼트가 취득하는 것은 ETH, USDC, USDG, 자신의 바스켓 주식, 그리고 선택 사항인 Ondo 레일에서의 USDon 환불뿐입니다
  10. 에어드랍 주기는 주식별로 자신이 보유한 것보다 많이 지급하지 않으며, 어떤 홀더도 비례 배분 몫보다 많이 받지 않습니다. 에어드랍 컨트랙트가 주기와 따로 보관된 주식 전체에 걸쳐 지급해야 할 양은 결코 그 잔액을 넘지 않습니다 — 2026-09-27부터 요구되며, 2026-10-04부터 상태 기반 불변식으로 테스트됩니다. 2026-10-05부터는 긴급 상황을 거치더라도, 그 장부를 뒷받침하는 것은 항상 컨트랙트가 실제로 보유한 토큰으로 이루어지며 결코 아직 적립되지 않은 토큰으로 이루어지지 않습니다. 이는 두 번째 상태 기반 불변식으로 테스트됩니다
  11. StockFun 풀에 유동성을 추가하는 것은 유동성 락뿐입니다
  12. 거래가 지급하는 대상은 마켓의 볼트뿐이며, 나머지 몫은 청구될 때까지 훅에서 기다립니다. 2026-10-05부터 자신의 몫을 거부하는 볼트에는 그 몫이 훅에 지급해야 할 것으로 남으며, 거래는 계속됩니다
  13. 각 바스켓 주식은 자신에게 배정된 현금만 지출합니다

이 불변식들은 현재 구현 컨트랙트를 대상으로 검사됩니다. 업그레이드는 검사 대상인 코드를 교체합니다.

크기 제약

EIP-170 제한은 런타임 코드 24,576바이트입니다. 이는 수동 점검이 아니라, 테스트 스위트를 실패시키는 테스트로 강제됩니다.

여기서는 실제 제약입니다: 팩토리는 분할해야 했습니다. 2026-10-02까지는 TreasuryVault의 생성 코드를 내장하던 볼트 배포자가 상한에 가장 가까운 컨트랙트였고, 볼트에 추가되는 모든 코드 줄은 그 여유 공간에서 차감되었습니다. 2026-09-28에 크리에이터 바이백을 제거하면서 볼트 배포자는 23,055바이트에서 14,965바이트로 줄었습니다. 현금을 주식별로 배정하는 2026-10-01 수정으로 볼트 배포자는 15,954바이트가 되었습니다. 2026-10-02부터 볼트 배포자는 프록시를 배포하며, 제한 아래에 들어가야 하는 것은 각 모듈의 구현 컨트랙트입니다.

2026-10-05에는 에어드랍 컨트랙트가 상한에 가장 가까운 컨트랙트가 되었습니다. 네 번째 감사 루프에서 바뀐 청구 때문에 제한을 넘었고, 일부 뷰 함수를 private으로 바꾸고 한 주기에 차감되는 긴급 이동을 없애자 24,432바이트, 즉 상한보다 144바이트 아래로 돌아왔습니다.

2026-09-29 감사 이후

2026-09-29 보안 감사 이후 수정된 각 발견 사항에는 감사 대상 코드에서는 실패하는 회귀 테스트가 있습니다. 대부분은 감사의 개념 증명을 뒤집어, 수정된 동작을 검사하게 만든 것입니다.

2026-10-01 보안 파이프라인

독립적인 감사자 두 명이 진행한 두 번째 감사 라운드, 그리고 수정된 코드에 대해 다시 수행한 모든 검사. 이 파이프라인이 컨트랙트와 키퍼에 적용한 각 수정에는 회귀 테스트가 있습니다. 그 수정 이후, 2026-10-01 기준으로:

  • Foundry. 포크 스위트를 제외한 테스트 421개가 통과하며, 심층 프로필도 통과합니다: 퍼즈 실행 20,000회, 그리고 깊이 128로 512회 실행한 불변식. 포크 스위트는 실행되지 않았습니다: 사용할 수 있는 RPC URL이 없었습니다.
  • 뮤테이션 테스트. slither-mutate를 다섯 개 컨트랙트에 실행했습니다: 수수료 분배 컨트랙트, 보유량 기록, 트레저리 볼트, Robinhood Chain의 주식 라우터, 리모트 허브. 코드에 가한 작고 의도적인 변경인 뮤턴트 1,301개를 만들었고, 테스트는 그중 1,118개를 잡아냈습니다. 살아남은 183개 중 107개는 원래 코드와 동등하여, 어떤 입력으로도 구별할 수 없습니다. 나머지 76개는 테스트가 하지 않던 검사를 드러냈고, 이제 테스트가 그 검사를 모두 다룹니다. 버그를 드러낸 뮤턴트는 없었습니다.
  • 정형 검증. 모든 Certora 작업을 수정된 코드로 프루버에서 다시 실행했습니다(certora-cli 8.8.1, 기본 건전성 검사). 위반된 규칙은 없었습니다. LiquidityLock 규칙 7개는 한 메서드, lockProtocolLiquidity에 대해 아직 결론이 나지 않았습니다. 시간 제한을 넘겼고, 더 긴 제한으로 다시 실행했지만 판정 없이 끝났습니다. 다른 모든 메서드에서는 성립합니다. 이 메서드는 오너만 호출할 수 있고 $STOCKFUN을 위해 한 번만 실행되며, Foundry 테스트가 그 접근 제어, 일회성 사용, 락의 형태를 다룹니다. 검증되지 않은 나머지 결과는 알려진 공허성 사례입니다: 모든 메서드에 대해 실행되는 규칙이, 검증 설정에서 결코 성공할 수 없는 메서드에 적용된 경우입니다. TreasuryVault의 첫 실행에서 나온 실패는 명세의 아티팩트 두 가지에서 비롯되었고, 어느 것도 버그가 아니었습니다. 명세는 수정되었습니다.
  • 퍼징과 기호 실행. Medusa는 보유량 기록의 속성 다섯 개를 10분 동안 실행했으며, 실패는 없었습니다. Medusa의 첫 실행은 시계가 시간 0에서 시작하는 체인, 즉 테스트 체인에서 기록된 공급량의 첫 시간별 기록점이 빠져 있다는 것을 발견했습니다. 실제 체인은 영향을 받지 않았으며, 이제 토큰은 배포 시점에 그 기록점을 남깁니다. Halmos는 범위 안의 모든 입력에 대해 기호 검사 세 개를 통과합니다: 수수료 배분은 정확하고, 기록된 공급량은 임의의 두 전송을 따라가며, 다음 정각에서의 그 적분값은 올바릅니다. Mythril은 배포 밖에서는 프로토콜 컨트랙트를 탐색할 수 없어 커버리지가 약 14 %입니다. 자체 완결적인 보유량 기록에서는 15분 동안 31 %에 도달했고, 아무것도 보고하지 않았습니다.
  • 정적 분석. Slither, Aderyn, Solhint는 새로운 것을 찾지 못했습니다.
  • 새 로컬 체인에서. 전체 배포, 실제 키퍼 주기, 사용 사례 10개가 통과합니다. 런북의 순서대로 프로덕션 스크립트를 실행하는 배포 리허설은 스크립트 하나를 수정한 뒤 통과합니다: 모의 레일의 스크립트가 시드 자금을 배포 키가 아니라 forge의 기본 발신자에게 민트하고 있었습니다.
Certora 작업 검증됨
BuybackBurner 12
Fees 5
LiquidityLock 13
StockFunFactory 23
StockFunHook 36
StockFunProtocolToken 9
StockFunSwapRouter 8
StockFunToken 11
TreasuryOracle 8
TreasuryVault 32
UniswapV4StockRouter 12

그 실행은 TeamVesting의 규칙 11개도 검증했으며, 이 컨트랙트는 2026-10-05에 그 명세와 함께 삭제되었습니다. 프루버는 Robinhood Chain 쪽을 다루지 않습니다: 리모트 허브, 미러 볼트, 주식 라우터.

2026-10-02의 업그레이드 가능 구조 재설계

모듈을 업그레이드 가능하게 만든 재설계는 위의 모든 검사 이후에 이루어졌습니다. 2026-10-02 기준으로, 새 코드에서:

  • Foundry. 포크 스위트를 제외한 테스트 458개가 통과합니다. 새 스위트가 Ethereum과 Robinhood Chain에서의 업그레이드 — 누가 업그레이드할 수 있는지, 새 구현 컨트랙트가 유지해야 하는 권한자와 PoolManager, 볼트를 하나씩 업그레이드하는 것 — 그리고 종료 모드를 다룹니다. 보유량 기록의 스위트는 기록과 함께 보유량 기록기로 옮겨졌습니다.
  • 스토리지 레이아웃. 업그레이드 가능한 모든 모듈의 스토리지 레이아웃이 contracts/storage-layouts/에 기록되어 있으며, contracts/script/check-storage-layouts.sh는 끝에 추가하는 것 외의 변경이 있으면 실패합니다. 이 스크립트는 매 업그레이드 전에 실행하도록 만들어졌습니다. 2026-10-05부터는 모든 구조체를 모든 깊이까지 검사합니다: 배포를 참고하십시오.
  • 새 로컬 체인에서. LocalRun 스크립트가 통과합니다.
  • 정형 검증. Certora 명세는 새 코드에 맞춰 갱신되는 중입니다. 프루버에서의 마지막 전체 실행인 2026-10-01 실행은 재설계 이전의 것입니다: 위의 표는 그 실행을 설명합니다.

위에서 설명한 심층 프로필, 뮤테이션 테스트, 퍼징과 기호 실행, 정적 분석은 2026-10-01에 재설계 이전의 코드로 실행되었습니다.

2026-10-04의 에어드랍

에어드랍 컨트랙트와 두 sendToAirdrop 경로는 2026-10-04에 코딩되었으며, 배포되지 않았습니다. 그날, 새 코드에서:

  • Foundry. 포크 스위트를 제외한 테스트 514개가 통과하며, 에어드랍 이전에는 459개였습니다. 새 스위트 세 개: 테스트 38개인 AirdropDistributorTest; LayerZero를 목으로 대체한, 15개인 AirdropCrossChainTest; 상태 기반 불변식을 중심으로 한, 2개인 AirdropInvariantsTest. 이 불변식은 무작위 전송, 청구, 귀속, 주기 개시, 그리고 제외 목록과 주기 시각의 변경 아래에서 두 마켓에 걸친 보존과 지급 능력을 검사하며, 64회 호출로 128회 실행합니다. 몫 계산의 퍼즈는 512회 실행됩니다.
  • 가스. 주식 두세 개로 이루어진 주기를 청구하는 데에는 테스트 측정 기준으로 대략 120,000~210,000 가스가 들며, 테스트는 웜 스토리지 상태에서 실행되므로 실제 트랜잭션은 이보다 조금 더 듭니다. Robinhood Chain에서 오는 전달은 콜드 상태로 측정했을 때 Ethereum에서 약 85,000~1,016,000 가스가 듭니다: 배포를 참고하십시오.
  • 로컬 체인에서. Anvil에서 LocalRun, 이어서 LocalAirdrop: 홀더 둘, 13:00 UTC 전에 따로 보관된 뒤 그 이후에 귀속되고 청구되는 주식. 각 청구는 정확히 그 몫의 내림값이며, 주식마다 1 wei의 잔량이 남습니다.
  • 정형 검증. 변경된 컨트랙트에 관련된 Certora 명세 다섯 개가 타입 검사를 통과합니다. 프루버는 실행되지 않았으며, 에어드랍 컨트랙트를 다루는 명세는 없습니다.
  • 오프체인. 키퍼의 테스트 93개, shared 패키지의 43개, 백엔드의 9개, 워커의 58개가 통과합니다.
  • 검토. Codex(gpt-6-astra)가 설계를, 이어서 코드를 검토했습니다: 따로 보관된 주식의 시점에 관한 Medium 하나와 로컬 스크립트에 관한 Low 하나가 나왔고, 둘 다 수정되었습니다. 수정 사항에 대한 재검토에서는 제외 목록 변경의 시점에 관한 Medium 하나와 로컬 스크립트의 실행 순서에 관한 Low 하나가 나왔으며, 둘 다 수정되었습니다: 측정 구간은 그 구간이 닫힌 시점에 유효했던 제외 목록을 기준으로 측정되고, 스크립트는 따로 보관된 주식을 먼저 귀속시킵니다. 이 두 수정에 대한 최종 점검에서는 명시된 보장 아래에서 버그가 발견되지 않았습니다: 따로 보관된 주식은 누가 호출하든 언제 호출하든, 그 사이에 주기 시각이 바뀌지 않고 토큰의 보유량 기록기가 교체되지 않는 한, 적격 보유량이 있는 첫 측정 구간으로 갑니다. 보고서는 projet/docs/audit-2026-10-04/에 있습니다.

2026-10-05의 변경

팀 할당 없음, 즉시 작동하는 긴급 모드, 오너 설정이 된 모든 수치: 2026-10-05에 코딩되었으며, 배포되지 않았습니다. 그날, 새 코드에서:

  • Foundry. 스위트가 통과하며, 테스트는 520개입니다. 새 스위트 두 개, Settings와 SettingsCrossChain이 각 설정을 다룹니다: 누가 설정할 수 있는지, 설정이 거부하는 값, 그리고 변경 뒤의 다음 작업 — 그중에는 택스와 안티 스나이프, 런칭 형태, 각 풀이 자신의 키를 유지하는 것, 잠긴 포지션이 거두는 것의 몫이 있습니다. 퍼징과 기호 실행으로 검사한 수수료 속성은 허용되는 모든 설정에서 성립합니다. 긴급 모드 테스트는 즉시 이루어지는 이동에 맞춰졌으며, 베스팅 컨트랙트의 테스트는 컨트랙트와 함께 사라졌습니다.
  • 가스. 택스 설정을 읽는 데 스왑 1회당 약 1,200 가스가 더 들며, 웜 상태 기준입니다.
  • 정형 검증. 수수료, 훅, 락, 팩토리, 토큰, 오라클의 명세가 설정에 맞춰 갱신되었으며 로컬에서 타입 검사를 통과합니다. 프루버는 실행되지 않았습니다.

2026-10-05의 감사 루프

같은 날, 배포되지 않은 코드를 다섯 차례의 검토 루프가 살펴보았습니다; 네 번째 루프부터는 하나의 실패가 결코 나머지를 막지 않는다는 창업자의 규칙을 어기는 것이 결함으로 계산됩니다(아키텍처 참고). 각 수정에는 회귀 테스트가 있습니다. 각 루프의 코드에서:

  • Foundry. 네 번째 루프 뒤에 테스트 602개, 다섯 번째 루프 뒤에 613개가 통과합니다; 그날이 끝날 때는 614개가 통과하며, RPC가 없어 포크 스위트 세 개는 건너뜁니다. 모든 컨트랙트가 EIP-170 제한 아래에 들어가며, 스토리지 레이아웃은 수정이 상태를 끝에 추가한 곳에서만 커졌습니다.
  • 불변식. 에어드랍의 불변식은 세 번째 루프 뒤에 심층 프로필에서 깊이 128로 512회 실행을 통과합니다. 두 번째 상태 기반 불변식은 에어드랍을 긴급 상황 속으로 몰아넣습니다: 마지막 단계가 늦게 실행되는 전달, 잘못 들어온 토큰, 밖으로의 이동, 반환, 상각, 일시 중지, 그리고 다섯 번째 루프 이후로는 발행사가 동결한 주식과 claimMany.
  • 정형 검증. 루프들은 훅(볼트에 대한 채무, 잘못 들어온 자금), 락(보관하는 몫, 레스큐), 버너와 토큰(레스큐)에 대한 규칙, 그리고 라우터, 오라클, 팩토리에서 레스큐는 프로토콜 오너만 한다는 규칙을 추가했습니다: 네 번째 루프 뒤에 명세 11개에 규칙과 불변식 213개, 다섯 번째 루프 뒤에는 토큰의 레스큐에 대한 규칙이 하나 더 있습니다. 손댄 모든 명세는 로컬에서 타입 검사를 통과합니다; 프루버에는 아무것도 보내지 않았으며, 프루버의 마지막 실행은 여전히 2026-10-01의 실행입니다.
  • 오프체인. 키퍼의 에어드랍 단계, 디앱의 청구 화면, 네 번째 루프의 오프체인 회차 이후: 키퍼의 테스트 191개, shared 패키지의 51개, 백엔드의 9개, 워커의 90개가 통과하며, 앱은 타입 검사와 린트를 통과합니다.
  • 로컬 체인에서. Anvil에서 LocalRun, 이어서 키퍼, 워커의 엔진, 앱의 청구 준비: 배포가 이루어진 측정 구간에서 키퍼는 아무것도 보내지 않았고, 다음 측정 구간에서 주기 두 개를 열어 두 볼트의 주식을 보냈으며, claimMany 한 번이 두 주기에 걸친 주식 다섯 개를 약 421,000 가스로 지급했고, 그 뒤 claimable은 0을 반환했습니다.

다섯 번째 루프의 오프체인 회차, 2026-10-06

다섯 번째 루프의 키퍼, 워커, 앱, 스크립트 검토는 심각도가 중간인 문제 3개와 낮은 문제 16개를 찾았으며, 각각 회귀 테스트와 함께 수정되었고, 배포되지 않았습니다. 2026-10-06에 실행:

  • Foundry. 그날의 코드에서 테스트 619개가 통과하며, RPC가 없어 포크 스위트 세 개는 건너뜁니다: Lens의 새 필드, 주식 다섯 개라는 바스켓 상한, 이어 간 $STOCKFUN 런칭에는 각각 테스트가 있습니다. 스토리지 레이아웃은 바뀌지 않았습니다.
  • 오프체인. 키퍼의 테스트 210개, shared 패키지의 51개, 백엔드의 9개, 워커의 111개가 통과합니다; 키퍼의 각 수정의 테스트는 그 수정을 빼면 실패합니다. 앱은 타입 검사와 린트를 통과합니다; 앱에는 테스트 러너가 없으며, 그 청구 준비는 워커의 엔진으로 옮겨져 워커의 테스트가 이를 다룹니다.
  • 로컬 체인에서. 프라이빗 Anvil에서의 리허설은 여섯 가지 시나리오를 통과했습니다: 드라이 런이 배포 파일을 쓰지 않는 LocalRun; 각 볼트의 ETH를 측정 구간마다 한 번 전환한 뒤 에어드랍을 한 번 귀속하고, 열고, 보낸, 두 측정 구간에 걸친 키퍼, 그리고 wei 단위까지 정확한 모든 청구; 복구된 뒤 훅과 락에 있던 채무가 지급된, ETH를 거부하는 볼트; 아무것도 두 번 보내지지 않고 대체된 트랜잭션과 버려진 트랜잭션이 처리된, 상태 파일과 그 잠금 파일을 둔 채 작업 도중의 강제 종료; 레스큐와 LP 수수료의 일일 수취. 브리지 레일, 앱, 워커는 포함되지 않았습니다.

리허설 이후, 2026-10-06

리허설의 발견 사항은 같은 날 수정되었으며, 배포되지 않았습니다. 키퍼는 마켓의 볼트에 새로 보낼 것이 없을 때에도 에어드랍 컨트랙트가 그 마켓을 위해 따로 보관하는 주식을 귀속하며, 임계값보다 적은 것으로 확인된 볼트를 자신의 시계가 아니라 확인 대상이 된 측정 구간으로 기록합니다; 따로 보관된 전송은 따로 세며, 볼트의 마지막 매수 레그가 결코 쓸 수 없는 몇 단위의 USDC는 건드리지 않고, 로그에 풀 ID를 표시합니다. 여섯 번째 검토 루프의 첫 영역은 컨트랙트에서 아무것도 찾지 못했고, 로컬 스크립트에서 심각도가 낮은 문제 1개를 찾아 수정했습니다: 로컬 배포 파일은 키퍼, 앱, 워커가 사용하기 전에 체인과 대조됩니다. 그 코드에서:

  • Foundry. 테스트 620개가 통과하며, RPC가 없어 포크 스위트 세 개는 건너뜁니다: 하나가 늘었는데, 이어 간 $STOCKFUN 런칭이 다른 바스켓의 주식을 보유한 볼트를 거부한 테스트입니다
  • 오프체인. 키퍼의 테스트 218개, shared 패키지의 51개, 백엔드의 9개, 워커의 111개가 통과합니다; 키퍼의 각 수정의 테스트는 그 수정 이전의 키퍼에서 실패합니다. 로컬 배포 파일의 확인에는 자동화된 테스트가 없습니다: 프라이빗 Anvil에서 직접 실행되었으며, 그곳에서 새 배포를 통과시키고, 두 컨트랙트의 역할이 뒤바뀐 파일을 거부했습니다

여섯 번째와 일곱 번째 감사 루프 이후, 2026-10-06

여섯 번째 루프와 일곱 번째 루프의 키퍼와 워커 수정은 같은 날 이루어졌으며, 배포되지 않았습니다; 둘 다 어떤 컨트랙트의 코드도 바꾸지 않았고, 컨트랙트의 주석 하나가 고쳐졌을 뿐입니다. 2026-10-06에 커밋 415dcae에서 실행(다른 컨트랙트 작업이 진행 중이었으므로, 컨트랙트는 그 커밋의 깨끗한 사본에서):

  • Foundry. 테스트 620개가 통과합니다: 포크 파일 밖의 615개와, 공개 RPC를 상대로 한 Robinhood Chain 포크 스위트의 다섯 개; Ethereum 포크 스위트 세 개는 RPC가 없어 건너뜁니다. 리허설 이후와 같은 수이며, 어떤 컨트랙트 테스트도 바뀌지 않았습니다
  • 오프체인. 키퍼의 테스트 277개, shared 패키지의 51개, 백엔드의 9개, 워커의 137개가 파일 11개에서 통과합니다. 일곱 번째 루프의 각 개념 증명은 회귀 테스트이며, 각 수정의 새 테스트는 그 수정 이전의 코드에서 실패합니다. 예외는 이전 코드가 이미 하던 일을 고정하는 몇 개의 테스트(키퍼가 가치를 매길 수 없는 전송은 예전처럼 나감; 이전 상태 파일을 불러옴)와, 함수가 새로 생긴 차트의 테스트입니다. 여섯 번째 루프 이전의 키퍼가 쓴 상태 파일은 테스트 픽스처로 보관되며, 새 키퍼에서 불러와집니다
  • 앱. 타입 검사, 린트, 빌드를 통과하며, 미리 렌더링된 데모 차트는 플롯 영역 전체에 펼쳐집니다; 테스트 러너는 없으며, 브라우저 테스트는 실행되지 않았습니다. 차트의 배치는 워커 엔진의 코드를 실행하며, 이는 워커의 테스트가 다룹니다

가격 피드 보호 장치와 여덟 번째 감사 루프, 2026-10-06

Robinhood Chain을 위한 오라클의 두 가격 피드 보호 장치는 2026-10-06에 작성되었고, 여덟 번째 루프의 키퍼, 워커, 앱 수정도 같은 날 이루어졌으며, 배포되지 않았습니다. 컨트랙트는 루프가 아니라 보호 장치와 함께 바뀌었습니다. 2026-10-06에 커밋 6586d40에서 실행(다른 컨트랙트 작업이 진행 중이었으므로, 컨트랙트는 그 커밋의 깨끗한 사본에서):

  • Foundry. 테스트 640개가 통과합니다: 스위트 82개에 걸친 포크 파일 밖의 633개와, 공개 RPC를 상대로 한 Robinhood Chain 포크 파일의 일곱 개; Ethereum 포크 스위트 세 개는 RPC가 없어 건너뜁니다. 보호 장치는 포크 파일 밖에 테스트 18개를 더했습니다: 목 피드와 목 토큰을 쓰는 오라클 단독 테스트 16개(각 가드를 끈 뒤 켬, 피드나 토큰이 응답하지 못하는 모든 경우, 일시 중지로 치는 모든 응답, 가스가 모자란 읽기, 오너의 설정, 배포 스크립트의 거부)와 크로스체인 레일 테스트 2개(일시 중지된 주식의 레그는 단독으로 실패하고 일시 중지가 풀리면 매수함; 시퀀서 중단은 유예 기간이 끝날 때까지 모든 레그를 멈춤)입니다. 새 포크 테스트 두 개는 실제 주식 토큰을 읽습니다: 스무 개의 토큰은 각각 오라클이 읽는 대로 일시 중지 신호에 응답하며, 읽기 한 번에 든 가스는 최대 13,288이었고, 실제 코드가 플래그를 보관하는 곳에서 일시 중지된 토큰은 자신의 가격만 보류합니다
  • 스토리지와 명세. 업그레이드 가능한 모듈 17개의 스토리지 레이아웃은 오라클의 끝부분 추가를 빼고는 바뀌지 않았습니다; 오라클의 정형 명세(규칙과 불변식 25개)는 타입 검사를 통과하며, 프루버는 실행되지 않았습니다
  • 오프체인. 키퍼의 테스트 306개, shared 패키지의 51개, 백엔드의 9개, 워커의 155개가 파일 12개에서 통과합니다. 여덟 번째 루프의 각 개념 증명은 회귀 테스트이며, 각 수정의 새 테스트는, 새로 생긴 함수의 테스트를 빼고는, 그 수정 이전의 코드에서 실패합니다
  • 앱. 타입 검사, 린트, 엔진 검사, 빌드를 통과합니다; 테스트 러너는 없으며, 브라우저 테스트는 실행되지 않았습니다. 런칭 양식의 바스켓 확인과 거래 패널의 택스는 워커 엔진의 코드를 실행하며, 이는 워커의 테스트가 다룹니다

아홉 번째 감사 루프, Glamsterdam과 LayerZero 테스트넷 실행, 2026-10-06

아홉 번째 루프의 수정, Ethereum의 Glamsterdam 업그레이드에서의 에어드랍 전달 가스, 그리고 멈춘 전달을 키퍼가 다시 실행하는 것은 2026-10-06에 이루어졌으며, 메인넷에는 배포되지 않았습니다. 이와 함께 컨트랙트 변경이 하나 있었으며(리모트 허브의 가스 정책과, 미러 볼트의 인자 세 개짜리 전송과 견적), 크로스체인 레일의 새 테스트 아홉 개(측정값 대비 기본값, 양 끝에서의 범위 맞춤, 전송의 옵션과 그에 맞는 견적, 인자 하나짜리 형태, 세터의 거부, 그 OFT가 강제하는 가스보다 무거운 전달, 정책이 없는 허브가 주식을 그대로 두는 것, 바이트 단위의 옵션)와 배포 스크립트의 테스트 네 개로 검증되었습니다. 주식 OFT 목은 이제 가스 옵션을 합산하며, 가스가 부족한 전달을 LayerZero의 엔드포인트처럼 더 많은 가스로의 실행을 기다리는 상태로 남깁니다. 이 루프 이후로 두 번째 에이전트가 각 수정의 변경을 푸시하기 전에 검토하며(검증자 게이트), 그 발견 사항도 같은 방식으로 수정되고 테스트됩니다. 2026-10-06에 커밋 92a1904에서 실행(다른 작업이 진행 중이었으므로, 컨트랙트는 그 커밋의 깨끗한 사본에서):

  • Foundry. 테스트 653개가 통과합니다: 스위트 83개에 걸친 포크 파일 밖의 646개와, 공개 RPC를 상대로 한 Robinhood Chain 포크 파일의 일곱 개; Ethereum 포크 스위트 세 개는 RPC가 없어 건너뜁니다. Foundry는 Cancun 가스 일정으로 실행되므로, 그 가스 수치는 예전 가격이며, 새 가격은 Sepolia에서 얻습니다
  • 스토리지. 업그레이드 가능한 모듈 17개의 스토리지 레이아웃은 리모트 허브의 끝부분 추가(그 가스 정책, 슬롯 21~23)를 빼고는 바뀌지 않았습니다
  • 오프체인. 키퍼의 테스트 370개, shared 패키지의 51개, 백엔드의 9개, 워커의 189개가 파일 14개에서 통과합니다(게이트의 마지막 수정 뒤인 e1dd5f6에서 키퍼는 372개, 나머지는 그대로); 키퍼와 워커는 타입 검사를 통과합니다. 각 수정의 새 테스트는, 새로 생긴 함수의 테스트를 빼고는, 그 수정 이전의 코드에서 실패합니다. 앱의 쓰기 트랜잭션마다의 새 가스 한도는 워커 엔진의 코드를 실행하며, 이는 워커의 테스트가 다룹니다
  • 앱. 타입 검사, 린트, 엔진 검사를 통과합니다; 테스트 러너는 없습니다. 다른 두 수정, 즉 추정치로 표시되는 트레저리 금액과 읽지 못한 승인은, 임시 사본에서 앱 자체의 코드를 실행해 확인했습니다
  • Glamsterdam 이후의 Sepolia에서. 컨트랙트와 스크립트의 모든 고정 가스 수치를 실제 체인에서 다시 측정했습니다: 가스 정책으로 고친 에어드랍 전달의 두 수치를 빼고는 모두 여유 있게 유지됩니다. 열 번째 감사 루프는 두 가지를 더 찾았습니다: forge가 예전 가격의 자체 시뮬레이션에서 가져오는 배포 스크립트 자체의 가스(이제 모든 브로드캐스트는 노드의 견적을 씁니다)와, 배치가 아니라 마켓 하나에 맞춰 정해진 브리지 compose의 가스입니다. LayerZero 테스트넷 실행의 배포에서 읽기 전용으로 실행한 키퍼의 사전 점검은 확인 68개를 통과합니다
  • LayerZero 테스트넷 실행. 프로토콜은 LayerZero의 실제 테스트넷 엔드포인트, DVN, 익스큐터 위에서 Sepolia와 Robinhood Chain의 테스트넷에서 처음부터 끝까지 실행되었으며, 1시간짜리 측정 구간 7개와 에어드랍 주기 6개에서 모든 청구가 보유량 기록기로 계산한 몫과 정확히 같았습니다: Robinhood 레일을 참고하십시오. 그 마지막 측정 구간은 아홉 번째 루프의 키퍼로, 그 시뮬레이션에서 고른 전달 가스로 실행되었습니다

열 번째 감사 루프, 2026-10-06

열 번째 루프의 수정은 2026-10-06에 이루어졌으며, 메인넷에는 배포되지 않았고, 각각 병합 전에 두 번째 에이전트의 검토를 거쳤습니다. 컨트랙트 하나, USDG 브리지 어댑터가 바뀌었습니다: Robinhood Chain에서 브리지 배치의 마지막 단계는 이제 기본분에 마켓당 몫을 더한 가스를 받으며, 배치 하나는 많아야 마켓 17개를 담습니다. 새 테스트 아홉 개가 이를 다룹니다: 주식 다섯 개짜리 새 마켓 네 개, 마켓 17개의 꽉 찬 배치, 배치 두 개로 보내는 마켓 서른 개(각각 정확히 그 가스로 콜드 상태에서 실행); 메시지 크기 때문에 거부되는 마켓 18개; 배치와 함께 커지는 수수료; 상한을 넘는 배치는 통째로 거부되고 다음 배치는 나가는 것; 새 설정 전에 설치된 어댑터가 설정될 때까지 아무것도 브리지하지 않는 것; 그리고 설정의 범위입니다. USDG 토큰 브리지의 목은 이제 LayerZero처럼, LayerZero의 크기 제한을 넘는 메시지를 거부하고 가스에 가격을 매깁니다. 배포 절차에는 테스트가 없습니다: 그 변경은 Sepolia에서 실행해 확인했으며, 그곳에서 배포 도구 자체의 가스로 보낸 생성은 가스가 바닥났고, 노드의 견적으로 보낸 같은 생성들은 통과했습니다. 2026-10-07에 커밋 5ea8bf0에서 실행(컨트랙트는 그 커밋의 깨끗한 사본에서):

  • Foundry. 테스트 662개가 통과합니다: 스위트 84개에 걸친 포크 파일 밖의 655개와, 공개 RPC를 상대로 한 Robinhood Chain 포크 파일의 일곱 개; Ethereum 포크 스위트 세 개는 RPC가 없어 건너뜁니다. LayerZero 테스트넷 프로젝트의 테스트 2개는 LayerZero 자체의 컨트랙트에서 통과합니다
  • 스토리지. 업그레이드 가능한 모듈 17개의 스토리지 레이아웃은 브리지 어댑터의 끝부분 추가(그 두 배치 설정)를 빼고는 바뀌지 않았습니다
  • 오프체인. 키퍼의 테스트 400개, shared 패키지의 51개, 백엔드의 9개, 워커의 210개가 파일 15개에서 통과합니다; 키퍼와 워커는 타입 검사를 통과합니다. 각 검토자의 개념 증명은 회귀 테스트이며, 각 수정의 새 테스트는, 새로 생긴 함수의 테스트를 빼고는, 그 수정 이전의 코드에서 실패합니다. 지갑이 취소하거나 교체한 트랜잭션에 대한 앱의 추적과, 자신의 마지막 트랜잭션의 블록보다 이르지 않은 블록에서의 앱의 읽기는 워커 엔진의 코드를 실행하며, 이는 워커의 테스트가 다룹니다
  • 앱. 타입 검사, 린트, 엔진 검사를 통과합니다; 테스트 러너는 없습니다
  • 테스트넷에서. LayerZero 테스트넷 실행의 브리지 어댑터는 2026-10-06에 Sepolia에서 업그레이드되었고, 그 다음 배치(마켓 하나)는 LayerZero의 익스큐터가 새 가스 600,000으로 전달하고 compose했으며, 그중 115,990이 쓰였습니다

테스트가 증명하지 않는 것

이는 중요하며, 프로젝트의 자체 보고서도 이를 분명히 밝힙니다.

로컬 테스트는 크로스체인 전달을 시뮬레이션합니다. 실제 LayerZero 전달을 증명하지는 않습니다. 에어드랍의 크로스체인 테스트는 LayerZero와, 메인넷용으로는 저장소에 없는 주식 어댑터의 목을 상대로 실행됩니다. 공개 엔드포인트가 HTTP 오류를 반환해 최소 한 번의 실행에서 포크 스위트가 제외되었습니다 — 그리고 해당 보고서는 이 스위트들을 통과한 것처럼 제시하는 대신 그 사실을 밝힙니다.

2026-10-06의 LayerZero 테스트넷 실행은 테스트 주식, 테스트 어댑터, 테스트 USDG, 목 피드로 두 테스트넷 사이에 실제 LayerZero 메시지를 운반했습니다: 이 실행은 StockFun의 코드가 LayerZero의 전송 위에서 동작함을 증명하지만, Paxos의 USDG 쌍, Robinhood의 주식과 그 어댑터, 실제 피드와 유동성, 또는 메인넷의 가스, 수수료, 완결성은 증명하지 않습니다.

어떤 실행도 다음을 증명하지 않습니다: 메인넷에서의 LayerZero 전송, 외부 검토, 또는 현재 레일을 다루는 정형 증명. 테스트넷 실행은 실제 지갑으로 서명했으며, 그 청구 중 세 개는 앱을 통해 이루어졌습니다.

로컬 환경

전체 시나리오는 네 개의 터미널에서 실행됩니다: 체인, 배포와 시뮬레이션, 가격 서비스, 디앱. 그다음 키퍼와 브라우저 하네스를 실행합니다.

정확한 절차는 projet/docs/LOCAL_TESTING.md에 있습니다.