function tend(
 uint256 totalWeight,
 LiquidityPositions memory newPositions,
 bytes calldata timeSensitiveData
 ) external onlyRole(ORCHESTRATOR\_ROLE) whenNotPaused {
 (int256 swapAmount, uint160 sqrtPriceLimitX96) = abi.decode(
 timeSensitiveData,
 (int256, uint160)
 );

 // Get current pool state
 (uint160 sqrtPriceX96, int24 currentTick, , , , , ) = pool.slot0();

 // currentTick must be close enough to TWAP tick to avoid MEV exploit
 // This is essentially a way to prevent a flashloan attack
 // even if sqrtPriceLimit is set incorrectly.
 \_checkVolatility(currentTick);

 // Withdraw liquidity from Uniswap pool by passing in 1 and 1
 // (indicating we're withdrawing 100% of liquidity)
 \_burnAndCollect(1, 1);

 // Update positions if desired. If newPositions is empty,
 // we'll just continue with the old positions instead.
 if (newPositions.lowerTick.length > 0) {
 positions = newPositions;
 }

 // Perform a swap if desired.
 if (swapAmount != 0) {
 bool zeroForOne = swapAmount > 0;
 pool.swap(
 address(this),
 zeroForOne,
 zeroForOne ? swapAmount : -swapAmount,
 sqrtPriceLimitX96,
 ""
 );

 // Update sqrtPriceX96; it will have moved due to the swap
 (sqrtPriceX96, , , , , , ) = pool.slot0();
 }

 uint256 balance0 = \_getBalance0();
 uint256 balance1 = \_getBalance1();

 emit Snapshot(sqrtPriceX96, balance0, balance1, totalSupply());

 // Create new positions in Uniswap
 if (totalWeight > 0) {
 \_setBins(
 sqrtPriceX96,
 // balance0 adjusted by totalWeight
 FullMath.mulDiv(balance0, totalWeight, 1e4),
 // balance1 adjusted by totalWeight
 FullMath.mulDiv(balance1, totalWeight, 1e4),
 swapAmount
 );
 }
 }

 // Public Functions

 /// @dev burns each vault position which contains liquidity, updating fees owed to that position.
 /// Call this before calling getTotalAmounts if total amounts must include fees.
 /// There's a function in the periphery to do so through a static call.
 function poke() public override {
 LiquidityPositions memory \_positions = positions;
 uint256 positionCount = \_positions.lowerTick.length;
 for (uint256 i; i != positionCount; ++i) {
 // Get position liquidity so that we can ignore this position if it has 0 liquidity.
 (uint128 positionLiquidity, , , , ) = \_position(
 \_positions.lowerTick[i],
 \_positions.upperTick[i]
 );

 // If position has liquidity, update fees owed.
 if (positionLiquidity > 0) {
 pool.burn(\_positions.lowerTick[i], \_positions.upperTick[i], 0);
 }
 }
 }

 /// @dev Calculates the vault's total holdings of token0 and token1.
 /// in other words, how much of each token the vault would hold if it withdrew
 /// all its liquidity from Uniswap.
 /// This function DOES NOT include fees earned since the last burn.
 /// To include fees, first poke() and then call getTotalAmounts.
 /// There's a function inside the periphery to do so.
 function getTotalAmounts()
 public
 view
 override
 returns (uint256 total0, uint256 total1)
 {
 // Start with tokens currently held inside the vault
 total0 = \_getBalance0();
 total1 = \_getBalance1();

 // These include fees to steer and strategist,
 // which we remove before adding them to total0 and total1.
 uint256 totalT0Fees;
 uint256 totalT1Fees;

 // Might be easier to just calculate this and use it directly.
 // Probably will be.
 LiquidityPositions memory \_positions = positions;
 (uint160 sqrtPriceX96, , , , , , ) = pool.slot0();
 uint256 positionCount = \_positions.lowerTick.length;
 for (uint256 i; i != positionCount; ++i) {
 // Get calculated amounts of tokens contained within this pool position
 (
 uint256 amt0,
 uint256 amt1,
 uint256 fees0,
 uint256 fees1
 ) = \_getPositionAmounts(
 sqrtPriceX96,
 \_positions.lowerTick[i],
 \_positions.upperTick[i]
 );

 // Increase balances
 total0 = total0.add(amt0);
 total1 = total1.add(amt1);

 // Increase fees
 totalT0Fees = totalT0Fees.add(fees0);
 totalT1Fees = totalT1Fees.add(fees1);
 }

 // Subtract protocol fees from position fee earned,
 // then add the (now LP-owned) remaining tokens to total balances
 total0 = total0.add(
 FullMath.mulDiv(totalT0Fees, ONE\_MINUS\_FEE, FEE\_DIVISOR)
 );
 total1 = total1.add(
 FullMath.mulDiv(totalT1Fees, ONE\_MINUS\_FEE, FEE\_DIVISOR)
 );
 }
함수 tend( uint256 totalWeight, 유동성포지션 메모리 newPositions, 바이트 호출 데이터 시간 민감 데이터 ) external onlyRole(ORCHESTRATOR\_ROLE) whenNotPaused { (int256 swapAmount, uint160 sqrtPriceLimitX96) = abi.decode( timeSensitiveData, (int256, uint160) ); // 현재 풀 상태 가져오기 (uint160 sqrtPriceX96, int24 currentTick, , , , , ) = pool.slot0(); // MEV 익스플로잇을 피하려면 currentTick이 TWAP tick에 충분히 가까워야 합니다. // 이것은 본질적으로 플래시론 공격을 방지하는 방법입니다. // 플래시론 공격을 방지하는 방법입니다. \체크변동성(currentTick); // 1과 1을 전달하여 유니스왑 풀에서 유동성을 인출합니다. // (유동성의 100%를 인출한다는 의미) \_burnAndCollect(1, 1); // 원하는 경우 포지션을 업데이트합니다. 새로운 포지션이 비어 있으면, // 대신 이전 포지션을 계속 유지합니다. if (newPositions.lowerTick.length > 0) { positions = newPositions; } // 원하는 경우 스왑을 수행합니다. if (swapAmount != 0) { 불리언 제로포원 = swapAmount > 0; pool.swap( address(this), zeroForOne, zeroForOne ? swapAmount : -swapAmount, sqrtPriceLimitX96, "" ); // sqrtPriceX96 업데이트; 스왑으로 인해 이동했을 것입니다. (sqrtPriceX96, , , , , ) = pool.slot0(); } uint256 balance0 = \_getBalance0(); uint256 balance1 = \_getBalance1(); emit Snapshot(sqrtPriceX96, balance0, balance1, totalSupply()); // 유니스왑에 새 포지션 생성 if (totalWeight > 0) { \_setBins( sqrtPriceX96, // 잔액0은 총무게로 조정됨 FullMath.mulDiv(balance0, totalWeight, 1e4), // 총무게로 조정된 잔액1 FullMath.mulDiv(balance1, totalWeight, 1e4), swapAmount ); } } // 공용 함수 /// 유동성이 포함된 각 볼트 포지션을 소각하여 해당 포지션에 지불해야 할 수수료를 업데이트합니다. /// 총 금액에 수수료가 포함되어야 하는 경우 getTotalAmounts를 호출하기 전에 이 함수를 호출하세요. /// 정적 호출을 통해 이를 수행하는 함수가 주변부에 있습니다. 함수 포케() 공개 오버라이드 { 유동성포지션 메모리 \_포지션 = 포지션; uint256 positionCount = \_positions.lowerTick.length; for (uint256 i; i != positionCount; ++i) { // 유동성이 0인 경우 이 포지션을 무시할 수 있도록 포지션 유동성을 가져옵니다. (uint128 positionLiquidity, , , , ) = \_position( \_positions.lowerTick[i], \_positions.upperTick[i], ) ); // 포지션에 유동성이 있는 경우, 업데이트 수수료를 지불합니다. if (positionLiquidity > 0) { pool.burn(\_positions.lowerTick[i], \_positions.upperTick[i], 0); } } } /// @dev 볼트의 토큰0과 토큰1의 총 보유량을 계산합니다. /// 즉, 볼트가 모든 토큰을 인출할 경우 보유할 수 있는 각 토큰의 양을 계산합니다. /// 유니스왑에서 모든 유동성을 인출할 경우 각 토큰이 얼마나 보유하게 될지 계산합니다. /// 이 함수에는 마지막 소각 이후 발생한 수수료는 포함되지 않습니다. /// 수수료를 포함하려면 먼저 poke()를 호출한 다음 getTotalAmounts를 호출하세요. /// 이를 위한 함수가 퍼리퍼리 안에 있습니다. 함수 getTotalAmounts() public view override 반환 (uint256 total0, uint256 total1) { // 현재 볼트에 보관된 토큰부터 시작합니다. total0 = \_getBalance0(); total1 = \_getBalance1(); // 여기에는 운영자와 전략가에게 지불하는 수수료가 포함됩니다, // 총액과 총액1에 추가하기 전에 제거합니다. uint256 totalT0Fees; uint256 totalT1Fees; // 그냥 계산해서 직접 사용하는 것이 더 쉬울 수도 있습니다. // 아마 그럴 겁니다. 유동성포지션 메모리 \_포지션 = 포지션; (uint160 sqrtPriceX96, , , , , , ) = pool.slot0(); uint256 positionCount = \_positions.lowerTick.length; for (uint256 i; i != positionCount; ++i) { // 이 풀 포지션에 포함된 토큰의 계산된 수량을 가져옵니다. ( uint256 amt0, uint256 amt1, uint256 fees0, uint256 수수료1 ) = \_getPositionAmounts( sqrtPriceX96, \_positions.lowerTick[i], \_positions.upperTick[i], \_positions.upperTick[i]) ); // 잔액 증가 total0 = total0.add(amt0); total1 = total1.add(amt1); // 수수료 증가 totalT0Fees = totalT0Fees.add(fees0); totalT1Fees = totalT1Fees.add(fees1); } // 벌어들인 포지션 수수료에서 프로토콜 수수료를 뺍니다, // 그리고 (현재 LP가 소유한) 남은 토큰을 총 잔액에 더합니다. total0 = total0.add( FullMath.mulDiv(totalT0Fees, ONE\_MINUS\_FEE, FEE\_DIVISOR) ); total1 = total1.add( FullMath.mulDiv(totalT1Fees, ONE\_MINUS\_FEE, FEE\_DIVISOR) ); }