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

Add swapWithSlippage to SDK #106

Merged
merged 1 commit into from
Jul 18, 2024
Merged
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
51 changes: 48 additions & 3 deletions src/invariant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
WithdrawProtocolFee
} from '../artifacts/ts'
import { FeeTier, Pool, PoolKey, Position, QuoteResult, Tick } from '../artifacts/ts/types'
import { calculateTick } from './math'
import { calculateSqrtPriceAfterSlippage, calculateTick } from './math'
import { Network } from './network'
import { getReserveAddress } from './testUtils'
import {
Expand All @@ -36,7 +36,6 @@ import {
Address,
ALPH_TOKEN_ID,
DUST_AMOUNT,
ONE_ALPH,
SignerProvider,
TransactionBuilder
} from '@alephium/web3'
Expand Down Expand Up @@ -390,6 +389,52 @@ export class Invariant {
return await signAndSend(signer, tx)
}

async swapWithSlippageTx(
signer: SignerProvider,
poolKey: PoolKey,
xToY: boolean,
amount: bigint,
byAmountIn: boolean,
estimatedSqrtPrice: bigint,
slippage: bigint
) {
const sqrtPriceAfterSlippage = calculateSqrtPriceAfterSlippage(
estimatedSqrtPrice,
slippage,
!xToY
)

return this.swapTx(
signer,
poolKey,
xToY,
amount,
byAmountIn,
xToY ? sqrtPriceAfterSlippage - 1n : sqrtPriceAfterSlippage + 1n
)
}

async swapWithSlippage(
signer: SignerProvider,
poolKey: PoolKey,
xToY: boolean,
amount: bigint,
byAmountIn: boolean,
estimatedSqrtPrice: bigint,
slippage: bigint
): Promise<string> {
const tx = await this.swapWithSlippageTx(
signer,
poolKey,
xToY,
amount,
byAmountIn,
estimatedSqrtPrice,
slippage
)
return await signAndSend(signer, tx)
}

async feeTierExist(feeTier: FeeTier): Promise<boolean> {
return (await this.instance.view.feeTierExist({ args: { feeTier } })).returns
}
Expand Down Expand Up @@ -449,7 +494,7 @@ export class Invariant {
async getAllPoolKeys() {
return decodePoolKeys((await this.instance.view.getAllPoolKeys()).returns)
}
// async swapWithSlippage() {}

// async getPositionTicks() {}
// async getRawTickmap() {}
// async getFullTickmap() {}
Expand Down
58 changes: 58 additions & 0 deletions src/math.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { CLAMM, Utils } from '../artifacts/ts'
import { LiquidityResult, Pool, Position, SingleTokenLiquidity, Tick } from '../artifacts/ts/types'
import { LiquidityScale, PercentageScale, SqrtPriceScale } from './consts'

export const calculateSqrtPrice = async (tickIndex: bigint): Promise<bigint> => {
return (
Expand Down Expand Up @@ -204,3 +205,60 @@ export const calculateTokenAmounts = async (
})
).returns
}

const sqrt = (value: bigint): bigint => {
if (value < 0n) {
throw 'square root of negative numbers is not supported'
}

if (value < 2n) {
return value
}

return newtonIteration(value, 1n)
}

const newtonIteration = (n: bigint, x0: bigint): bigint => {
const x1 = (n / x0 + x0) >> 1n
if (x0 === x1 || x0 === x1 - 1n) {
return x0
}
return newtonIteration(n, x1)
}

export const sqrtPriceToPrice = (sqrtPrice: bigint): bigint => {
return (sqrtPrice * sqrtPrice) / toSqrtPrice(1n)
}

export const priceToSqrtPrice = (price: bigint): bigint => {
return sqrt(price * toSqrtPrice(1n))
}

export const calculateSqrtPriceAfterSlippage = (
sqrtPrice: bigint,
slippage: bigint,
up: boolean
): bigint => {
if (slippage === 0n) {
return sqrtPrice
}

const multiplier = toPercentage(1n) + (up ? slippage : -slippage)
const price = sqrtPriceToPrice(sqrtPrice)
const priceWithSlippage = price * multiplier * toPercentage(1n)
const sqrtPriceWithSlippage = priceToSqrtPrice(priceWithSlippage) / toPercentage(1n)

return sqrtPriceWithSlippage
}

export const toLiquidity = (value: bigint, offset = 0n) => {
return value * 10n ** (LiquidityScale - offset)
}

export const toSqrtPrice = (value: bigint, offset = 0n) => {
return value * 10n ** (SqrtPriceScale - offset)
}

export const toPercentage = (value: bigint, offset = 0n) => {
return value * 10n ** (PercentageScale - offset)
}
3 changes: 1 addition & 2 deletions src/snippets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,13 @@ import {
initPosition,
initSwap,
initTokensXY,
toLiquidity,
transferPosition,
verifyPositionList,
withdrawTokens
} from './testUtils'
import { balanceOf, deployInvariant, newFeeTier, newPoolKey } from './utils'
import { PrivateKeyWallet } from '@alephium/web3-wallet'
import { calculateSqrtPrice } from './math'
import { calculateSqrtPrice, toLiquidity } from './math'

type TokenInstance = TokenFaucetInstance

Expand Down
4 changes: 0 additions & 4 deletions src/testUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,3 @@ export const getReserveBalances = async (invariant: InvariantInstance, poolKey:
const y = await balanceOf(poolKey.tokenY, reserveY)
return { x, y }
}

export const toLiquidity = (value: bigint) => {
return value * 10n ** LiquidityScale
}
2 changes: 1 addition & 1 deletion test/cross.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ import {
initFeeTier,
initPosition,
initSwap,
toLiquidity,
withdrawTokens
} from '../src/testUtils'
import { toLiquidity } from '../src/math'

web3.setCurrentNodeProvider('http://127.0.0.1:22973')
let admin: PrivateKeyWallet
Expand Down
3 changes: 1 addition & 2 deletions test/cross_both_side.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@ import {
initFeeTier,
initPosition,
initSwap,
toLiquidity,
withdrawTokens
} from '../src/testUtils'
import { InvariantError, MaxSqrtPrice, MinSqrtPrice } from '../src/consts'
import { calculateSqrtPrice } from '../src/math'
import { calculateSqrtPrice, toLiquidity } from '../src/math'
import { InvariantInstance, TokenFaucetInstance } from '../artifacts/ts'

web3.setCurrentNodeProvider('http://127.0.0.1:22973')
Expand Down
4 changes: 2 additions & 2 deletions test/interaction_with_pool_on_removed_fee_tier.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ import {
removePosition,
getPosition,
expectError,
getReserveBalances,
toLiquidity
getReserveBalances
} from '../src/testUtils'
import {
ChangeFeeReceiver,
Expand All @@ -28,6 +27,7 @@ import {
WithdrawProtocolFee
} from '../artifacts/ts'
import { FeeTier, PoolKey } from '../artifacts/ts/types'
import { toLiquidity } from '../src/math'

web3.setCurrentNodeProvider('http://127.0.0.1:22973')
let admin: PrivateKeyWallet
Expand Down
1 change: 0 additions & 1 deletion test/limits.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
} from '../src/consts'
import {
getPool,
getReserveAddress,
getReserveBalances,
initFeeTier,
initPool,
Expand Down
2 changes: 1 addition & 1 deletion test/liquidity_gap.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ import {
initPosition,
initSwap,
initTokensXY,
toLiquidity,
quote,
withdrawTokens
} from '../src/testUtils'
import { FeeTier, PoolKey } from '../artifacts/ts/types'
import { toLiquidity } from '../src/math'

web3.setCurrentNodeProvider('http://127.0.0.1:22973')

Expand Down
83 changes: 82 additions & 1 deletion test/math.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ import { web3 } from '@alephium/web3'
import {
calculateFee,
calculateSqrtPrice,
calculateSqrtPriceAfterSlippage,
getLiquidity,
getLiquidityByX,
getLiquidityByY
getLiquidityByY,
toPercentage,
toSqrtPrice
} from '../src/math'
import { expectError } from '../src/testUtils'
import { UtilsError } from '../src/consts'
Expand Down Expand Up @@ -258,4 +261,82 @@ describe('math spec', () => {
expect(y).toBe(0n)
})
})
describe('test calculateSqrtPriceAfterSlippage', () => {
test('no slippage up', () => {
const sqrtPrice = toSqrtPrice(1n, 0n)
const slippage = toPercentage(0n, 0n)
const limitSqrt = calculateSqrtPriceAfterSlippage(sqrtPrice, slippage, true)
expect(limitSqrt).toBe(sqrtPrice)
})
test('no slippage down', () => {
const sqrtPrice = toSqrtPrice(1n, 0n)
const slippage = toPercentage(0n, 0n)
const limitSqrt = calculateSqrtPriceAfterSlippage(sqrtPrice, slippage, false)
expect(limitSqrt).toBe(sqrtPrice)
})
test('slippage of 1% up', () => {
const sqrtPrice = toSqrtPrice(1n, 0n)
const slippage = toPercentage(1n, 2n)
// sqrt(1) * sqrt(1 + 0.01) = 1.0049876
const expected = 1004987562112089027021926n
const limitSqrt = calculateSqrtPriceAfterSlippage(sqrtPrice, slippage, true)
expect(limitSqrt).toBe(expected)
})
test('slippage of 1% down', () => {
const sqrtPrice = toSqrtPrice(1n, 0n)
const slippage = toPercentage(1n, 2n)
// sqrt(1) * sqrt(1 - 0.01) = 0.99498744
const expected = 994987437106619954734479n
const limitSqrt = calculateSqrtPriceAfterSlippage(sqrtPrice, slippage, false)
expect(limitSqrt).toBe(expected)
})
test('slippage of 0.5% up', () => {
const sqrtPrice = toSqrtPrice(1n, 0n)
const slippage = toPercentage(5n, 3n)
// sqrt(1) * sqrt(1 - 0.005) = 1.00249688
const expected = 1002496882788171067537936n
const limitSqrt = calculateSqrtPriceAfterSlippage(sqrtPrice, slippage, true)
expect(limitSqrt).toBe(expected)
})
test('slippage of 0.5% down', () => {
const sqrtPrice = toSqrtPrice(1n, 0n)
const slippage = toPercentage(5n, 3n)
// sqrt(1) * sqrt(1 - 0.005) = 0.997496867
const expected = 997496867163000166582694n
const limitSqrt = calculateSqrtPriceAfterSlippage(sqrtPrice, slippage, false)
expect(limitSqrt).toBe(expected)
})
test('slippage of 0.00003% up', () => {
const sqrtPrice = toSqrtPrice(1n, 0n)
const slippage = toPercentage(3n, 7n)
// sqrt(1) * sqrt(1 + 0.0000003) = 1.00000015
const expected = 1000000149999988750001687n
const limitSqrt = calculateSqrtPriceAfterSlippage(sqrtPrice, slippage, true)
expect(limitSqrt).toBe(expected)
})
test('slippage of 0.00003% down', () => {
const sqrtPrice = toSqrtPrice(1n, 0n)
const slippage = toPercentage(3n, 7n)
// sqrt(1) * sqrt(1 - 0.0000003) = 0.99999985
const expected = 999999849999988749998312n
const limitSqrt = calculateSqrtPriceAfterSlippage(sqrtPrice, slippage, false)
expect(limitSqrt).toBe(expected)
})
test('slippage of 100% up', () => {
const sqrtPrice = toSqrtPrice(1n, 0n)
const slippage = toPercentage(1n, 0n)
// sqrt(1) * sqrt(1 + 1) = 1.414213562373095048801688...
const expected = 1414213562373095048801688n
const limitSqrt = calculateSqrtPriceAfterSlippage(sqrtPrice, slippage, true)
expect(limitSqrt).toBe(expected)
})
test('slippage of 100% down', () => {
const sqrtPrice = toSqrtPrice(1n, 0n)
const slippage = toPercentage(1n, 0n)
// sqrt(1) * sqrt(1 - 1) = 0
const expected = 0n
const limitSqrt = calculateSqrtPriceAfterSlippage(sqrtPrice, slippage, false)
expect(limitSqrt).toBe(expected)
})
})
})
3 changes: 2 additions & 1 deletion test/pool-with-alph.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import { getSigner } from '@alephium/web3-test'
import { PrivateKeyWallet } from '@alephium/web3-wallet'
import { balanceOf, newFeeTier, newPoolKey } from '../src/utils'
import { MinSqrtPrice, PercentageScale } from '../src/consts'
import { initTokensXY, toLiquidity, withdrawTokens } from '../src/testUtils'
import { initTokensXY, withdrawTokens } from '../src/testUtils'
import { TokenFaucetInstance } from '../artifacts/ts'
import { FeeTier, PoolKey } from '../artifacts/ts/types'
import { Invariant } from '../src/invariant'
import { Network } from '../src/network'
import { getBasicFeeTickSpacing } from '../src/snippets'
import { toLiquidity } from '../src/math'

web3.setCurrentNodeProvider('http://127.0.0.1:22973')

Expand Down
2 changes: 1 addition & 1 deletion test/position.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ import {
initSwap,
initTokensXY,
isTickInitialized,
toLiquidity,
removePosition,
withdrawTokens
} from '../src/testUtils'
import { InvariantError, MaxSqrtPrice, MinSqrtPrice, PercentageScale } from '../src/consts'
import { toLiquidity } from '../src/math'

web3.setCurrentNodeProvider('http://127.0.0.1:22973')

Expand Down
3 changes: 1 addition & 2 deletions test/position_list.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,12 @@ import {
initPosition,
initTokensXY,
isTickInitialized,
toLiquidity,
removePosition,
transferPosition,
verifyPositionList,
withdrawTokens
} from '../src/testUtils'
import { calculateSqrtPrice } from '../src/math'
import { calculateSqrtPrice, toLiquidity } from '../src/math'
import { InvariantError, MaxSqrtPrice, PercentageScale } from '../src/consts'
import { deployInvariant, newFeeTier, newPoolKey } from '../src/utils'
import { InvariantInstance, TokenFaucetInstance } from '../artifacts/ts'
Expand Down
3 changes: 1 addition & 2 deletions test/position_slippage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@ import {
initPool,
initPosition,
initTokensXY,
toLiquidity,
withdrawTokens
} from '../src/testUtils'
import { calculateSqrtPrice } from '../src/math'
import { calculateSqrtPrice, toLiquidity } from '../src/math'
import { InvariantError } from '../src/consts'
import { InvariantInstance, TokenFaucetInstance } from '../artifacts/ts'

Expand Down
2 changes: 1 addition & 1 deletion test/swap.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ import {
initPosition,
initSwap,
initTokensXY,
toLiquidity,
quote,
withdrawTokens
} from '../src/testUtils'
import { InvariantError, MaxSqrtPrice, MinSqrtPrice, PercentageScale, VMError } from '../src/consts'
import { toLiquidity } from '../src/math'

web3.setCurrentNodeProvider('http://127.0.0.1:22973')
let admin: PrivateKeyWallet
Expand Down
Loading