Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mixed route v4 tests #374

Open
wants to merge 6 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
251 changes: 247 additions & 4 deletions test/integration-tests/UniswapMixed.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { expect } from './shared/expect'
import { BigNumber } from 'ethers'
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, USDT, PERMIT2 } from './shared/mainnetForkHelpers'
import { resetFork, WETH, DAI, USDC, USDT, PERMIT2, USD_ETH_PRICE } from './shared/mainnetForkHelpers'
import {
ADDRESS_THIS,
ALICE_ADDRESS,
Expand Down Expand Up @@ -51,9 +51,6 @@ describe('Uniswap V2, V3, and V4 Tests:', () => {
let v4PoolManager: PoolManager
let v4PositionManager: PositionManager

// current market ETH price at block
const USD_ETH_PRICE = 3820

beforeEach(async () => {
await resetFork()
await hre.network.provider.request({
Expand Down Expand Up @@ -170,6 +167,252 @@ describe('Uniswap V2, V3, and V4 Tests:', () => {
const { amount1: wethTraded } = v3SwapEventArgs!
expect(wethBalanceAfter.sub(wethBalanceBefore)).to.eq(wethTraded.mul(-1))
})

it('V3, then V4', async () => {
// DAI -v3-> USDC -v4-> WETH
const v3Tokens = [DAI.address, USDC.address]
const v3AmountIn: BigNumber = expandTo18DecimalsBN(1234)
const v3AmountOutMin = 0
const v4AmountOutMin = expandTo18DecimalsBN(1234 / (USD_ETH_PRICE * 1.01))

planner.addCommand(CommandType.V3_SWAP_EXACT_IN, [
ADDRESS_THIS, // the router is the recipient of the v3 trade
v3AmountIn,
v3AmountOutMin,
encodePathExactInput(v3Tokens),
SOURCE_MSG_SENDER, // the user pays for the input of the v3 trade
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in v3 you input the payer?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its just a boolean (like in v4) called payerIsUser. So SOURCE_MSG_SENDER == true and SOURCE_ROUTER == false

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesnt let you pull from any address

])

// prep the v4 swap
// settle USDC that are in the router, into v4, to get a positive delta
v4Planner.addAction(Actions.SETTLE, [USDC.address, CONTRACT_BALANCE, false])
v4Planner.addAction(Actions.SWAP_EXACT_IN, [
{
currencyIn: USDC.address,
path: encodeMultihopExactInPath([USDC_WETH.poolKey], USDC.address),
amountIn: OPEN_DELTA,
amountOutMinimum: v4AmountOutMin,
},
])
v4Planner.addAction(Actions.TAKE_ALL, [WETH.address, 0])

// add the v4 commands to the UR calldata
planner.addCommand(CommandType.V4_SWAP, [v4Planner.actions, v4Planner.params])

const { daiBalanceBefore, daiBalanceAfter, wethBalanceBefore, wethBalanceAfter } = await executeRouter(
planner,
bob,
router,
wethContract,
daiContract,
usdcContract
)

expect(wethBalanceAfter.sub(wethBalanceBefore)).to.be.gte(v4AmountOutMin)
expect(daiBalanceBefore.sub(daiBalanceAfter)).to.be.eq(v3AmountIn)
expect(await usdcContract.balanceOf(router.address)).to.be.eq(0)
})

it('V2, then V4', async () => {
// DAI -v2-> USDC -v4-> WETH
const v2Tokens = [DAI.address, USDC.address]
const v2AmountIn: BigNumber = expandTo18DecimalsBN(1234)
const v2AmountOutMin = 0
const v4AmountOutMin = expandTo18DecimalsBN(1234 / (USD_ETH_PRICE * 1.01))

planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [
ADDRESS_THIS, // the router is the recipient of the v3 trade
v2AmountIn,
v2AmountOutMin,
v2Tokens,
SOURCE_MSG_SENDER, // the user pays for the input of the v3 trade
])

// prep the v4 swap
// settle USDC that are in the router, into v4, to get a positive delta
v4Planner.addAction(Actions.SETTLE, [USDC.address, CONTRACT_BALANCE, false])
v4Planner.addAction(Actions.SWAP_EXACT_IN, [
{
currencyIn: USDC.address,
path: encodeMultihopExactInPath([USDC_WETH.poolKey], USDC.address),
amountIn: OPEN_DELTA,
amountOutMinimum: v4AmountOutMin,
},
])
v4Planner.addAction(Actions.TAKE_ALL, [WETH.address, 0])

// add the v4 commands to the UR calldata
planner.addCommand(CommandType.V4_SWAP, [v4Planner.actions, v4Planner.params])

const { daiBalanceBefore, daiBalanceAfter, wethBalanceBefore, wethBalanceAfter } = await executeRouter(
planner,
bob,
router,
wethContract,
daiContract,
usdcContract
)

expect(wethBalanceAfter.sub(wethBalanceBefore)).to.be.gte(v4AmountOutMin)
expect(daiBalanceBefore.sub(daiBalanceAfter)).to.be.eq(v2AmountIn)
expect(await usdcContract.balanceOf(router.address)).to.be.eq(0)
})

it('V4, then V3', async () => {
// DAI -v4-> USDC -v3-> WETH
const v4AmountIn: BigNumber = expandTo18DecimalsBN(1234)
const v4AmountOutMin = 0
const v3Tokens = [USDC.address, WETH.address]
const v3AmountOutMin = expandTo18DecimalsBN(1234 / (USD_ETH_PRICE * 1.01))

v4Planner.addAction(Actions.SWAP_EXACT_IN_SINGLE, [
{
poolKey: DAI_USDC.poolKey,
zeroForOne: true,
amountIn: v4AmountIn,
amountOutMinimum: v4AmountOutMin,
sqrtPriceLimitX96: 0,
hookData: '0x',
},
])
v4Planner.addAction(Actions.SETTLE_ALL, [daiContract.address, MAX_UINT160])
// take all of the available tokens (open_delta), with the router (address(this)) as the recipient
v4Planner.addAction(Actions.TAKE, [usdcContract.address, ADDRESS_THIS, OPEN_DELTA])
planner.addCommand(CommandType.V4_SWAP, [v4Planner.actions, v4Planner.params])

planner.addCommand(CommandType.V3_SWAP_EXACT_IN, [
MSG_SENDER, // the recipient of the output is the caller
CONTRACT_BALANCE, // the router's balance is the input amount
v3AmountOutMin,
encodePathExactInput(v3Tokens),
SOURCE_ROUTER, // the USDC is in the router
])

const { daiBalanceBefore, daiBalanceAfter, wethBalanceBefore, wethBalanceAfter, v3SwapEventArgs } =
await executeRouter(planner, bob, router, wethContract, daiContract, usdcContract)
const { amount1: wethTraded } = v3SwapEventArgs!

expect(wethBalanceAfter.sub(wethBalanceBefore)).to.eq(wethTraded.mul(-1))
expect(wethTraded.mul(-1)).to.be.gte(v4AmountOutMin)
expect(daiBalanceBefore.sub(daiBalanceAfter)).to.be.eq(v4AmountIn)
expect(await usdcContract.balanceOf(router.address)).to.be.eq(0)
})

it('ETH into V4, then V3', async () => {
// ETH -v4-> USDC -v3-> DAI
const v4AmountIn: BigNumber = expandTo18DecimalsBN(1.2)
const v4AmountOutMin = 0
const v3Tokens = [USDC.address, DAI.address]
const v3AmountOutMin = expandTo18DecimalsBN(1.2 * USD_ETH_PRICE * 0.99)

v4Planner.addAction(Actions.SWAP_EXACT_IN_SINGLE, [
{
poolKey: ETH_USDC.poolKey,
zeroForOne: true,
amountIn: v4AmountIn,
amountOutMinimum: v4AmountOutMin,
sqrtPriceLimitX96: 0,
hookData: '0x',
},
])
v4Planner.addAction(Actions.SETTLE_ALL, [ETH_ADDRESS, MAX_UINT160])
// take all of the available tokens (open_delta), with the router (address(this)) as the recipient
v4Planner.addAction(Actions.TAKE, [usdcContract.address, ADDRESS_THIS, OPEN_DELTA])
planner.addCommand(CommandType.V4_SWAP, [v4Planner.actions, v4Planner.params])

planner.addCommand(CommandType.V3_SWAP_EXACT_IN, [
MSG_SENDER, // the recipient of the output is the caller
CONTRACT_BALANCE, // the router's balance is the input amount
v3AmountOutMin,
encodePathExactInput(v3Tokens),
SOURCE_ROUTER, // the USDC is in the router
])

const { daiBalanceBefore, daiBalanceAfter, ethBalanceBefore, ethBalanceAfter, gasSpent, v3SwapEventArgs } =
await executeRouter(planner, bob, router, wethContract, daiContract, usdcContract, v4AmountIn)
const { amount0: daiTraded } = v3SwapEventArgs!

expect(ethBalanceBefore.sub(ethBalanceAfter)).to.eq(v4AmountIn.add(gasSpent))
expect(daiBalanceAfter.sub(daiBalanceBefore)).to.be.eq(daiTraded.mul(-1))
expect(daiTraded.mul(-1)).to.be.gte(v3AmountOutMin)
})

it('ETH wrap, WETH into V4, then V3, ', async () => {
// ETH -> WETH -v4-> USDC -v3-> DAI
const v4AmountIn: BigNumber = expandTo18DecimalsBN(1.2)
const v4AmountOutMin = 0
const v3Tokens = [USDC.address, DAI.address]
const v3AmountOutMin = expandTo18DecimalsBN(1.2 * USD_ETH_PRICE * 0.99)

// wrap ETH into WETH
planner.addCommand(CommandType.WRAP_ETH, [ADDRESS_THIS, v4AmountIn])
// swap WETH to USDC
v4Planner.addAction(Actions.SWAP_EXACT_IN_SINGLE, [
{
poolKey: USDC_WETH.poolKey,
zeroForOne: false,
amountIn: v4AmountIn,
amountOutMinimum: v4AmountOutMin,
sqrtPriceLimitX96: 0,
hookData: '0x',
},
])
// settle the whole open WETH delta, where the payer is the router (payerIsUser = false)
v4Planner.addAction(Actions.SETTLE, [WETH.address, OPEN_DELTA, false])
// take all of the available tokens (open_delta), with the router (address(this)) as the recipient
v4Planner.addAction(Actions.TAKE, [usdcContract.address, ADDRESS_THIS, OPEN_DELTA])
planner.addCommand(CommandType.V4_SWAP, [v4Planner.actions, v4Planner.params])

planner.addCommand(CommandType.V3_SWAP_EXACT_IN, [
MSG_SENDER, // the recipient of the output is the caller
CONTRACT_BALANCE, // the router's balance is the input amount
v3AmountOutMin,
encodePathExactInput(v3Tokens),
SOURCE_ROUTER, // the USDC is in the router
])

const { daiBalanceBefore, daiBalanceAfter, ethBalanceBefore, ethBalanceAfter, gasSpent, v3SwapEventArgs } =
await executeRouter(planner, bob, router, wethContract, daiContract, usdcContract, v4AmountIn)
const { amount0: daiTraded } = v3SwapEventArgs!

expect(ethBalanceBefore.sub(ethBalanceAfter)).to.eq(v4AmountIn.add(gasSpent))
expect(daiBalanceAfter.sub(daiBalanceBefore)).to.be.eq(daiTraded.mul(-1))
expect(daiTraded.mul(-1)).to.be.gte(v3AmountOutMin)
})

it('V4, then V2', async () => {
// DAI -v4-> USDC -v2-> WETH
const v4AmountIn: BigNumber = expandTo18DecimalsBN(1234)
const v4AmountOutMin = 0
const v2Tokens = [USDC.address, WETH.address]
const v2AmountOutMin = expandTo18DecimalsBN(1234 / (USD_ETH_PRICE * 1.01))

v4Planner.addAction(Actions.SWAP_EXACT_IN_SINGLE, [
{
poolKey: DAI_USDC.poolKey,
zeroForOne: true,
amountIn: v4AmountIn,
amountOutMinimum: v4AmountOutMin,
sqrtPriceLimitX96: 0,
hookData: '0x',
},
])
v4Planner.addAction(Actions.SETTLE_ALL, [daiContract.address, MAX_UINT160])
// take all of the available tokens (open_delta), with the v2 pair as the recipient
v4Planner.addAction(Actions.TAKE, [usdcContract.address, Pair.getAddress(USDC, WETH), OPEN_DELTA])
planner.addCommand(CommandType.V4_SWAP, [v4Planner.actions, v4Planner.params])

planner.addCommand(CommandType.V2_SWAP_EXACT_IN, [MSG_SENDER, 0, v2AmountOutMin, v2Tokens, SOURCE_MSG_SENDER])

const { daiBalanceBefore, daiBalanceAfter, wethBalanceBefore, wethBalanceAfter, v2SwapEventArgs } =
await executeRouter(planner, bob, router, wethContract, daiContract, usdcContract)
const { amount1Out: wethTraded } = v2SwapEventArgs!

expect(wethBalanceAfter.sub(wethBalanceBefore)).to.eq(wethTraded)
expect(wethTraded).to.be.gte(v4AmountOutMin)
expect(daiBalanceBefore.sub(daiBalanceAfter)).to.be.eq(v4AmountIn)
expect(await usdcContract.balanceOf(router.address)).to.be.eq(0)
})
})

describe('Split routes', () => {
Expand Down
6 changes: 2 additions & 4 deletions test/integration-tests/UniswapV4.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { BigNumber } from 'ethers'
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 { resetFork, WETH, DAI, USDC, PERMIT2, USD_ETH_PRICE } from './shared/mainnetForkHelpers'
import {
ALICE_ADDRESS,
DEADLINE,
Expand Down Expand Up @@ -46,9 +46,6 @@ describe('Uniswap V4 Tests:', () => {
let v4PoolManager: PoolManager
let v4PositionManager: PositionManager

// current market ETH price at block
const USD_ETH_PRICE = 3820

// USD-pegged -> (W)NATIVE trades
// exact in trade
const amountIn = 1000
Expand Down Expand Up @@ -115,6 +112,7 @@ describe('Uniswap V4 Tests:', () => {
await permit2.approve(DAI.address, v4PositionManager.address, MAX_UINT160, DEADLINE)
await permit2.approve(WETH.address, v4PositionManager.address, MAX_UINT160, DEADLINE)
await permit2.approve(USDC.address, v4PositionManager.address, MAX_UINT160, DEADLINE)

// bob initializes 3 v4 pools
await initializeV4Pool(v4PoolManager, USDC_WETH.poolKey, USDC_WETH.price)
await initializeV4Pool(v4PoolManager, DAI_USDC.poolKey, DAI_USDC.price)
Expand Down
3 changes: 3 additions & 0 deletions test/integration-tests/shared/mainnetForkHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ export const GALA = new Token(1, '0x15D4c048F83bd7e37d49eA4C83a07267Ec4203dA', 8
export const SWAP_ROUTER_V2 = '0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45'
export const V2_FACTORY = 0x5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f

// current market ETH price at block 20010000
export const USD_ETH_PRICE = 3820

export const approveSwapRouter02 = async (
alice: SignerWithAddress,
currency: Currency,
Expand Down
Loading