머리말
탈중앙화 금융(DeFi)의 급속한 발전과 함께 Uniswap은 선도적인 탈중앙화 거래소로서 혁신의 최전선에 서왔습니다. 이 기사에서는 Uniswap v3 프로토콜의 핵심 메커니즘에 대한 심층 분석과 중앙 집중식 유동성, 다중 요율, 토큰 교환 및 플래시 대출과 같은 주요 기능을 포함한 기능 설계에 대한 자세한 설명을 제공하는 동시에 관련 감사 포인트를 제공합니다. 감사자. (참고: 이 기사의 이미지는 https://www.figma.com/board/QyIpAUR93MxZ4XZZf2QjDk/uniswap-v3에서 고화질로 볼 수 있습니다.)
아키텍처 분석
Uniswap v3 프로토콜은 주로 4개의 모듈로 구성됩니다.
- PositionManager: 사용자가 토큰 풀을 생성하고, 유동성을 제공/제거하고, 유동성 공급자(LP)에 대한 자격 증명으로 ERC721을 사용할 수 있는 유동성 작업을 수행하기 위한 기본 인터페이스입니다.
- SwapRouter: 사용자가 토큰을 교환하기 위한 입구입니다. 사용자는 이 모듈을 통해 토큰 교환 작업을 완료할 수 있습니다.
- 풀: 토큰 거래 구현, 유동성 관리, 거래 수수료 징수 및 Oracle 데이터 관리 기능을 담당합니다. 그 중 Tick 메커니즘은 가격 범위를 여러 개의 미세한 척도로 나눕니다.
- 팩토리: 풀 계약을 생성하고 관리하는 데 사용됩니다.
프로세스 정렬
토큰 쌍 만들기
사용자는 createAndInitializePoolIfNecessary 함수를 통해 이를 수행할 수 있습니다. 사용자는 토큰 쌍의 token0, token1, 처리 수수료(수수료) 및 초기 가격()을 전달해야 합니다. 먼저 시스템은 getPool 함수를 통해 토큰 쌍이 이미 존재하는지 확인합니다. 토큰 쌍이 생성되지 않은 경우 createPool이 호출되고 CREATE2 명령을 사용하여 거래 쌍을 배포합니다. 마지막으로 초기화 기능은 가격, 처리 수수료, 진드기, 오라클 및 기타 관련 매개변수의 초기화를 완료하는 데 사용됩니다.
유동성 제공
유동성 제공
사용자는 민트 기능을 통해 새로운 유동성 포지션을 생성하고 해당 NFT를 생성하거나, 증가액체 기능을 통해 기존 NFT 유동성 포지션에 유동성을 추가할 수 있습니다. 먼저 시스템은 지정된 시간 범위 내에 트랜잭션이 실행되는지 확인한 다음 addLiquidity 함수를 호출하여 특정 작업을 완료합니다. 이 함수에서는 풀의 주소와 유동성을 먼저 계산한 후 _updatePosition을 호출하여 사용자의 포지션을 업데이트하고 하위 틱, 상위 틱 및 총 누적 처리 수수료를 수정합니다. 이후 시스템은 _modifyPosition을 통해 유동성을 추가하고 틱이 상한 및 하한 조건을 충족하는지 확인하고 계산된 token0 및 token1 수(int256)를 반환하여 풀로 보냅니다. 마지막으로 시스템은 사용자의 tokenId를 기반으로 해당 위치 정보를 업데이트합니다.
유동성 제거
사용자는 ReduceLiquidity 기능을 통해 유동성을 제거할 수 있습니다. 먼저, 시스템은 LP 인증서의 권한과 거래의 시간 유효성을 확인합니다. 풀의 유동성이 충분한지 확인한다는 전제하에 소각 기능을 호출하여 유동성을 제거합니다. 그 후, 시스템은 제거된 실제 토큰 수가 사용자가 설정한 최소 요구 사항을 충족하는지 확인하고 그에 따라 사용자의 위치 정보를 업데이트합니다.
교환
사용자는 strictInput 함수를 통해 지불할 토큰 수와 획득할 것으로 예상되는 최소 토큰 수를 지정하거나, strictOutput 함수를 통해 지불할 최대 토큰 수를 지정하고 획득할 것으로 예상되는 토큰 수를 설정할 수 있습니다. 시스템은 먼저 경로(path)를 구문 분석한 다음, 정확한InputInternal 또는 정확한OutputInternal 함수를 순서대로 호출하여 스왑 작업의 각 단계를 완료합니다.
스왑 기능에서 시스템은 먼저 잠금 해제 상태를 잠가서 다른 트랜잭션이 상태 변수 업데이트를 방해하지 못하도록 합니다. 루프에 진입한 후 시스템은 틱을 통해 다음 거래 가격을 찾고, tokenIn 또는 tokenOut이 사용자의 기대에 도달할 때까지 각 단계에서 exchange를 계산하기 위해 ComputeSwapStep 함수를 호출합니다. 동시에 시스템은 수수료, 유동성, 진드기 및 가격과 관련된 값을 업데이트합니다. 눈금이 변경되면 Oracle 데이터도 업데이트해야 합니다. 이러한 작업을 완료한 후 시스템은 사용자에게 tokenOut을 지불하고 사용자는 콜백 함수 uniswapV3SwapCallback을 통해 tokenIn을 지불합니다. 이 메커니즘은 플래시 스왑으로 간주될 수 있습니다. 이후 시스템은 계약 잔액이 일치하는지 확인하고 확인 후 잠금 해제 상태를 잠금 해제합니다.
경로의 모든 스왑 작업이 완료되고 트랜잭션이 사용자의 기대를 충족하면 트랜잭션이 성공적으로 종료됩니다.
플래시
사용자는 플래시 기능을 통해 플래시 대출 업무를 수행할 수 있습니다. 먼저 시스템은 대출 수수료를 계산한 후 사용자가 요구하는 토큰을 지정된 대출 주소로 보냅니다. 다음으로 시스템은 사용자가 구현한 uniswapV3FlashCallback 함수를 콜백하고 사용자는 이 함수에서 상환 작업을 완료합니다. 시스템은 콜백 이후 계약 잔액의 변경 사항을 확인하여 사용자의 대출 금액과 일치하는지 확인하고 해당 처리 수수료를 업데이트합니다. 플래시 기능 외에도 사용자는 스왑 작업을 통해 유사한 플래시 대출 기능을 구현할 수도 있습니다. 즉, 거래 프로세스 중에 토큰을 먼저 빌린 다음 상환하는 것입니다.
감사 포인트
감사 포인트
1. 스왑 작업 후 RefundETH가 호출되는지 확인합니다.
정확한Input 함수에서 사용자는 지불할 토큰 수와 획득할 것으로 예상되는 최소 토큰 수를 지정해야 합니다. uniswapV3SwapCallback을 호출하기 전에 시스템은 사용자가 토큰을 정확하게 보낼 수 있도록 amount0과 amount1을 다시 계산합니다. 단, ETH로 교환할 경우 사용자는 거래와 함께 ETH를 보내야 합니다. 거래 중에 ETH를 모두 사용하지 않더라도 이 기능은 초과분을 자동으로 반환하지 않습니다. 정확한Input 함수는 amountOut만 반환하므로 거래자는 이 거래소에서 실제로 얼마나 많은 ETH가 소비되었는지 직접 알 수 없습니다.
또한 누구든지 RefundETH 함수를 호출하여 계약에서 사용되지 않은 ETH를 인출할 수 있습니다. 따라서 사용자가 프로토콜에 사용하지 않은 ETH를 남기는 것을 방지하기 위해 스왑 작업 후 환불ETH가 호출되는지 확인하거나 MultiCall 기능을 사용하여 한 작업에서 여러 함수 호출을 완료하는 것이 좋습니다.
2. 오라클 가격을 얻기 위한 TWAP 구현 여부 확인
Uniswap을 가격 소스로 사용하는 경우 외부 프로토콜이 sqrtPriceX96을 얻기 위해 Slot0에 직접 액세스하면 가격 조작의 위험이 있을 수 있습니다. 공격자는 거래 실행 시 유리한 가격을 얻기 위해 스왑 및 기타 방법을 통해 유동성 풀의 상태를 조작할 수 있습니다.
이러한 위험을 줄이기 위해 개발자는 가격을 얻기 위해 TWAP(Time Weighted Average Price)를 추가로 구현하는 것이 좋습니다. TWAP는 단기적으로 급격한 가격 변동의 영향을 효과적으로 줄여 가격 조작을 더 어렵게 만들 수 있기 때문입니다.
3. 사용자가 스스로 슬리피지 매개변수를 설정할 수 있도록 허용하는 것이 좋습니다.
다른 프로토콜이 스왑 작업에 Uniswap v3를 사용하는 경우 개발자는 비즈니스 시나리오에 따라 미끄러짐 방지를 설정하고 사용자가 샌드위치 공격을 방지하기 위해 매개변수를 직접 조정할 수 있도록 허용하는 것이 좋습니다. 이 스왑 함수에서 네 번째 매개변수 sqrtPriceLimitX96은 사용자가 스왑을 수행하려는 최소 또는 최대 가격을 지정하는 데 사용됩니다. 이 매개변수는 거래 과정에서 극심한 가격 변동을 효과적으로 방지하여 과도한 미끄러짐으로 인한 사용자의 손실을 줄일 수 있습니다.
4. 유동성 풀 화이트리스트 메커니즘 도입을 권장합니다.
Uniswap v3에서는 동일한 ERC20 토큰 쌍이 서로 다른 수수료에 따라 여러 유동성 풀(풀)에 동시에 존재할 수 있습니다. 일반적으로 소수의 유동성 풀이 유동성의 대부분을 보유하는 반면, 다른 풀은 TVL(잠금된 총 거래량)이 거의 없거나 아직 생성되지 않았을 수 있습니다. 이러한 낮은 TVL 풀은 가격 조작의 대상이 될 가능성이 더 높습니다.
따라서 프로젝트 당사자가 유동성 풀 데이터를 사용하기로 선택한 경우 단순히 LP를 데이터 소스로 사용하는 것은 피해야 합니다. 데이터의 신뢰성을 보장하기 위해 유동성이 충분하고 조작이 어려운 풀을 선별하는 화이트리스트 메커니즘을 도입하는 것이 좋습니다. 이 메커니즘은 위험을 크게 줄여 가격 참조 데이터의 보안과 정확성을 보장하는 동시에 TVL이 너무 낮은 풀 조작으로 인한 잠재적 손실을 방지할 수 있습니다.
5. TickMath.sol, FullMath.sol 및 Position.sol에서 선택 취소가 사용되는지 확인하십시오.
TickMath, FullMath 및 Position과 같은 모듈은 Uniswap v3에서 Solidity의 오버플로 처리 메커니즘에 의존하는 복잡한 수학적 계산을 수행하는 데 사용됩니다. 이전 버전의 Solidity(<0.8.0)에서는 정수 오버플로 및 언더플로 동작이 기본적으로 예외를 발생시키지 않았으므로 이 가정에 따라 코드가 정상적으로 실행될 수 있었습니다. 그러나 Solidity 버전 0.8.0부터 오버플로 및 언더플로는 자동으로 예외를 발생시켜 기존 코드 실행에 영향을 미칩니다. 이러한 모듈이 Solidity 0.8.0 이상에서 제대로 작동하는지 확인하려면 개발자는 특정 기능에서 확인되지 않은 코드 블록을 사용하여 오버플로 검사를 수동으로 비활성화해야 합니다. 이는 이전 버전의 동작을 복원하고 오버플로에 민감한 작업의 효율적인 실행을 보장합니다.
Solidity 0.8.0 이상에 대한 공식 지원 및 조정이 이루어졌습니다. 자세한 내용은 이 업데이트(https://github.com/Uniswap/v3-core/commit/6562c52e8f75f0c10f9deaf44861847585fc8129)를 참조하세요. 이 변경으로 인해 TickMath, FullMath 및 기타 관련 모듈이 새 버전의 컴파일러에서 계속 올바르게 실행됩니다.
6. 경로 인코딩 방식과 디코딩 방식이 동일한지 확인하세요.
Uniswap v3의 정확한Input 및 정확한Output 기능에서 사용자는 단계별 토큰 교환 작업을 위해 고정 형식, 즉 tokenA-fee-tokenB에 따라 인코딩 및 디코딩되어야 하는 경로 매개변수를 입력해야 합니다. 이 경로 구조는 거래의 각 홉에 관련된 두 개의 토큰과 그 사이의 수수료 수준을 명시적으로 지정합니다. Uniswap v3의 토큰 교환 기능을 사용할 때 외부 프로토콜이 다른 경로 디코딩 방법을 선택하는 경우 Uniswap의 예상 경로 형식과 일치하지 않는 경로 형식이 발생할 수 있습니다. 이 경우 프로토콜이 경로를 올바르게 해석하지 못하여 의도한 토큰 교환 작업을 성공적으로 수행하지 못할 수 있습니다.
따라서 개발자는 Uniswap v3의 토큰 교환 기능을 통합할 때 외부 프로토콜이 Uniswap의 경로 인코딩 규칙을 엄격하게 따르도록 하는 것이 좋습니다. 경로 디코딩 오류를 방지하려면 외부 프로토콜은 정확한Input 및 정확한Output을 호출할 때 경로 매개변수의 형식을 주의 깊게 확인하여 트랜잭션 실패나 예상치 못한 결과를 방지해야 합니다.
따라서 개발자는 Uniswap v3의 토큰 교환 기능을 통합할 때 외부 프로토콜이 Uniswap의 경로 인코딩 규칙을 엄격하게 따르도록 하는 것이 좋습니다. 경로 디코딩 오류를 방지하려면 외부 프로토콜은 정확한Input 및 정확한Output을 호출할 때 경로 매개변수의 형식을 주의 깊게 확인하여 트랜잭션 실패나 예상치 못한 결과를 방지해야 합니다.
7. 토큰 순서가 프로젝트 로직에 영향을 미치는지 확인하세요.
Uniswap에서는 token0이 낮은 순서의 토큰으로 기본 토큰으로 사용되고, token1은 높은 순서의 토큰으로 견적 토큰으로 사용됩니다. Uniswap은 풀에서 토큰 쌍의 순서가 항상 일관되도록 두 토큰의 주소를 사전순으로 정렬합니다.
그러나 서로 다른 블록체인 네트워크에 있는 동일한 토큰의 계약 주소가 다를 수 있으므로, 특히 체인 전체에 배포된 계약의 경우 토큰의 정렬 순서가 변경될 수 있습니다. 이러한 변경으로 인해 token0과 token1의 역할이 바뀌게 되어 가격 성능에 영향을 미치게 됩니다. 예를 들어, 일부 체인에서는 특정 토큰이 token0일 수 있지만 다른 체인에서는 token1로 주문될 수 있으며, 이로 인해 기본 토큰과 인용 토큰 간의 관계가 달라져 궁극적으로 표시된 가격에 영향을 미칠 수 있습니다. 따라서 개발자는 토큰의 순서가 프로젝트 로직에 영향을 미치는지 확인하는 것이 좋습니다. 특히 크로스체인 환경에서는 가격 성과에 부정적인 영향을 미치지 않도록 토큰의 순서로 인해 발생할 수 있는 가격 문제를 반드시 고려하는 것이 좋습니다. 그리고 트랜잭션 로직.
요약
위의 기본 점검 항목은 현재 버전의 Uniswap v3을 기준으로 하며 감사자가 Uniswap v3와 상호 작용하는 프로젝트를 점검하는 데 사용됩니다. 다양한 프로젝트 수행에는 고유한 특성이 있으므로 감사인은 계약에 대한 심층적인 이해를 갖고 실제 상황에 따라 엄격한 검사를 수행해야 합니다. 개발 중인 프로젝트의 경우 SlowMist 보안 팀은 개발자가 프로토콜의 보안과 신뢰성을 보장하기 위해 개발 프로세스 중에 이러한 검사를 신중하게 고려할 것을 권장합니다.
저자 |
편집자 |
모든 댓글