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

Added slippage test #114

Merged
merged 6 commits into from
Jul 23, 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
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)
PrzemyslawKulej marked this conversation as resolved.
Show resolved Hide resolved
})
})
Loading