From 118095170f66c94567a94e26aacd3c94fc344bfb Mon Sep 17 00:00:00 2001 From: Alice <34962750+hensha256@users.noreply.github.com> Date: Wed, 21 Aug 2024 12:55:16 +0100 Subject: [PATCH] Take portion tests (#377) * take portion test * take portion test native * correct comment * take portion on input test * remove console logs oops --- test/integration-tests/UniswapV4.test.ts | 130 ++++++++++++++++++++++- 1 file changed, 129 insertions(+), 1 deletion(-) diff --git a/test/integration-tests/UniswapV4.test.ts b/test/integration-tests/UniswapV4.test.ts index 7dcae5fd..324c8c31 100644 --- a/test/integration-tests/UniswapV4.test.ts +++ b/test/integration-tests/UniswapV4.test.ts @@ -4,7 +4,16 @@ import { expect } from './shared/expect' import { IPermit2, PoolManager, PositionManager, UniversalRouter } from '../../typechain' import { abi as TOKEN_ABI } from '../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json' import { resetFork, WETH, DAI, USDC, PERMIT2 } from './shared/mainnetForkHelpers' -import { ALICE_ADDRESS, DEADLINE, ETH_ADDRESS, MAX_UINT, MAX_UINT160, MSG_SENDER } from './shared/constants' +import { + ALICE_ADDRESS, + DEADLINE, + ETH_ADDRESS, + MAX_UINT, + MAX_UINT160, + MSG_SENDER, + ONE_PERCENT_BIPS, + OPEN_DELTA, +} from './shared/constants' import { expandTo18DecimalsBN, expandTo6DecimalsBN } from './shared/helpers' import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' import deployUniversalRouter from './shared/deployUniversalRouter' @@ -201,6 +210,125 @@ describe('Uniswap V4 Tests:', () => { expect(daiBalanceBefore.sub(daiBalanceAfter)).to.be.eq(amountInDAI) }) + it('completes a v4 exactIn 2 hop swap, with take portion on output', async () => { + // DAI -> USDC -> WETH + let currencyIn = daiContract.address + v4Planner.addAction(Actions.SWAP_EXACT_IN, [ + { + currencyIn, + path: encodeMultihopExactInPath([DAI_USDC.poolKey, USDC_WETH.poolKey], currencyIn), + amountIn: amountInDAI, + amountOutMinimum: minAmountOutNative, + }, + ]) + // take 1% of the output to alice, then settle and take the rest to the caller + v4Planner.addAction(Actions.TAKE_PORTION, [WETH.address, alice.address, ONE_PERCENT_BIPS]) + v4Planner.addAction(Actions.SETTLE_TAKE_PAIR, [currencyIn, wethContract.address]) + + planner.addCommand(CommandType.V4_SWAP, [v4Planner.actions, v4Planner.params]) + + const wethBalanceBeforeAlice = await wethContract.balanceOf(alice.address) + + const { wethBalanceBefore, wethBalanceAfter } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) + + const wethBalanceAfterAlice = await wethContract.balanceOf(alice.address) + + const aliceFee = wethBalanceAfterAlice.sub(wethBalanceBeforeAlice) + const bobEarnings = wethBalanceAfter.sub(wethBalanceBefore) + const totalOut = aliceFee.add(bobEarnings) + + expect(totalOut).to.be.gte(minAmountOutNative) + expect(totalOut.mul(ONE_PERCENT_BIPS).div(10_000)).to.eq(aliceFee) + }) + + it('completes a v4 exactIn 2 hop swap, with take portion on input', async () => { + // DAI -> USDC -> WETH + let currencyIn = daiContract.address + // trade is 1% less than previously, so adjust expected output + let minOut = minAmountOutNative.mul(99).div(100) + + // settle the input tokens to the pool manager + v4Planner.addAction(Actions.SETTLE, [currencyIn, amountInDAI, true]) + // take 1% of the input tokens + v4Planner.addAction(Actions.TAKE_PORTION, [currencyIn, alice.address, ONE_PERCENT_BIPS]) + // swap using the OPEN_DELTA as input amount + v4Planner.addAction(Actions.SWAP_EXACT_IN, [ + { + currencyIn, + path: encodeMultihopExactInPath([DAI_USDC.poolKey, USDC_WETH.poolKey], currencyIn), + amountIn: OPEN_DELTA, + amountOutMinimum: minOut, + }, + ]) + // take the output weth + v4Planner.addAction(Actions.TAKE_ALL, [wethContract.address, minOut]) + + planner.addCommand(CommandType.V4_SWAP, [v4Planner.actions, v4Planner.params]) + + const daiBalanceBeforeAlice = await daiContract.balanceOf(alice.address) + + const { daiBalanceBefore, daiBalanceAfter } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) + + const daiBalanceAfterAlice = await daiContract.balanceOf(alice.address) + + const aliceFee = daiBalanceAfterAlice.sub(daiBalanceBeforeAlice) + const bobSpent = daiBalanceBefore.sub(daiBalanceAfter) + + expect(bobSpent.mul(ONE_PERCENT_BIPS).div(10_000)).to.eq(aliceFee) + }) + + it('completes a v4 exactIn 2 hop swap, with take portion native', async () => { + // DAI -> USDC -> ETH + let currencyIn = daiContract.address + v4Planner.addAction(Actions.SWAP_EXACT_IN, [ + { + currencyIn, + path: encodeMultihopExactInPath([DAI_USDC.poolKey, ETH_USDC.poolKey], currencyIn), + amountIn: amountInDAI, + amountOutMinimum: minAmountOutNative, + }, + ]) + // take 1% of the output to alice, then settle and take the rest to the caller + v4Planner.addAction(Actions.TAKE_PORTION, [ETH_ADDRESS, alice.address, ONE_PERCENT_BIPS]) + v4Planner.addAction(Actions.SETTLE_TAKE_PAIR, [currencyIn, ETH_ADDRESS]) + + planner.addCommand(CommandType.V4_SWAP, [v4Planner.actions, v4Planner.params]) + + const ethBalanceBeforeAlice: BigNumber = await ethers.provider.getBalance(alice.address) + + const { ethBalanceBefore, ethBalanceAfter, gasSpent } = await executeRouter( + planner, + bob, + router, + wethContract, + daiContract, + usdcContract + ) + + const ethBalanceAfterAlice: BigNumber = await ethers.provider.getBalance(alice.address) + + const aliceFee = ethBalanceAfterAlice.sub(ethBalanceBeforeAlice) + const bobEarnings = ethBalanceAfter.add(gasSpent).sub(ethBalanceBefore) + const totalOut = aliceFee.add(bobEarnings) + + expect(totalOut).to.be.gte(minAmountOutNative) + expect(totalOut.mul(ONE_PERCENT_BIPS).div(10_000)).to.eq(aliceFee) + }) + it('completes a v4 exactOutSingle swap', async () => { v4Planner.addAction(Actions.SWAP_EXACT_OUT_SINGLE, [ {