Skip to content

feat(ur-sdk): migrating out of range -> in range or out of range -> opposite side out of range #277

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

Open
wants to merge 18 commits into
base: more-migrating-params
Choose a base branch
from
Open
51 changes: 41 additions & 10 deletions sdks/universal-router-sdk/src/swapRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
PoolKey,
} from '@uniswap/v4-sdk'
import { Trade as RouterTrade } from '@uniswap/router-sdk'
import { Currency, TradeType, Percent, CHAIN_TO_ADDRESSES_MAP, SupportedChainsType } from '@uniswap/sdk-core'
import { Currency, TradeType, Percent, CHAIN_TO_ADDRESSES_MAP, SupportedChainsType, Token } from '@uniswap/sdk-core'
import { UniswapTrade, SwapOptions } from './entities/actions/uniswap'
import { RoutePlanner, CommandType } from './utils/routerCommands'
import { encodePermit, encodeV3PositionPermit } from './utils/inputTokens'
Expand All @@ -36,8 +36,8 @@ export interface MigrateV3ToV4Options {
v4AddLiquidityOptions: V4AddLiquidityOptions
}

function isMint(options: V4AddLiquidityOptions): options is MintOptions {
return Object.keys(options).some((k) => k === 'recipient')
function isMigrate(options: V4AddLiquidityOptions): options is MintOptions {
return 'recipient' in options && options.migrate === true
}

export abstract class SwapRouter {
Expand Down Expand Up @@ -87,6 +87,10 @@ export abstract class SwapRouter {
const v4PositionManagerAddress =
positionManagerOverride ?? CHAIN_TO_ADDRESSES_MAP[v4Pool.chainId as SupportedChainsType].v4PositionManagerAddress

const universalRouterAddress = UNIVERSAL_ROUTER_ADDRESS(
UniversalRouterVersion.V2_0,
options.inputPosition.pool.chainId as SupportedChainsType
)
// owner of the v3 nft must be the receiver of the v4 nft

// validate the parameters
Expand All @@ -110,8 +114,8 @@ export abstract class SwapRouter {
options.v3RemoveLiquidityOptions.collectOptions.recipient === v4PositionManagerAddress,
'RECIPIENT_NOT_POSITION_MANAGER'
)
invariant(isMint(options.v4AddLiquidityOptions), 'MINT_REQUIRED')
invariant(options.v4AddLiquidityOptions.migrate, 'MIGRATE_REQUIRED')
// Migration must be a mint operation, not an increase because the UR should not have permission to increase liquidity on a v4 position
invariant(isMigrate(options.v4AddLiquidityOptions), 'MIGRATE_REQUIRED')

const planner = new RoutePlanner()

Expand All @@ -132,10 +136,6 @@ export abstract class SwapRouter {
// add position permit to the universal router planner
if (options.v3RemoveLiquidityOptions.permit) {
// permit spender should be UR
const universalRouterAddress = UNIVERSAL_ROUTER_ADDRESS(
UniversalRouterVersion.V2_0,
options.inputPosition.pool.chainId as SupportedChainsType
)
invariant(universalRouterAddress == options.v3RemoveLiquidityOptions.permit.spender, 'INVALID_SPENDER')
// don't need to transfer it because v3posm uses isApprovedOrOwner()
encodeV3PositionPermit(planner, options.v3RemoveLiquidityOptions.permit, options.v3RemoveLiquidityOptions.tokenId)
Expand All @@ -162,6 +162,31 @@ export abstract class SwapRouter {
planner.addCommand(CommandType.V3_POSITION_MANAGER_CALL, [v3Call])
}

// if migrate options has a currency, require a batch permit
if (
options.v4AddLiquidityOptions.currencyAmount &&
options.v4AddLiquidityOptions.currencyAmount.inputCurrency instanceof Token
) {
// need to permit the UR to spend your currency on permit2
if (options.v4AddLiquidityOptions.batchPermit) {
invariant(
options.v4AddLiquidityOptions.batchPermit.permitBatch.spender == universalRouterAddress,
'INVALID_SPENDER'
)
planner.addCommand(CommandType.PERMIT2_PERMIT_BATCH, [
Copy link
Contributor

Choose a reason for hiding this comment

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

its only ever going to be 1 currency i think? so using batch is unnecessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

permitBatch is already a param of v4AddLiquidityOptions so i figured i would just keep it instead of adding another one?

options.v4AddLiquidityOptions.batchPermit.permitBatch,
options.v4AddLiquidityOptions.batchPermit.signature,
])
}
planner.addCommand(CommandType.PERMIT2_TRANSFER_FROM, [
options.v4AddLiquidityOptions.currencyAmount.inputCurrency.address,
options.v3RemoveLiquidityOptions.collectOptions.recipient,
options.v4AddLiquidityOptions.currencyAmount.inputAmount,
])
// remove batchPermit so it doesn't get encoded again later
delete options.v4AddLiquidityOptions.batchPermit
}

// encode v4 mint
const v4AddParams = V4PositionManager.addCallParameters(options.outputPosition, options.v4AddLiquidityOptions)
// only modifyLiquidities can be called by the UniversalRouter
Expand All @@ -170,7 +195,13 @@ export abstract class SwapRouter {

planner.addCommand(CommandType.V4_POSITION_MANAGER_CALL, [v4AddParams.calldata])

return SwapRouter.encodePlan(planner, BigNumber.from(0), {
let nativeCurrencyValue = BigNumber.from(0)

if (options.v4AddLiquidityOptions.currencyAmount?.inputCurrency?.isNative) {
nativeCurrencyValue = BigNumber.from(options.v4AddLiquidityOptions.currencyAmount?.inputAmount)
}

return SwapRouter.encodePlan(planner, nativeCurrencyValue, {
deadline: BigNumber.from(options.v4AddLiquidityOptions.deadline),
})
}
Expand Down
Loading
Loading