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

feat(v4-sdk): Create and Add Implementation #98

Merged
merged 32 commits into from
Sep 23, 2024

Conversation

snreynolds
Copy link
Member

@snreynolds snreynolds commented Sep 16, 2024

PR Scope

Please title your PR according to the following types and scopes following conventional commits:

  • fix(SDK name): will trigger a patch version
  • chore(<type>): will not trigger any release and should be used for internal repo changes
  • <type>(public): will trigger a patch version for non-code changes (e.g. README changes)
  • feat(SDK name): will trigger a minor version
  • feat(breaking): will trigger a major version for a breaking change

Description

This PR will just implement createCallParameters and addCallParameters to reduce the size of the PR. All permit2 forwarding is also out of scope in this PR.

Note that @zhongeric has started the impl for remove and collect but I will be removing them from this PR and finalizing those implementations in a different PR.

How Has This Been Tested?

[e.g. Manually, E2E tests, unit tests, Storybook]

Are there any breaking changes?

[e.g. Type definitions, API definitions]

If there are breaking changes, please ensure you bump the major version Bump the major version (by using the title feat(breaking): ...), post a notice in #eng-sdks, and explicitly notify all Uniswap Labs consumers of the SDK.

(Optional) Feedback Focus

[Specific parts of this PR you'd like feedback on, or that reviewers should pay closer attention to]

(Optional) Follow Ups

[Things that weren't addressed in this PR, ways you plan to build on this work, or other ways this work could be extended]

import { ADDRESS_ZERO } from '../internalConstants'

// Uniswap v4 supports native pools. Those currencies are represented by the zero address.
// TODO: Figure out if this is how we should be handling weird edge case tokens like CELO/Polygon/etc..
Copy link
Member Author

Choose a reason for hiding this comment

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

I believe this has been resolved and that any Currency's isNative value will be set according to how it should be "treated" in the interface

@snreynolds snreynolds marked this pull request as ready for review September 18, 2024 23:49
@snreynolds snreynolds requested a review from a team as a code owner September 18, 2024 23:49
@snreynolds snreynolds requested a review from codyborn September 18, 2024 23:49
Copy link

graphite-app bot commented Sep 18, 2024

Graphite Automations

"Request reviewers once CI passes on sdks monorepo" took an action on this PR • (09/18/24)

1 reviewer was added and 1 assignee was added to this PR based on 's automation.

}

/**
* Returns the amount of token0 that this position's liquidity could be burned for at the current pool price
*/
public get amount0(): CurrencyAmount<Currency> {
throw new Error('Not implemented')
if (this._token0Amount === null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

if (!this._token0Amount) will include the case it's undefined

Copy link
Member Author

Choose a reason for hiding this comment

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

oh got it cool! updated. out of curiousity, could it ever be set to undefined instead of null ?

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh no, you're right.. I see it being initialized to null.

this.pool.currency0,
SqrtPriceMath.getAmount0Delta(
TickMath.getSqrtRatioAtTick(this.tickLower),
TickMath.getSqrtRatioAtTick(this.tickUpper),
Copy link
Contributor

Choose a reason for hiding this comment

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

just double checking.. this should be tickLower and tickUpper if it's less than the tickLower?

Copy link
Member Author

@snreynolds snreynolds Sep 19, 2024

Choose a reason for hiding this comment

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

yep! good question.

So on L70 we check where the tickCurrent is.

If the tickCurrent is below the tickLower (tickCurrent < tickLower) that means the full range of liquidity is above the current price. I think about it like this in my head:


__________|______________|||||||__
          ^              ^     ^
__________tC____________tL_____tU

Anything below(to the left) of tC (tickCurrent) is going to be entirely in amount1 and anything above (to the right) of tickCurrent is going to be entirely in amount0.

Since this function wants to know what the amount0 is, then in this case we need to query for the amount between the tickLower and the tickUpper. Hopefully that makes sense!

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think I fully understand. If amount0 is everything to the right of tC, wouldn't it be between tC and tL?

Copy link
Member Author

@snreynolds snreynolds Sep 20, 2024

Choose a reason for hiding this comment

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

yeah so everything to the right of tickCurrent is ALL of amount0 on the pool, since we are just working with a specific position (who is defined from tL to tU) we just care about the amount0 between tL and tU.

Copy link
Member Author

Choose a reason for hiding this comment

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

if we wanted to know amount1 (where the state of the pool is the same in the above graph.. ie the position is entirely above the current tick) then amount1 = 0 since the position does not touch any of amount1. And you can verify this logic in the amount1() function call

@snreynolds snreynolds requested a review from codyborn September 20, 2024 02:13
Copy link
Contributor

@Ayoakala Ayoakala left a comment

Choose a reason for hiding this comment

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

Looks great so far and thanks for cranking this out! Left a few comments and asks but the SDK contract looks very similar to V3 which is helpful

sdks/v4-sdk/src/PositionManager.ts Show resolved Hide resolved
let nativeCurrency: Currency = position.pool.currency0.isNative
? position.pool.currency0
: position.pool.currency1
value = position.pool.currency0.isNative ? toHex(amount0Max) : toHex(amount1Max)
Copy link
Contributor

Choose a reason for hiding this comment

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

Wanted to confirm by comparing to V3. Seems we are choosing the upper bound of slippage of the tokens rather than the inputed amount when 1 of the tokens is Native, is this desired?

Copy link
Member Author

Choose a reason for hiding this comment

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

what do you mean rather than inputtedAmount? Can you link me to the line in v3?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yea was referring to this

We use amountDesired (inputtedAmount) instead of amountMax

Copy link
Contributor

Choose a reason for hiding this comment

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

I think this is desired behavior because unlike in v3 we bound increasing positions by the maximum tokens required, so we need to send the MAX, and sweep any excess back to the user. This allows the price to slip "up" to your slippage.

sdks/v4-sdk/src/PositionManager.ts Show resolved Hide resolved
@@ -0,0 +1,524 @@
// TODO: import this from npm
export const abi = [
Copy link
Contributor

@Ayoakala Ayoakala Sep 20, 2024

Choose a reason for hiding this comment

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

Ahh is this ABI currently on Sepolia?

Copy link
Member Author

Choose a reason for hiding this comment

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

i think this is the most up to date abi yes, but let me know if you see discrepancies!

sdks/v4-sdk/src/utils/priceTickConversions.ts Show resolved Hide resolved
sdks/v4-sdk/src/utils/priceTickConversions.ts Show resolved Hide resolved
return calldatas.length === 1 ? calldatas[0] : Multicall.INTERFACE.encodeFunctionData('multicall', [calldatas])
return calldataList.length === 1
? calldataList[0]
: Multicall.INTERFACE.encodeFunctionData('multicall', [calldataList])
Copy link
Contributor

Choose a reason for hiding this comment

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

Since calldataList is already an array, do we need to wrap it again in an array?

Copy link
Contributor

Choose a reason for hiding this comment

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

because the mutlicall signature requires [], and the underlying functions being called in periphery require a list of actions (calldataList)

Copy link
Contributor

@codyborn codyborn left a comment

Choose a reason for hiding this comment

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

Looks good! Left small comments

Copy link
Contributor

@codyborn codyborn left a comment

Choose a reason for hiding this comment

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

🚢

@zhongeric zhongeric merged commit db85bdd into main Sep 23, 2024
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants