Skip to content

feat(v4-sdk): additional options when migrating #276

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 16 commits into
base: main
Choose a base branch
from
Open
43 changes: 41 additions & 2 deletions sdks/v4-sdk/src/PositionManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ describe('PositionManager', () => {
recipient,
slippageTolerance,
deadline,
migrate: true,
migrateOptions: { migrate: true },
})

// Rebuild the data with the planner for the expected mint. MUST sweep since we are using the native currency.
Expand Down Expand Up @@ -338,7 +338,7 @@ describe('PositionManager', () => {
recipient,
slippageTolerance,
deadline,
migrate: true,
migrateOptions: { migrate: true },
useNative: currency_native,
})

Expand Down Expand Up @@ -367,6 +367,45 @@ describe('PositionManager', () => {
expect(value).toEqual('0x00')
})

it('succeeds when migrating out of range and need to transfer eth', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

would love a test for non-eth too, and for ETH-WETH wrapping combos if they are to be supported too

const position: Position = new Position({
pool: pool_1_eth,
tickLower: TICK_SPACINGS[FeeAmount.MEDIUM],
tickUpper: TICK_SPACINGS[FeeAmount.MEDIUM] * 2,
liquidity: 1,
})
const { calldata, value } = V4PositionManager.addCallParameters(position, {
recipient,
slippageTolerance,
deadline,
migrateOptions: { migrate: true, neededCurrency: currency_native, neededAmount: 1 },
useNative: currency_native,
})

// Rebuild the data with the planner for the expected mint. MUST sweep since we are using the native currency.
const planner = new V4Planner()
const { amount0: amount0Max, amount1: amount1Max } = position.mintAmountsWithSlippage(slippageTolerance)
// Expect position to be minted correctly
planner.addAction(Actions.MINT_POSITION, [
pool_1_eth.poolKey,
TICK_SPACINGS[FeeAmount.MEDIUM],
TICK_SPACINGS[FeeAmount.MEDIUM] * 2,
1,
toHex(amount0Max),
toHex(amount1Max),
recipient,
EMPTY_BYTES,
])

planner.addAction(Actions.SETTLE, [toAddress(pool_1_eth.currency0), OPEN_DELTA, false])
planner.addAction(Actions.SETTLE, [toAddress(pool_1_eth.currency1), OPEN_DELTA, false])
planner.addAction(Actions.SWEEP, [toAddress(pool_1_eth.currency0), recipient])
planner.addAction(Actions.SWEEP, [toAddress(pool_1_eth.currency1), recipient])
expect(calldata).toEqual(V4PositionManager.encodeModifyLiquidities(planner.finalize(), deadline))

expect(value).toEqual('0x00')
})

it('succeeds for batchPermit', () => {
const position: Position = new Position({
pool: pool_0_1,
Expand Down
28 changes: 14 additions & 14 deletions sdks/v4-sdk/src/PositionManager.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BigintIsh, Percent, validateAndParseAddress, NativeCurrency } from '@uniswap/sdk-core'
import { BigintIsh, Percent, validateAndParseAddress, NativeCurrency, Currency } from '@uniswap/sdk-core'
import { TypedDataDomain, TypedDataField } from '@ethersproject/abstract-signer'
import JSBI from 'jsbi'
import { Position } from './entities/position'
Expand Down Expand Up @@ -62,9 +62,9 @@ export interface MintSpecificOptions {
sqrtPriceX96?: BigintIsh

/**
* Whether the mint is part of a migration from V3 to V4.
* Whether the mint is part of a migration from V3 to V4 and the additional currency and amount to send if needed
*/
migrate?: boolean
migrateOptions?: MigrateOptions
Copy link
Contributor

Choose a reason for hiding this comment

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

looks like we are pulling out migrate into its own object; might need to change trading api impl

}
Copy link
Contributor Author

@dianakocsis dianakocsis Feb 10, 2025

Choose a reason for hiding this comment

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

was thinking if we like this format, we could add the recipient field here too inside of MigrateOptions


/**
Expand Down Expand Up @@ -114,21 +114,19 @@ export interface CollectSpecificOptions {
recipient: string
}

export interface TransferOptions {
export interface MigrateOptions {
/**
* The account sending the NFT.
* Whether the mint is part of a migration from V3 to V4.
*/
sender: string

migrate?: boolean
/**
* The account that should receive the NFT.
* The additional currency that needs to be transferred if migrating from out of range to in range or out of range to opposite side out of range
*/
recipient: string

neededCurrency?: Currency
/**
* The id of the token being sent.
* The amount of additional currency that needs to be transferred if migrating from out of range to in range or out of range to opposite side out of range
*/
tokenId: BigintIsh
neededAmount?: BigintIsh
}

Copy link
Contributor

Choose a reason for hiding this comment

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

not too bad it seems on the Trading API side to send in; curious how to derive the amount, seems it wouldnt bee to tedious to compute from earlier convos

export interface PermitDetails {
Expand Down Expand Up @@ -284,9 +282,11 @@ export abstract class V4PositionManager {

let value: string = toHex(0)

let needToSendEth = isMint(options) && options.migrateOptions?.neededCurrency?.isNative

Copy link
Contributor

Choose a reason for hiding this comment

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

questions...

  1. does it need to check somewhere that neededCurrency is one of the 2 currencies in the position? it should revert if the neededCurrency isnt a valid currency
  2. if the position is WETH, can the neededCurrency be ETH? I would guess yes, in which case we need to make sure its wrapping
  3. if the position is ETH, can the needed currency be WETH?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i was thinking the trading api would calculate how much is needed of the currency in the new position

Copy link
Contributor Author

Choose a reason for hiding this comment

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

but i guess the sdk could handle all scenarios?

// If migrating, we need to settle and sweep both currencies individually
if (isMint(options) && options.migrate) {
if (options.useNative) {
if (isMint(options) && options.migrateOptions?.migrate) {
if (options.useNative && !needToSendEth) {
// unwrap the exact amount needed to send to the pool manager
Copy link
Contributor

Choose a reason for hiding this comment

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

in the case where neededCurrency is ETH and the position is in WETH this path should still be taken i think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think the opposite right? it would need to wrap not unwrap

planner.addUnwrap(OPEN_DELTA)
// payer is v4 position manager
Expand Down
Loading