Skip to content

Commit

Permalink
Merge pull request #114 from invariant-labs/slippage-test-new
Browse files Browse the repository at this point in the history
Added slippage test
  • Loading branch information
PrzemyslawKulej authored Jul 23, 2024
2 parents 0f19207 + 2cf75f1 commit c6c211d
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 16 deletions.
8 changes: 5 additions & 3 deletions contracts/collections/tickmap.ral
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ Abstract Contract Tickmap() extends Decimal(), BatchHelper() {
}

@using(checkExternalCaller = false)

pub fn getCloserLimit(sqrtPriceLimit: U256, xToY: Bool, currentTick: I256, tickSpacing: U256, poolKey: PoolKey) -> (U256, Bool, I256, Bool) {
let mut closesTickBool = false
let mut closesTickIndex = 0i
Expand All @@ -184,11 +185,10 @@ Abstract Contract Tickmap() extends Decimal(), BatchHelper() {

if (closesTickBool) {
let sqrtPriceExist = calculateSqrtPrice(closesTickIndex)

if ((xToY && sqrtPriceExist > sqrtPriceLimit) || (!xToY && sqrtPriceExist < sqrtPriceLimit)) {
return sqrtPriceExist, true, closesTickIndex, true
} else {
return sqrtPriceExist, false, 0i, false
return sqrtPriceLimit, false, 0i, false
}
} else {
let index = getSearchLimit(currentTick, tickSpacing, !xToY)
Expand All @@ -199,11 +199,13 @@ Abstract Contract Tickmap() extends Decimal(), BatchHelper() {
if ((xToY && sqrtPriceNotExist > sqrtPriceLimit) || (!xToY && sqrtPriceNotExist < sqrtPriceLimit)) {
return sqrtPriceNotExist, true, index, false
} else {
return sqrtPriceNotExist, false, 0i, false
return sqrtPriceLimit, false, 0i, false
}
}
}



fn getChunk(chunk: U256, poolKey: PoolKey) -> U256 {
let key = poolKeyBytes(poolKey) ++ toByteVec!(getKey(chunk))
let exists = bitmap.contains!(key)
Expand Down
25 changes: 23 additions & 2 deletions src/snippets.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Address, SignerProvider } from '@alephium/web3'
import { InvariantInstance, TokenFaucetInstance } from '../artifacts/ts'
import { MinSqrtPrice, PercentageScale } from './consts'
import { MinSqrtPrice, MaxSqrtPrice, PercentageScale } from './consts'
import { PoolKey } from '../artifacts/ts/types'
import {
getPool,
getReserveBalances,
Expand All @@ -11,7 +12,8 @@ import {
initTokensXY,
transferPosition,
verifyPositionList,
withdrawTokens
withdrawTokens,
quote
} from './testUtils'
import { balanceOf, deployInvariant, newFeeTier, newPoolKey } from './utils'
import { PrivateKeyWallet } from '@alephium/web3-wallet'
Expand Down Expand Up @@ -135,6 +137,25 @@ export const initBasicSwap = async (
return tx
}

export const swapExactLimit = async (
invariant: InvariantInstance,
signer: SignerProvider,
poolKey: PoolKey,
xToY: boolean,
amount: bigint,
byAmountIn: boolean
) => {
const sqrtPriceLimit: bigint = xToY ? MinSqrtPrice : MaxSqrtPrice

const quoteResult = await quote(invariant, poolKey, xToY, amount, byAmountIn, sqrtPriceLimit)

await initSwap(invariant, signer, poolKey, xToY, amount, byAmountIn, quoteResult.targetSqrtPrice)

const poolAfter = await getPool(invariant, poolKey)

expect(poolAfter.sqrtPrice).toBe(quoteResult.targetSqrtPrice)
}

export const transferAndVerifyPosition = async (
invariant: InvariantInstance,
owner: PrivateKeyWallet,
Expand Down
20 changes: 9 additions & 11 deletions test/contract/e2e/max-tick-cross.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ describe('max tick cross spec', () => {

test('max tick cross swap xToY and ByAmountIn, no liquidity gap between positions', async () => {
const lastInitializedTick = -250n
const amount = 40300n
const amount = 40282n
const xToY = true
const slippage = MinSqrtPrice
const byAmountIn = true
Expand Down Expand Up @@ -95,7 +95,7 @@ describe('max tick cross spec', () => {
}, 100000)
test('max tick cross swap yToX and ByAmountIn, no liquidity gap between positions', async () => {
const lastInitializedTick = 120n
const amount = 45000n
const amount = 44998n
const xToY = false
const slippage = MaxSqrtPrice
const byAmountIn = true
Expand All @@ -120,9 +120,10 @@ describe('max tick cross spec', () => {

await withdrawTokens(swapper, [tokenY, amount])

const poolBefore = await getPool(invariant, poolKey)

const { targetSqrtPrice } = await quote(invariant, poolKey, xToY, amount, byAmountIn, slippage)

const poolBefore = await getPool(invariant, poolKey)
const { gasAmount } = await initSwap(
invariant,
swapper,
Expand Down Expand Up @@ -229,7 +230,7 @@ describe('max tick cross spec', () => {
}, 100000)
test('max tick cross swap xToY and ByAmountIn, positions between search limit range', async () => {
const lastInitializedTick = -35000n
const amount = 13570000n
const amount = 13569916n
const xToY = true
const slippage = MinSqrtPrice
const byAmountIn = true
Expand All @@ -254,17 +255,16 @@ describe('max tick cross spec', () => {

await withdrawTokens(swapper, [tokenX, amount])

const { targetSqrtPrice } = await quote(invariant, poolKey, xToY, amount, byAmountIn, slippage)

const poolBefore = await getPool(invariant, poolKey)

const { gasAmount } = await initSwap(
invariant,
swapper,
poolKey,
xToY,
amount,
byAmountIn,
targetSqrtPrice
slippage
)
const poolAfter = await getPool(invariant, poolKey)
const crosses = (poolAfter.currentTickIndex - poolBefore.currentTickIndex) / -searchLimit
Expand All @@ -273,7 +273,7 @@ describe('max tick cross spec', () => {
}, 100000)
test('max tick cross swap yToX and ByAmountIn, positions between search limit range', async () => {
const lastInitializedTick = 25000n
const amount = 17947900n
const amount = 17947500n
const xToY = false
const slippage = MaxSqrtPrice
const byAmountIn = true
Expand All @@ -298,8 +298,6 @@ describe('max tick cross spec', () => {

await withdrawTokens(swapper, [tokenY, amount])

const { targetSqrtPrice } = await quote(invariant, poolKey, xToY, amount, byAmountIn, slippage)

const poolBefore = await getPool(invariant, poolKey)
const { gasAmount } = await initSwap(
invariant,
Expand All @@ -308,7 +306,7 @@ describe('max tick cross spec', () => {
xToY,
amount,
byAmountIn,
targetSqrtPrice
slippage
)

const poolAfter = await getPool(invariant, poolKey)
Expand Down
146 changes: 146 additions & 0 deletions test/contract/e2e/slippage.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import { ONE_ALPH, web3 } from '@alephium/web3'
import { getSigner } from '@alephium/web3-test'
import { PrivateKeyWallet } from '@alephium/web3-wallet'
import { TokenFaucetInstance } from '../../../artifacts/ts/'
import { InvariantInstance } from '../../../artifacts/ts'
import { deployInvariant, newFeeTier, newPoolKey } from '../../../src/utils'
import {
getBasicFeeTickSpacing,
initBasicPool,
initBasicPosition,
swapExactLimit
} from '../../../src/snippets'
import {
initFeeTier,
initSwap,
quote,
expectError,
withdrawTokens,
initPosition,
getPool,
initTokensXY,
initPool
} from '../../../src/testUtils'
import { InvariantError, MaxSqrtPrice } from '../../../src/consts'
import { calculateSqrtPrice, toLiquidity } from '../../../src/math'
import { PoolKey } from '../../../artifacts/ts/types'

web3.setCurrentNodeProvider('http://127.0.0.1:22973')
let admin: PrivateKeyWallet
let positionOwner: PrivateKeyWallet
let invariant: InvariantInstance
let tokenX: TokenFaucetInstance
let tokenY: TokenFaucetInstance
let poolKey: PoolKey

describe('Invariant Swap Tests', () => {
const swapAmount = 10n ** 8n
const [fee, tickSpacing] = getBasicFeeTickSpacing()

const withdrawAmount = 10n ** 10n

beforeAll(async () => {
admin = await getSigner(ONE_ALPH * 1000n, 0)
})

beforeEach(async () => {
invariant = await deployInvariant(admin, 10n ** 10n)
const feeTier = await newFeeTier(fee, tickSpacing)
await initFeeTier(invariant, admin, feeTier)

const tokenSupply = 10n ** 23n
;[tokenX, tokenY] = await initTokensXY(admin, tokenSupply)
positionOwner = await getSigner(ONE_ALPH * 1000n, 0)
await withdrawTokens(positionOwner, [tokenX, withdrawAmount], [tokenY, withdrawAmount])

const initTick = 0n
const initSqrtPrice = await calculateSqrtPrice(initTick)

poolKey = await newPoolKey(tokenX.address, tokenY.address, feeTier)
await initPool(invariant, positionOwner, tokenX, tokenY, feeTier, initSqrtPrice, initTick)

const [lowerTick, upperTick] = [-1000n, 1000n]

const liquidityDelta = toLiquidity(10_000_000_000n)

const poolBefore = await getPool(invariant, poolKey)

const slippageLimitLower = poolBefore.sqrtPrice
const slippageLimitUpper = poolBefore.sqrtPrice

await initPosition(
invariant,
positionOwner,
poolKey,
withdrawAmount,
withdrawAmount,
lowerTick,
upperTick,
liquidityDelta,
slippageLimitLower,
slippageLimitUpper
)

expect(await getPool(invariant, poolKey)).toMatchObject({
liquidity: liquidityDelta,
poolKey,
currentTickIndex: 0n
})
})

test('test_basic_slippage', async () => {
const swapper = await getSigner(ONE_ALPH * 1000n, 0)

const swapAmount = 10n ** 8n
await withdrawTokens(swapper, [tokenY, swapAmount])

const targetSqrtPrice = 1009940000000000000000001n
await initSwap(invariant, swapper, poolKey, false, swapAmount, true, targetSqrtPrice)

let expectedSqrtPrice = 1009940000000000000000000n

const pool = await getPool(invariant, poolKey)

expect(pool.sqrtPrice).toBe(expectedSqrtPrice)
})

test('test_swap_close_to_limit', async () => {
const feeTier = await newFeeTier(fee, tickSpacing)

const swapper = await getSigner(ONE_ALPH * 1000n, 0)
await withdrawTokens(swapper, [tokenX, withdrawAmount], [tokenY, withdrawAmount])
const poolKey = await newPoolKey(tokenX.address, tokenY.address, feeTier)

const quoteResult = await quote(invariant, poolKey, false, swapAmount, true, MaxSqrtPrice)

const targetSqrtPrice = quoteResult.targetSqrtPrice - 1n

await expectError(
InvariantError.PriceLimitReached,
initSwap(invariant, positionOwner, poolKey, false, swapAmount, true, targetSqrtPrice),
invariant
)
})

test('test_swap_exact_limit', async () => {
invariant = await deployInvariant(admin, 10n ** 10n)
const tokenSupply = 10n ** 23n
;[tokenX, tokenY] = await initTokensXY(admin, tokenSupply)

const feeTier = await newFeeTier(fee, tickSpacing)
await initFeeTier(invariant, admin, feeTier)

await initBasicPool(invariant, admin, tokenX, tokenY)

await initBasicPosition(invariant, positionOwner, tokenX, tokenY)

const swapAmount = 1000n
const swapper = await getSigner(ONE_ALPH * 1000n, 0)

await withdrawTokens(swapper, [tokenX, swapAmount])

const poolKey = await newPoolKey(tokenX.address, tokenY.address, feeTier)

await swapExactLimit(invariant, swapper, poolKey, true, swapAmount, true)
})
})

0 comments on commit c6c211d

Please sign in to comment.