Skip to content

Commit

Permalink
Merge pull request #18 from uniswapfoundation/feat/modifying-positions
Browse files Browse the repository at this point in the history
feat: new version of modifying positions
  • Loading branch information
Florian-S-A-W authored Dec 18, 2023
2 parents c123fc9 + e7db940 commit ddbe51b
Show file tree
Hide file tree
Showing 13 changed files with 2,686 additions and 2,791 deletions.
6 changes: 6 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"semi": false,
"singleQuote": true,
"printWidth": 80,
"bracketSameLine": true
}
7 changes: 2 additions & 5 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,5 @@
"editor.codeActionsOnSave": {
"source.fixAll": true
},
"cSpell.words": [
"bips",
"exponentiate"
],
}
"cSpell.words": ["bips", "exponentiate"],
}
2 changes: 1 addition & 1 deletion v3-sdk/modifying-position/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,4 @@
}
]
}
}
}
2 changes: 1 addition & 1 deletion v3-sdk/modifying-position/.yarnrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
--frozen-lockfile true
--frozen-lockfile true
6 changes: 3 additions & 3 deletions v3-sdk/modifying-position/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
"@types/node": "^16.7.13",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"@uniswap/sdk-core": "^3.1.0",
"@uniswap/v3-sdk": "^3.9.0",
"@uniswap/sdk-core": "npm:@koraykoska/uniswap-sdk-core@^6.0.9",
"@uniswap/v3-sdk": "npm:@koraykoska/[email protected]",
"ethers": "^5.7.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand All @@ -19,7 +19,7 @@
"build": "react-scripts build",
"lint": "yarn eslint .",
"install:chain": "curl -L https://foundry.paradigm.xyz | bash && clear && echo $0 | exec && foundryup",
"start:chain": "anvil --chain-id 1337 --fork-url"
"start:chain": "anvil --chain-id 1 --fork-url"
},
"eslintConfig": {
"extends": [
Expand Down
4 changes: 2 additions & 2 deletions v3-sdk/modifying-position/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ export const CurrentConfig: ExampleConfig = {
'0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80',
},
tokens: {
token0: USDC_TOKEN,
token0: DAI_TOKEN,
token0Amount: 1000,
token1: DAI_TOKEN,
token1: USDC_TOKEN,
token1Amount: 1000,
poolFee: FeeAmount.LOW,
fractionToRemove: 1,
Expand Down
18 changes: 12 additions & 6 deletions v3-sdk/modifying-position/src/example/Example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,12 @@ const Example = () => {

return (
<div className="App">
{CurrentConfig.rpc.mainnet === '' && (
<h2 className="error">Please set your mainnet RPC URL in config.ts</h2>
)}
{CurrentConfig.rpc.mainnet === '' &&
CurrentConfig.env === Environment.MAINNET && (
<h2 className="error">
Please set your mainnet RPC URL in config.ts
</h2>
)}
{CurrentConfig.env === Environment.WALLET_EXTENSION &&
getProvider() === null && (
<h2 className="error">
Expand Down Expand Up @@ -133,7 +136,8 @@ const Example = () => {
disabled={
txState === TransactionState.Sending ||
getProvider() === null ||
CurrentConfig.rpc.mainnet === ''
(CurrentConfig.env === Environment.MAINNET &&
CurrentConfig.rpc.mainnet === '')
}>
<p>Mint Position</p>
</button>
Expand All @@ -145,7 +149,8 @@ const Example = () => {
disabled={
txState === TransactionState.Sending ||
getProvider() === null ||
CurrentConfig.rpc.mainnet === '' ||
(CurrentConfig.env === Environment.MAINNET &&
CurrentConfig.rpc.mainnet === '') ||
positionIds.length === 0
}>
<p>Add Liquidity to Position</p>
Expand All @@ -158,7 +163,8 @@ const Example = () => {
disabled={
txState === TransactionState.Sending ||
getProvider() === null ||
CurrentConfig.rpc.mainnet === '' ||
(CurrentConfig.env === Environment.MAINNET &&
CurrentConfig.rpc.mainnet === '') ||
positionIds.length === 0
}>
<p>Remove Liquidity from Position</p>
Expand Down
8 changes: 4 additions & 4 deletions v3-sdk/modifying-position/src/libs/constants.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// This file stores web3 related constants such as addresses, token definitions, ETH currency references and ABI's

import { SupportedChainId, Token } from '@uniswap/sdk-core'
import { Token, ChainId } from '@uniswap/sdk-core'

// Addresses

Expand All @@ -12,15 +12,15 @@ export const NONFUNGIBLE_POSITION_MANAGER_CONTRACT_ADDRESS =
// Currencies and Tokens

export const USDC_TOKEN = new Token(
SupportedChainId.MAINNET,
ChainId.MAINNET,
'0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
6,
'USDC',
'USD//C'
)

export const DAI_TOKEN = new Token(
SupportedChainId.MAINNET,
ChainId.MAINNET,
'0x6B175474E89094C44Da98b954EedeAC495271d0F',
18,
'DAI',
Expand All @@ -31,7 +31,7 @@ export const DAI_TOKEN = new Token(

export const MAX_FEE_PER_GAS = '100000000000'
export const MAX_PRIORITY_FEE_PER_GAS = '100000000000'
export const TOKEN_AMOUNT_TO_APPROVE_FOR_TRANSFER = 1000000000000
export const TOKEN_AMOUNT_TO_APPROVE_FOR_TRANSFER = '100000000000000000000000'

// ABI's

Expand Down
17 changes: 4 additions & 13 deletions v3-sdk/modifying-position/src/libs/conversion.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,13 @@
import JSBI from 'jsbi'

export function fromReadableAmount(amount: number, decimals: number): JSBI {
export function fromReadableAmount(amount: number, decimals: number): bigint {
const extraDigits = Math.pow(10, countDecimals(amount))
const adjustedAmount = amount * extraDigits
return JSBI.divide(
JSBI.multiply(
JSBI.BigInt(adjustedAmount),
JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(decimals))
),
JSBI.BigInt(extraDigits)
return (
(BigInt(adjustedAmount) * 10n ** BigInt(decimals)) / BigInt(extraDigits)
)
}

export function toReadableAmount(rawAmount: number, decimals: number): string {
return JSBI.divide(
JSBI.BigInt(rawAmount),
JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(decimals))
).toString()
return (BigInt(rawAmount) / 10n ** BigInt(decimals)).toString(10)
}

function countDecimals(x: number) {
Expand Down
135 changes: 41 additions & 94 deletions v3-sdk/modifying-position/src/libs/liquidity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,20 @@ import {
NONFUNGIBLE_POSITION_MANAGER_CONTRACT_ADDRESS,
} from './constants'
import { TOKEN_AMOUNT_TO_APPROVE_FOR_TRANSFER } from './constants'
import { sendTransaction, TransactionState } from './providers'
import { getWallet, sendTransaction, TransactionState } from './providers'
import {
Pool,
Position,
nearestUsableTick,
MintOptions,
NonfungiblePositionManager,
AddLiquidityOptions,
RemoveLiquidityOptions,
CollectOptions,
TickMath,
} from '@uniswap/v3-sdk'
import { CurrentConfig } from '../config'
import { getPoolInfo } from './pool'
import { getProvider, getWalletAddress } from './providers'
import { Percent, CurrencyAmount, Token } from '@uniswap/sdk-core'
import { Percent, CurrencyAmount, Token, Fraction } from '@uniswap/sdk-core'
import { fromReadableAmount } from './conversion'

export interface PositionInfo {
Expand All @@ -37,117 +36,69 @@ export interface PositionInfo {
export async function addLiquidity(
positionId: number
): Promise<TransactionState> {
const address = getWalletAddress()
const provider = getProvider()
if (!address || !provider) {
const wallet = getWallet()
if (!provider) {
return TransactionState.Failed
}

const positionToIncreaseBy = await constructPosition(
CurrencyAmount.fromRawAmount(
CurrentConfig.tokens.token0,
fromReadableAmount(
CurrentConfig.tokens.token0Amount * CurrentConfig.tokens.fractionToAdd,
CurrentConfig.tokens.token0.decimals
)
),
CurrencyAmount.fromRawAmount(
CurrentConfig.tokens.token1,
fromReadableAmount(
CurrentConfig.tokens.token1Amount * CurrentConfig.tokens.fractionToAdd,
CurrentConfig.tokens.token1.decimals
)
)
)

const addLiquidityOptions: AddLiquidityOptions = {
deadline: Math.floor(Date.now() / 1000) + 60 * 20,
slippageTolerance: new Percent(50, 10_000),
tokenId: positionId,
}

// get calldata for increasing a position
const { calldata, value } = NonfungiblePositionManager.addCallParameters(
positionToIncreaseBy,
addLiquidityOptions
)
const position = await Position.fetchWithPositionId(provider, positionId)

// build transaction
const transaction = {
data: calldata,
to: NONFUNGIBLE_POSITION_MANAGER_CONTRACT_ADDRESS,
value: value,
from: address,
maxFeePerGas: MAX_FEE_PER_GAS,
maxPriorityFeePerGas: MAX_PRIORITY_FEE_PER_GAS,
}
try {
await position.increasePositionByPercentageOnChain({
signer: wallet,
provider: provider,
percentage: new Fraction(CurrentConfig.tokens.fractionToAdd * 100, 100),
options: addLiquidityOptions,
})

return sendTransaction(transaction)
return TransactionState.Sent
} catch (err) {
return TransactionState.Failed
}
}

export async function removeLiquidity(
positionId: number
): Promise<TransactionState> {
const address = getWalletAddress()
const provider = getProvider()
if (!address || !provider) {
const wallet = getWallet()
if (!provider) {
return TransactionState.Failed
}

const currentPosition = await constructPosition(
CurrencyAmount.fromRawAmount(
CurrentConfig.tokens.token0,
fromReadableAmount(
CurrentConfig.tokens.token0Amount,
CurrentConfig.tokens.token0.decimals
)
),
CurrencyAmount.fromRawAmount(
CurrentConfig.tokens.token1,
fromReadableAmount(
CurrentConfig.tokens.token1Amount,
CurrentConfig.tokens.token1.decimals
)
)
)

const collectOptions: Omit<CollectOptions, 'tokenId'> = {
expectedCurrencyOwed0: CurrencyAmount.fromRawAmount(
CurrentConfig.tokens.token0,
0
),
expectedCurrencyOwed1: CurrencyAmount.fromRawAmount(
CurrentConfig.tokens.token1,
0
),
recipient: address,
}

const removeLiquidityOptions: RemoveLiquidityOptions = {
const decreaseLiquidityOptions: Omit<
RemoveLiquidityOptions,
'liquidityPercentage' | 'collectOptions'
> = {
deadline: Math.floor(Date.now() / 1000) + 60 * 20,
slippageTolerance: new Percent(50, 10_000),
tokenId: positionId,
// percentage of liquidity to remove
liquidityPercentage: new Percent(CurrentConfig.tokens.fractionToRemove),
collectOptions,
}
// get calldata for minting a position
const { calldata, value } = NonfungiblePositionManager.removeCallParameters(
currentPosition,
removeLiquidityOptions
)

// build transaction
const transaction = {
data: calldata,
to: NONFUNGIBLE_POSITION_MANAGER_CONTRACT_ADDRESS,
value: value,
from: address,
maxFeePerGas: MAX_FEE_PER_GAS,
maxPriorityFeePerGas: MAX_PRIORITY_FEE_PER_GAS,
}
const position = await Position.fetchWithPositionId(provider, positionId)

return sendTransaction(transaction)
try {
await position.decreasePositionByPercentageOnChain({
signer: wallet,
provider: provider,
percentage: new Fraction(
CurrentConfig.tokens.fractionToRemove * 100,
100
),
options: decreaseLiquidityOptions,
})

return TransactionState.Sent
} catch (err) {
return TransactionState.Failed
}
}

export async function getPositionIds(): Promise<number[]> {
Expand Down Expand Up @@ -255,12 +206,8 @@ export async function constructPosition(
// create position using the maximum liquidity from input amounts
return Position.fromAmounts({
pool: configuredPool,
tickLower:
nearestUsableTick(poolInfo.tick, poolInfo.tickSpacing) -
poolInfo.tickSpacing * 2,
tickUpper:
nearestUsableTick(poolInfo.tick, poolInfo.tickSpacing) +
poolInfo.tickSpacing * 2,
tickLower: TickMath.MIN_TICK + 2,
tickUpper: TickMath.MAX_TICK - 2,
amount0: token0Amount.quotient,
amount1: token1Amount.quotient,
useFullPrecision: true,
Expand Down
Loading

0 comments on commit ddbe51b

Please sign in to comment.