From d202eaac6b03aedbc88a5ffe84a3c3e2aea8834b Mon Sep 17 00:00:00 2001 From: zielvna Date: Tue, 8 Oct 2024 13:51:24 +0200 Subject: [PATCH 1/5] add execute methods and create pool and position script --- contracts/scripts/invariant_tx.ral | 23 ++ src/invariant.ts | 284 +++++++++++++++++- .../e2e/create-pool-and-position.test.ts | 72 +++++ test/sdk/e2e/execute.test.ts | 178 +++++++++++ 4 files changed, 556 insertions(+), 1 deletion(-) create mode 100644 test/contract/e2e/create-pool-and-position.test.ts create mode 100644 test/sdk/e2e/execute.test.ts diff --git a/contracts/scripts/invariant_tx.ral b/contracts/scripts/invariant_tx.ral index fa3df90..bc95547 100644 --- a/contracts/scripts/invariant_tx.ral +++ b/contracts/scripts/invariant_tx.ral @@ -67,3 +67,26 @@ TxScript Swap(invariant: Invariant, poolKey: PoolKey, xToY: Bool, amount: TokenA let _ = invariant.swap{callerAddress!() -> swappedToken: swappedAmount}(poolKey, xToY, amount, byAmountIn, sqrtPriceLimit) } +TxScript CreatePoolAndPosition( + invariant: Invariant, + poolKey: PoolKey, + initSqrtPrice: SqrtPrice, + initTick: I256, + lowerTick: I256, + upperTick: I256, + liquidityDelta: Liquidity, + slippageLimitLower: SqrtPrice, + slippageLimitUpper: SqrtPrice +) { + invariant.createPool{callerAddress!() -> ALPH: 5 * mapEntryDeposit!() }(poolKey.tokenX, poolKey.tokenY, poolKey.feeTier, initSqrtPrice, initTick) + + let amountX = tokenRemaining!(callerAddress!(), poolKey.tokenX) + let amountY = tokenRemaining!(callerAddress!(), poolKey.tokenY) + if(poolKey.tokenX == ALPH) { + invariant.createPosition{callerAddress!() -> ALPH: amountX, poolKey.tokenY: amountY} + (poolKey, lowerTick, upperTick, liquidityDelta, slippageLimitLower, slippageLimitUpper) + } else { + invariant.createPosition{callerAddress!() -> poolKey.tokenX: amountX, poolKey.tokenY: amountY, ALPH: mapEntryDeposit!() * 6} + (poolKey, lowerTick, upperTick, liquidityDelta, slippageLimitLower, slippageLimitUpper) + } +} diff --git a/src/invariant.ts b/src/invariant.ts index fc2b455..2f71d88 100644 --- a/src/invariant.ts +++ b/src/invariant.ts @@ -13,7 +13,8 @@ import { Swap, TransferPosition, WithdrawProtocolFee, - CLAMM + CLAMM, + CreatePoolAndPosition } from '../artifacts/ts' import { PoolKey as _PoolKey } from '../artifacts/ts/types' import { @@ -167,6 +168,13 @@ export class Invariant { return await signAndSend(signer, tx) } + async addFeeTierExecute(signer: SignerProvider, feeTier: FeeTier) { + return await AddFeeTier.execute(signer, { + initialFields: { invariant: this.instance.contractId, feeTier: wrapFeeTier(feeTier) }, + attoAlphAmount: MAP_ENTRY_DEPOSIT + }) + } + async removeFeeTierTx(signer: SignerProvider, feeTier: FeeTier) { const builder = TransactionBuilder.from(web3.getCurrentNodeProvider()) const txBytecode = RemoveFeeTier.script.buildByteCodeToDeploy({ @@ -186,6 +194,12 @@ export class Invariant { return await signAndSend(signer, tx) } + async removeFeeTierExecute(signer: SignerProvider, feeTier: FeeTier) { + return await RemoveFeeTier.execute(signer, { + initialFields: { invariant: this.instance.contractId, feeTier: wrapFeeTier(feeTier) } + }) + } + async createPoolTx(signer: SignerProvider, poolKey: PoolKey, initSqrtPrice: SqrtPrice) { const initTick = calculateTick(initSqrtPrice, poolKey.feeTier.tickSpacing) const builder = TransactionBuilder.from(web3.getCurrentNodeProvider()) @@ -214,6 +228,20 @@ export class Invariant { return await signAndSend(signer, tx) } + async createPoolExecute(signer: SignerProvider, poolKey: PoolKey, initSqrtPrice: SqrtPrice) { + return await CreatePool.execute(signer, { + initialFields: { + invariant: this.instance.contractId, + token0: poolKey.tokenX, + token1: poolKey.tokenY, + feeTier: wrapFeeTier(poolKey.feeTier), + initSqrtPrice: { v: initSqrtPrice }, + initTick: calculateTick(initSqrtPrice, poolKey.feeTier.tickSpacing) + }, + attoAlphAmount: MAP_ENTRY_DEPOSIT * 5n + }) + } + async withdrawProtocolFeeTx(signer: SignerProvider, poolKey: PoolKey) { const builder = TransactionBuilder.from(web3.getCurrentNodeProvider()) const txBytecode = WithdrawProtocolFee.script.buildByteCodeToDeploy({ @@ -233,6 +261,13 @@ export class Invariant { return await signAndSend(signer, tx) } + async withdrawProtocolFeeExecute(signer: SignerProvider, poolKey: PoolKey) { + return await WithdrawProtocolFee.execute(signer, { + initialFields: { invariant: this.instance.contractId, poolKey: wrapPoolKey(poolKey) }, + attoAlphAmount: DUST_AMOUNT * 2n + }) + } + async changeFeeReceiverTx(signer: SignerProvider, poolKey: PoolKey, newFeeReceiver: Address) { const builder = TransactionBuilder.from(web3.getCurrentNodeProvider()) const txBytecode = ChangeFeeReceiver.script.buildByteCodeToDeploy({ @@ -257,6 +292,20 @@ export class Invariant { return await signAndSend(signer, tx) } + async changeFeeReceiverExecute( + signer: SignerProvider, + poolKey: PoolKey, + newFeeReceiver: Address + ) { + return await ChangeFeeReceiver.execute(signer, { + initialFields: { + invariant: this.instance.contractId, + poolKey: wrapPoolKey(poolKey), + newFeeReceiver + } + }) + } + async changeProtocolFeeTx(signer: SignerProvider, fee: bigint) { const builder = TransactionBuilder.from(web3.getCurrentNodeProvider()) const txBytecode = ChangeProtocolFee.script.buildByteCodeToDeploy({ @@ -275,6 +324,12 @@ export class Invariant { return await signAndSend(signer, tx) } + async changeProtocolFeeExecute(signer: SignerProvider, fee: bigint) { + return await ChangeProtocolFee.execute(signer, { + initialFields: { invariant: this.instance.contractId, newFee: { v: fee } } + }) + } + async createPositionTx( signer: SignerProvider, poolKey: PoolKey, @@ -360,6 +415,46 @@ export class Invariant { return await signAndSend(signer, tx) } + async createPositionExecute( + signer: SignerProvider, + poolKey: PoolKey, + lowerTick: bigint, + upperTick: bigint, + liquidityDelta: Liquidity, + approvedTokensX: TokenAmount, + approvedTokensY: TokenAmount, + spotSqrtPrice: SqrtPrice, + slippageTolerance: Percentage + ) { + const slippageLimitLower = calculateSqrtPriceAfterSlippage( + spotSqrtPrice, + slippageTolerance, + false + ) + const slippageLimitUpper = calculateSqrtPriceAfterSlippage( + spotSqrtPrice, + slippageTolerance, + true + ) + + return await CreatePosition.execute(signer, { + initialFields: { + invariant: this.instance.contractId, + poolKey: wrapPoolKey(poolKey), + lowerTick, + upperTick, + liquidityDelta: { v: liquidityDelta }, + slippageLimitLower: { v: slippageLimitLower }, + slippageLimitUpper: { v: slippageLimitUpper } + }, + attoAlphAmount: MAP_ENTRY_DEPOSIT * 6n + DUST_AMOUNT * 2n, + tokens: [ + { id: poolKey.tokenX, amount: approvedTokensX }, + { id: poolKey.tokenY, amount: approvedTokensY } + ] + }) + } + async removePositionTx(signer: SignerProvider, index: bigint) { const builder = TransactionBuilder.from(web3.getCurrentNodeProvider()) const txBytecode = RemovePosition.script.buildByteCodeToDeploy({ @@ -379,6 +474,12 @@ export class Invariant { return await signAndSend(signer, tx) } + async removePositionExecute(signer: SignerProvider, index: bigint) { + return await RemovePosition.execute(signer, { + initialFields: { invariant: this.instance.contractId, index } + }) + } + async claimFeeTx(signer: SignerProvider, index: bigint) { const builder = TransactionBuilder.from(web3.getCurrentNodeProvider()) const txBytecode = ClaimFee.script.buildByteCodeToDeploy({ @@ -398,6 +499,13 @@ export class Invariant { return await signAndSend(signer, tx) } + async claimFeeExecute(signer: SignerProvider, index: bigint) { + return await ClaimFee.execute(signer, { + initialFields: { invariant: this.instance.contractId, index }, + attoAlphAmount: DUST_AMOUNT * 2n + }) + } + async transferPositionTx(signer: SignerProvider, index: bigint, recipient: Address) { const builder = TransactionBuilder.from(web3.getCurrentNodeProvider()) const txBytecode = TransferPosition.script.buildByteCodeToDeploy({ @@ -421,6 +529,13 @@ export class Invariant { return await signAndSend(signer, tx) } + async transferPositionExecute(signer: SignerProvider, index: bigint, recipient: Address) { + return await TransferPosition.execute(signer, { + initialFields: { invariant: this.instance.contractId, index, recipient }, + attoAlphAmount: MAP_ENTRY_DEPOSIT * 2n + }) + } + async swapTx( signer: SignerProvider, poolKey: PoolKey, @@ -474,6 +589,29 @@ export class Invariant { return await signAndSend(signer, tx) } + async swapExecute( + signer: SignerProvider, + poolKey: PoolKey, + xToY: boolean, + amount: TokenAmount, + byAmountIn: boolean, + sqrtPriceLimit: SqrtPrice, + approvedAmount = amount + ) { + return await Swap.execute(signer, { + initialFields: { + invariant: this.instance.contractId, + poolKey: wrapPoolKey(poolKey), + xToY, + amount: { v: amount }, + byAmountIn, + sqrtPriceLimit: { v: sqrtPriceLimit } + }, + tokens: [{ id: xToY ? poolKey.tokenX : poolKey.tokenY, amount: approvedAmount }], + attoAlphAmount: DUST_AMOUNT * 2n + }) + } + async swapWithSlippageTx( signer: SignerProvider, poolKey: PoolKey, @@ -524,6 +662,27 @@ export class Invariant { return await signAndSend(signer, tx) } + async swapWithSlippageExecute( + signer: SignerProvider, + poolKey: PoolKey, + xToY: boolean, + amount: TokenAmount, + byAmountIn: boolean, + estimatedSqrtPrice: SqrtPrice, + slippage: Percentage, + approvedAmount: TokenAmount = amount + ) { + return await this.swapExecute( + signer, + poolKey, + xToY, + amount, + byAmountIn, + calculateSqrtPriceAfterSlippage(estimatedSqrtPrice, slippage, !xToY), + approvedAmount + ) + } + async feeTierExist(feeTier: FeeTier): Promise { return (await this.instance.view.feeTierExist({ args: { feeTier: wrapFeeTier(feeTier) } })) .returns @@ -815,4 +974,127 @@ export class Invariant { ).returns ) } + + async createPoolAndPositionTx( + signer: SignerProvider, + poolKey: PoolKey, + initSqrtPrice: SqrtPrice, + lowerTick: bigint, + upperTick: bigint, + liquidityDelta: Liquidity, + approvedTokensX: TokenAmount, + approvedTokensY: TokenAmount, + spotSqrtPrice: SqrtPrice, + slippageTolerance: Percentage + ) { + const initTick = calculateTick(initSqrtPrice, poolKey.feeTier.tickSpacing) + const slippageLimitLower = calculateSqrtPriceAfterSlippage( + spotSqrtPrice, + slippageTolerance, + false + ) + const slippageLimitUpper = calculateSqrtPriceAfterSlippage( + spotSqrtPrice, + slippageTolerance, + true + ) + + const builder = TransactionBuilder.from(web3.getCurrentNodeProvider()) + const txBytecode = CreatePoolAndPosition.script.buildByteCodeToDeploy({ + invariant: this.instance.contractId, + poolKey: wrapPoolKey(poolKey), + initSqrtPrice: { v: initSqrtPrice }, + initTick, + lowerTick, + upperTick, + liquidityDelta: { v: liquidityDelta }, + slippageLimitLower: { v: slippageLimitLower }, + slippageLimitUpper: { v: slippageLimitUpper } + }) + + const { address, publicKey } = await signer.getSelectedAccount() + const tx = await builder.buildExecuteScriptTx( + { + signerAddress: address, + bytecode: txBytecode, + attoAlphAmount: MAP_ENTRY_DEPOSIT * 11n + DUST_AMOUNT * 2n, + tokens: [ + { id: poolKey.tokenX, amount: approvedTokensX }, + { id: poolKey.tokenY, amount: approvedTokensY } + ] + }, + publicKey + ) + return tx + } + + async createPoolAndPosition( + signer: SignerProvider, + poolKey: PoolKey, + initSqrtPrice: SqrtPrice, + lowerTick: bigint, + upperTick: bigint, + liquidityDelta: Liquidity, + approvedTokensX: TokenAmount, + approvedTokensY: TokenAmount, + spotSqrtPrice: SqrtPrice, + slippageTolerance: Percentage + ): Promise { + const tx = await this.createPoolAndPositionTx( + signer, + poolKey, + initSqrtPrice, + lowerTick, + upperTick, + liquidityDelta, + approvedTokensX, + approvedTokensY, + spotSqrtPrice, + slippageTolerance + ) + return await signAndSend(signer, tx) + } + + async createPoolAndPositionExecute( + signer: SignerProvider, + poolKey: PoolKey, + initSqrtPrice: SqrtPrice, + lowerTick: bigint, + upperTick: bigint, + liquidityDelta: Liquidity, + approvedTokensX: TokenAmount, + approvedTokensY: TokenAmount, + spotSqrtPrice: SqrtPrice, + slippageTolerance: Percentage + ) { + const slippageLimitLower = calculateSqrtPriceAfterSlippage( + spotSqrtPrice, + slippageTolerance, + false + ) + const slippageLimitUpper = calculateSqrtPriceAfterSlippage( + spotSqrtPrice, + slippageTolerance, + true + ) + + return await CreatePoolAndPosition.execute(signer, { + initialFields: { + invariant: this.instance.contractId, + poolKey: wrapPoolKey(poolKey), + initSqrtPrice: { v: initSqrtPrice }, + initTick: calculateTick(initSqrtPrice, poolKey.feeTier.tickSpacing), + lowerTick, + upperTick, + liquidityDelta: { v: liquidityDelta }, + slippageLimitLower: { v: slippageLimitLower }, + slippageLimitUpper: { v: slippageLimitUpper } + }, + attoAlphAmount: MAP_ENTRY_DEPOSIT * 11n + DUST_AMOUNT * 2n, + tokens: [ + { id: poolKey.tokenX, amount: approvedTokensX }, + { id: poolKey.tokenY, amount: approvedTokensY } + ] + }) + } } diff --git a/test/contract/e2e/create-pool-and-position.test.ts b/test/contract/e2e/create-pool-and-position.test.ts new file mode 100644 index 0000000..c2ec498 --- /dev/null +++ b/test/contract/e2e/create-pool-and-position.test.ts @@ -0,0 +1,72 @@ +import { ONE_ALPH, web3 } from '@alephium/web3' +import { getSigner } from '@alephium/web3-test' +import { FungibleToken } from '../../../src/fungible-token' +import { PrivateKeyWallet } from '@alephium/web3-wallet' +import { + Invariant, + Liquidity, + newFeeTier, + newPoolKey, + Percentage, + PoolKey, + TokenAmount, + toPercentage, + toSqrtPrice +} from '../../../src' +import { getPool } from '../../../src/testUtils' + +web3.setCurrentNodeProvider('http://127.0.0.1:22973') + +let admin: PrivateKeyWallet +let user: PrivateKeyWallet +let invariant: Invariant +let token0: string +let token1: string + +const fee = toPercentage(1n, 2n) +const feeTier = newFeeTier(fee, 1n) +const anotherFeeTier = newFeeTier(fee, 2n) +let poolKey: PoolKey +let anotherPoolKey: PoolKey +const sqrtPrice = toSqrtPrice(1n, 0n) +const lowerTick = -10n +const upperTick = 10n +const liquidity = 1_000_000_000_000_000n as Liquidity +const amount = 1_000_000_000n as TokenAmount +const slippage = toPercentage(1n, 2n) + +describe('create pool and position tests', () => { + beforeAll(async () => { + admin = await getSigner(ONE_ALPH * 1000n, 0) + user = await getSigner(ONE_ALPH * 1000n, 0) + invariant = await Invariant.deploy(admin, 0n as Percentage) + token0 = await FungibleToken.deploy(admin, (amount * 2n) as TokenAmount, 'Coin', 'COIN', 12n) + token1 = await FungibleToken.deploy(admin, (amount * 2n) as TokenAmount, 'Coin', 'COIN', 12n) + poolKey = newPoolKey(token0, token1, feeTier) + anotherPoolKey = newPoolKey(token0, token1, anotherFeeTier) + }) + + test('create pool and position works', async () => { + await invariant.addFeeTier(admin, feeTier) + await invariant.addFeeTier(admin, anotherFeeTier) + await invariant.createPool(admin, poolKey, sqrtPrice) + + await invariant.createPoolAndPosition( + admin, + anotherPoolKey, + sqrtPrice, + lowerTick, + upperTick, + liquidity, + amount, + amount, + sqrtPrice, + slippage + ) + + const pool = await invariant.getPool(anotherPoolKey) + const [_, positionsCount] = await invariant.getPositions(admin.address, 100n, 0n) + expect(pool).toBeTruthy() + expect(positionsCount).toBe(1n) + }) +}) diff --git a/test/sdk/e2e/execute.test.ts b/test/sdk/e2e/execute.test.ts new file mode 100644 index 0000000..16254a2 --- /dev/null +++ b/test/sdk/e2e/execute.test.ts @@ -0,0 +1,178 @@ +import { ONE_ALPH, web3 } from '@alephium/web3' +import { getSigner } from '@alephium/web3-test' +import { FungibleToken } from '../../../src/fungible-token' +import { PrivateKeyWallet } from '@alephium/web3-wallet' +import { + Invariant, + Liquidity, + newFeeTier, + newPoolKey, + Percentage, + PoolKey, + SqrtPrice, + TokenAmount, + toPercentage, + toSqrtPrice, + waitTxConfirmed +} from '../../../src' + +web3.setCurrentNodeProvider('http://127.0.0.1:22973') + +let admin: PrivateKeyWallet +let user: PrivateKeyWallet +let invariant: Invariant +let token: FungibleToken +let token0: string +let token1: string + +const fee = toPercentage(1n, 2n) +const feeTier = newFeeTier(fee, 1n) +const anotherFeeTier = newFeeTier(fee, 2n) +let poolKey: PoolKey +let anotherPoolKey: PoolKey +const sqrtPrice = toSqrtPrice(1n, 0n) +const lowerTick = -10n +const upperTick = 10n +const liquidity = 1_000_000_000_000_000n as Liquidity +const amount = 1_000_000_000n as TokenAmount +const slippage = toPercentage(1n, 2n) +const amountIn = 10_000n as TokenAmount +const sqrtPriceLimit = 0n as SqrtPrice + +describe('execute tests', () => { + beforeAll(async () => { + admin = await getSigner(ONE_ALPH * 1000n, 0) + user = await getSigner(ONE_ALPH * 1000n, 0) + invariant = await Invariant.deploy(admin, 0n as Percentage) + token = FungibleToken.load() + token0 = await FungibleToken.deploy(admin, (amount * 2n) as TokenAmount, 'Coin', 'COIN', 12n) + token1 = await FungibleToken.deploy(admin, (amount * 2n) as TokenAmount, 'Coin', 'COIN', 12n) + poolKey = newPoolKey(token0, token1, feeTier) + anotherPoolKey = newPoolKey(token0, token1, anotherFeeTier) + }) + + test('change protocol fee works', async () => { + await waitTxConfirmed(invariant.changeProtocolFeeExecute(admin, fee)) + + const protocolFee = await invariant.getProtocolFee() + expect(protocolFee).toBe(fee) + }) + + test('add fee tier works', async () => { + await waitTxConfirmed(invariant.addFeeTierExecute(admin, feeTier)) + await waitTxConfirmed(invariant.addFeeTierExecute(admin, anotherFeeTier)) + + const feeTiers = await invariant.getFeeTiers() + expect(feeTiers.length).toBe(2) + expect(feeTiers[0]).toMatchObject(feeTier) + }) + + test('create pool works', async () => { + await waitTxConfirmed(invariant.createPoolExecute(admin, poolKey, sqrtPrice)) + + const poolKeys = await invariant.getAllPoolKeys() + expect(poolKeys.length).toBe(1) + expect(poolKeys[0]).toMatchObject(poolKey) + }) + + test('create position works', async () => { + await waitTxConfirmed( + invariant.createPositionExecute( + admin, + poolKey, + lowerTick, + upperTick, + liquidity, + amount, + amount, + sqrtPrice, + slippage + ) + ) + + const [_, positionsCount] = await invariant.getPositions(admin.address, 100n, 0n) + expect(positionsCount).toBe(1n) + }) + + test('swap works', async () => { + await waitTxConfirmed( + invariant.swapExecute(admin, poolKey, true, amountIn, true, sqrtPriceLimit, amountIn) + ) + + const balanceX = await FungibleToken.load().getBalanceOf(admin.address, poolKey.tokenX) + expect(balanceX).toBe(1994991499n) + const balanceY = await FungibleToken.load().getBalanceOf(admin.address, poolKey.tokenY) + expect(balanceY).toBe(1995011398n) + }) + + test('claim fee works', async () => { + await waitTxConfirmed(invariant.claimFeeExecute(admin, 0n)) + + const balanceX = await FungibleToken.load().getBalanceOf(admin.address, poolKey.tokenX) + expect(balanceX).toBe(1994991598n) + const balanceY = await FungibleToken.load().getBalanceOf(admin.address, poolKey.tokenY) + expect(balanceY).toBe(1995011398n) + }) + + test('withdraw protocol fee', async () => { + await waitTxConfirmed(invariant.withdrawProtocolFeeExecute(admin, poolKey)) + + const balanceX = await FungibleToken.load().getBalanceOf(admin.address, poolKey.tokenX) + expect(balanceX).toBe(1994991599n) + const balanceY = await FungibleToken.load().getBalanceOf(admin.address, poolKey.tokenY) + expect(balanceY).toBe(1995011398n) + }) + + test('transfer position works', async () => { + await waitTxConfirmed(invariant.transferPositionExecute(admin, 0n, user.address)) + + const [_, adminPositionsCount] = await invariant.getPositions(admin.address, 100n, 0n) + expect(adminPositionsCount).toBe(0n) + + const [__, puserPositionsCount] = await invariant.getPositions(user.address, 100n, 0n) + expect(puserPositionsCount).toBe(1n) + }) + + test('remove position works', async () => { + await waitTxConfirmed(invariant.removePositionExecute(user, 0n)) + + const [_, positionsCount] = await invariant.getPositions(user.address, 100n, 0n) + expect(positionsCount).toBe(0n) + }) + + test('change fee receiver works', async () => { + await waitTxConfirmed(invariant.changeFeeReceiverExecute(admin, poolKey, user.address)) + + const pool = await invariant.getPool(poolKey) + expect(pool.feeReceiver).toBe(user.address) + }) + + test('remove fee tier works', async () => { + await waitTxConfirmed(invariant.removeFeeTierExecute(admin, feeTier)) + + const feeTiers = await invariant.getFeeTiers() + expect(feeTiers.length).toBe(1) + }) + + test('create pool and position works', async () => { + await waitTxConfirmed( + invariant.createPoolAndPositionExecute( + admin, + anotherPoolKey, + sqrtPrice, + lowerTick, + upperTick, + liquidity, + amount, + amount, + sqrtPrice, + slippage + ) + ) + + const pool = await invariant.getPool(anotherPoolKey) + const [_, positionsCount] = await invariant.getPositions(admin.address, 100n, 0n) + expect(pool).toBeTruthy() + expect(positionsCount).toBe(1n) + }) +}) From 770f4759768b1c275f5b1c81749a7f51643fbd5a Mon Sep 17 00:00:00 2001 From: zielvna Date: Tue, 8 Oct 2024 16:33:03 +0200 Subject: [PATCH 2/5] refactor invariant methods in sdk --- src/consts.ts | 5 + src/invariant.ts | 573 +++++++---------------------------- src/types.ts | 4 + test/sdk/e2e/execute.test.ts | 178 ----------- 4 files changed, 122 insertions(+), 638 deletions(-) delete mode 100644 test/sdk/e2e/execute.test.ts diff --git a/src/consts.ts b/src/consts.ts index e7b4a19..8878700 100644 --- a/src/consts.ts +++ b/src/consts.ts @@ -15,6 +15,7 @@ import { FeeGrowth, FixedPoint, Liquidity, + Options, Percentage, Price, SqrtPrice, @@ -91,3 +92,7 @@ export const SOL_ID = { [Network.Testnet]: 'c925472266dbd7f2e01313ad787cfb12b7f4be6776e9a3b02daed9dde7706200', [Network.Mainnet]: '' } + +export const CONFIRMATIONS = 1 +export const REQUEST_INTERVAL = 1000 +export const DEFAULT_OPTIONS: Options = { waitForTxConfirmation: true } diff --git a/src/invariant.ts b/src/invariant.ts index 2f71d88..4525e4e 100644 --- a/src/invariant.ts +++ b/src/invariant.ts @@ -31,6 +31,7 @@ import { decodeTick, FeeTier, Liquidity, + Options, Percentage, Pool, PoolKey, @@ -65,10 +66,13 @@ import { } from './utils' import { CHUNK_SIZE, + CONFIRMATIONS, + DEFAULT_OPTIONS, MAX_BATCHES_QUERIED, MAX_LIQUIDITY_TICKS_QUERIED, MAX_POOL_KEYS_RETURNED, - POSITIONS_ENTRIES_LIMIT + POSITIONS_ENTRIES_LIMIT, + REQUEST_INTERVAL } from './consts' import { Address, @@ -78,6 +82,7 @@ import { MAP_ENTRY_DEPOSIT, SignerProvider, TransactionBuilder, + waitForTxConfirmation, web3 } from '@alephium/web3' @@ -149,87 +154,46 @@ export class Invariant { }) } - async addFeeTierTx(signer: SignerProvider, feeTier: FeeTier) { - const builder = TransactionBuilder.from(web3.getCurrentNodeProvider()) - const txBytecode = AddFeeTier.script.buildByteCodeToDeploy({ - invariant: this.instance.contractId, - feeTier: wrapFeeTier(feeTier) - }) - const { address, publicKey } = await signer.getSelectedAccount() - const tx = await builder.buildExecuteScriptTx( - { signerAddress: address, bytecode: txBytecode, attoAlphAmount: MAP_ENTRY_DEPOSIT }, - publicKey - ) - return tx - } - - async addFeeTier(signer: SignerProvider, feeTier: FeeTier): Promise { - const tx = await this.addFeeTierTx(signer, feeTier) - return await signAndSend(signer, tx) - } - - async addFeeTierExecute(signer: SignerProvider, feeTier: FeeTier) { - return await AddFeeTier.execute(signer, { + async addFeeTier( + signer: SignerProvider, + feeTier: FeeTier, + options: Options = DEFAULT_OPTIONS + ): Promise { + const { txId } = await AddFeeTier.execute(signer, { initialFields: { invariant: this.instance.contractId, feeTier: wrapFeeTier(feeTier) }, attoAlphAmount: MAP_ENTRY_DEPOSIT }) - } - async removeFeeTierTx(signer: SignerProvider, feeTier: FeeTier) { - const builder = TransactionBuilder.from(web3.getCurrentNodeProvider()) - const txBytecode = RemoveFeeTier.script.buildByteCodeToDeploy({ - invariant: this.instance.contractId, - feeTier: wrapFeeTier(feeTier) - }) - const { address, publicKey } = await signer.getSelectedAccount() - const tx = await builder.buildExecuteScriptTx( - { signerAddress: address, bytecode: txBytecode }, - publicKey - ) - return tx - } + if (options.waitForTxConfirmation) { + await waitForTxConfirmation(txId, CONFIRMATIONS, REQUEST_INTERVAL) + } - async removeFeeTier(signer: SignerProvider, feeTier: FeeTier): Promise { - const tx = await this.removeFeeTierTx(signer, feeTier) - return await signAndSend(signer, tx) + return txId } - async removeFeeTierExecute(signer: SignerProvider, feeTier: FeeTier) { - return await RemoveFeeTier.execute(signer, { + async removeFeeTier( + signer: SignerProvider, + feeTier: FeeTier, + options: Options = DEFAULT_OPTIONS + ): Promise { + const result = await RemoveFeeTier.execute(signer, { initialFields: { invariant: this.instance.contractId, feeTier: wrapFeeTier(feeTier) } }) - } - async createPoolTx(signer: SignerProvider, poolKey: PoolKey, initSqrtPrice: SqrtPrice) { - const initTick = calculateTick(initSqrtPrice, poolKey.feeTier.tickSpacing) - const builder = TransactionBuilder.from(web3.getCurrentNodeProvider()) - const txBytecode = CreatePool.script.buildByteCodeToDeploy({ - invariant: this.instance.contractId, - token0: poolKey.tokenX, - token1: poolKey.tokenY, - feeTier: wrapFeeTier(poolKey.feeTier), - initSqrtPrice: { v: initSqrtPrice }, - initTick - }) - const { address, publicKey } = await signer.getSelectedAccount() - const tx = await builder.buildExecuteScriptTx( - { signerAddress: address, bytecode: txBytecode, attoAlphAmount: MAP_ENTRY_DEPOSIT * 5n }, - publicKey - ) - return tx + if (options.waitForTxConfirmation) { + await waitForTxConfirmation(result.txId, CONFIRMATIONS, REQUEST_INTERVAL) + } + + return result.txId } async createPool( signer: SignerProvider, poolKey: PoolKey, - initSqrtPrice: SqrtPrice + initSqrtPrice: SqrtPrice, + options: Options = DEFAULT_OPTIONS ): Promise { - const tx = await this.createPoolTx(signer, poolKey, initSqrtPrice) - return await signAndSend(signer, tx) - } - - async createPoolExecute(signer: SignerProvider, poolKey: PoolKey, initSqrtPrice: SqrtPrice) { - return await CreatePool.execute(signer, { + const result = await CreatePool.execute(signer, { initialFields: { invariant: this.instance.contractId, token0: poolKey.tokenX, @@ -240,154 +204,62 @@ export class Invariant { }, attoAlphAmount: MAP_ENTRY_DEPOSIT * 5n }) - } - async withdrawProtocolFeeTx(signer: SignerProvider, poolKey: PoolKey) { - const builder = TransactionBuilder.from(web3.getCurrentNodeProvider()) - const txBytecode = WithdrawProtocolFee.script.buildByteCodeToDeploy({ - invariant: this.instance.contractId, - poolKey: wrapPoolKey(poolKey) - }) - const { address, publicKey } = await signer.getSelectedAccount() - const tx = await builder.buildExecuteScriptTx( - { signerAddress: address, bytecode: txBytecode, attoAlphAmount: DUST_AMOUNT * 2n }, - publicKey - ) - return tx - } + if (options.waitForTxConfirmation) { + await waitForTxConfirmation(result.txId, CONFIRMATIONS, REQUEST_INTERVAL) + } - async withdrawProtocolFee(signer: SignerProvider, poolKey: PoolKey): Promise { - const tx = await this.withdrawProtocolFeeTx(signer, poolKey) - return await signAndSend(signer, tx) + return result.txId } - async withdrawProtocolFeeExecute(signer: SignerProvider, poolKey: PoolKey) { - return await WithdrawProtocolFee.execute(signer, { + async withdrawProtocolFee( + signer: SignerProvider, + poolKey: PoolKey, + options: Options = DEFAULT_OPTIONS + ): Promise { + const result = await WithdrawProtocolFee.execute(signer, { initialFields: { invariant: this.instance.contractId, poolKey: wrapPoolKey(poolKey) }, attoAlphAmount: DUST_AMOUNT * 2n }) - } - async changeFeeReceiverTx(signer: SignerProvider, poolKey: PoolKey, newFeeReceiver: Address) { - const builder = TransactionBuilder.from(web3.getCurrentNodeProvider()) - const txBytecode = ChangeFeeReceiver.script.buildByteCodeToDeploy({ - invariant: this.instance.contractId, - poolKey: wrapPoolKey(poolKey), - newFeeReceiver - }) - const { address, publicKey } = await signer.getSelectedAccount() - const tx = await builder.buildExecuteScriptTx( - { signerAddress: address, bytecode: txBytecode }, - publicKey - ) - return tx + if (options.waitForTxConfirmation) { + await waitForTxConfirmation(result.txId, CONFIRMATIONS, REQUEST_INTERVAL) + } + + return result.txId } async changeFeeReceiver( signer: SignerProvider, poolKey: PoolKey, - newFeeReceiver: Address + newFeeReceiver: Address, + options: Options = DEFAULT_OPTIONS ): Promise { - const tx = await this.changeFeeReceiverTx(signer, poolKey, newFeeReceiver) - return await signAndSend(signer, tx) - } - - async changeFeeReceiverExecute( - signer: SignerProvider, - poolKey: PoolKey, - newFeeReceiver: Address - ) { - return await ChangeFeeReceiver.execute(signer, { + const result = await ChangeFeeReceiver.execute(signer, { initialFields: { invariant: this.instance.contractId, poolKey: wrapPoolKey(poolKey), newFeeReceiver } }) - } - async changeProtocolFeeTx(signer: SignerProvider, fee: bigint) { - const builder = TransactionBuilder.from(web3.getCurrentNodeProvider()) - const txBytecode = ChangeProtocolFee.script.buildByteCodeToDeploy({ - invariant: this.instance.contractId, - newFee: fee - }) - const { address, publicKey } = await signer.getSelectedAccount() - const tx = await builder.buildExecuteScriptTx( - { signerAddress: address, bytecode: txBytecode }, - publicKey - ) - return tx - } - async changeProtocolFee(signer: SignerProvider, fee: bigint): Promise { - const tx = await this.changeProtocolFeeTx(signer, fee) - return await signAndSend(signer, tx) - } + if (options.waitForTxConfirmation) { + await waitForTxConfirmation(result.txId, CONFIRMATIONS, REQUEST_INTERVAL) + } - async changeProtocolFeeExecute(signer: SignerProvider, fee: bigint) { - return await ChangeProtocolFee.execute(signer, { - initialFields: { invariant: this.instance.contractId, newFee: { v: fee } } - }) + return result.txId } - async createPositionTx( - signer: SignerProvider, - poolKey: PoolKey, - lowerTick: bigint, - upperTick: bigint, - liquidityDelta: Liquidity, - approvedTokensX: TokenAmount, - approvedTokensY: TokenAmount, - spotSqrtPrice: SqrtPrice, - slippageTolerance: Percentage - ) { - const slippageLimitLower = calculateSqrtPriceAfterSlippage( - spotSqrtPrice, - slippageTolerance, - false - ) - const slippageLimitUpper = calculateSqrtPriceAfterSlippage( - spotSqrtPrice, - slippageTolerance, - true - ) - - const builder = TransactionBuilder.from(web3.getCurrentNodeProvider()) - const txBytecode = CreatePosition.script.buildByteCodeToDeploy({ - invariant: this.instance.contractId, - poolKey: wrapPoolKey(poolKey), - lowerTick, - upperTick, - liquidityDelta: { v: liquidityDelta }, - slippageLimitLower: { v: slippageLimitLower }, - slippageLimitUpper: { v: slippageLimitUpper } + async changeProtocolFee(signer: SignerProvider, fee: bigint, options: Options = DEFAULT_OPTIONS) { + const result = await ChangeProtocolFee.execute(signer, { + initialFields: { invariant: this.instance.contractId, newFee: { v: fee } } }) - const { address, publicKey } = await signer.getSelectedAccount() - let attoAlphAmount = MAP_ENTRY_DEPOSIT * 6n + DUST_AMOUNT * 2n - const tokens: { id: string; amount: bigint }[] = [] - if (approvedTokensX) { - tokens.push({ id: poolKey.tokenX, amount: approvedTokensX }) - } - if (approvedTokensY) { - tokens.push({ id: poolKey.tokenY, amount: approvedTokensY }) - } - - if (poolKey.tokenX === ALPH_TOKEN_ID) { - attoAlphAmount += approvedTokensX + DUST_AMOUNT - tokens.shift() + if (options.waitForTxConfirmation) { + await waitForTxConfirmation(result.txId, CONFIRMATIONS, REQUEST_INTERVAL) } - const tx = await builder.buildExecuteScriptTx( - { - signerAddress: address, - bytecode: txBytecode, - attoAlphAmount, - tokens - }, - publicKey - ) - return tx + return result.txId } async createPosition( @@ -399,32 +271,8 @@ export class Invariant { approvedTokensX: TokenAmount, approvedTokensY: TokenAmount, spotSqrtPrice: SqrtPrice, - slippageTolerance: Percentage - ): Promise { - const tx = await this.createPositionTx( - signer, - poolKey, - lowerTick, - upperTick, - liquidityDelta, - approvedTokensX, - approvedTokensY, - spotSqrtPrice, - slippageTolerance - ) - return await signAndSend(signer, tx) - } - - async createPositionExecute( - signer: SignerProvider, - poolKey: PoolKey, - lowerTick: bigint, - upperTick: bigint, - liquidityDelta: Liquidity, - approvedTokensX: TokenAmount, - approvedTokensY: TokenAmount, - spotSqrtPrice: SqrtPrice, - slippageTolerance: Percentage + slippageTolerance: Percentage, + options: Options = DEFAULT_OPTIONS ) { const slippageLimitLower = calculateSqrtPriceAfterSlippage( spotSqrtPrice, @@ -437,7 +285,7 @@ export class Invariant { true ) - return await CreatePosition.execute(signer, { + const result = await CreatePosition.execute(signer, { initialFields: { invariant: this.instance.contractId, poolKey: wrapPoolKey(poolKey), @@ -453,152 +301,72 @@ export class Invariant { { id: poolKey.tokenY, amount: approvedTokensY } ] }) - } - async removePositionTx(signer: SignerProvider, index: bigint) { - const builder = TransactionBuilder.from(web3.getCurrentNodeProvider()) - const txBytecode = RemovePosition.script.buildByteCodeToDeploy({ - invariant: this.instance.contractId, - index - }) - const { address, publicKey } = await signer.getSelectedAccount() - const tx = await builder.buildExecuteScriptTx( - { signerAddress: address, bytecode: txBytecode, attoAlphAmount: DUST_AMOUNT * 2n }, - publicKey - ) - return tx - } + if (options.waitForTxConfirmation) { + await waitForTxConfirmation(result.txId, CONFIRMATIONS, REQUEST_INTERVAL) + } - async removePosition(signer: SignerProvider, index: bigint): Promise { - const tx = await this.removePositionTx(signer, index) - return await signAndSend(signer, tx) + return result.txId } - async removePositionExecute(signer: SignerProvider, index: bigint) { - return await RemovePosition.execute(signer, { + async removePosition(signer: SignerProvider, index: bigint, options: Options = DEFAULT_OPTIONS) { + const result = await RemovePosition.execute(signer, { initialFields: { invariant: this.instance.contractId, index } }) - } - async claimFeeTx(signer: SignerProvider, index: bigint) { - const builder = TransactionBuilder.from(web3.getCurrentNodeProvider()) - const txBytecode = ClaimFee.script.buildByteCodeToDeploy({ - invariant: this.instance.contractId, - index - }) - const { address, publicKey } = await signer.getSelectedAccount() - const tx = await builder.buildExecuteScriptTx( - { signerAddress: address, bytecode: txBytecode, attoAlphAmount: DUST_AMOUNT * 2n }, - publicKey - ) - return tx - } + if (options.waitForTxConfirmation) { + await waitForTxConfirmation(result.txId, CONFIRMATIONS, REQUEST_INTERVAL) + } - async claimFee(signer: SignerProvider, index: bigint): Promise { - const tx = await this.claimFeeTx(signer, index) - return await signAndSend(signer, tx) + return result.txId } - async claimFeeExecute(signer: SignerProvider, index: bigint) { - return await ClaimFee.execute(signer, { + async claimFee( + signer: SignerProvider, + index: bigint, + options: Options = DEFAULT_OPTIONS + ): Promise { + const result = await ClaimFee.execute(signer, { initialFields: { invariant: this.instance.contractId, index }, attoAlphAmount: DUST_AMOUNT * 2n }) - } - async transferPositionTx(signer: SignerProvider, index: bigint, recipient: Address) { - const builder = TransactionBuilder.from(web3.getCurrentNodeProvider()) - const txBytecode = TransferPosition.script.buildByteCodeToDeploy({ - invariant: this.instance.contractId, - index, - recipient - }) - const { address, publicKey } = await signer.getSelectedAccount() - const tx = await builder.buildExecuteScriptTx( - { signerAddress: address, bytecode: txBytecode, attoAlphAmount: MAP_ENTRY_DEPOSIT * 2n }, - publicKey - ) - return tx + if (options.waitForTxConfirmation) { + await waitForTxConfirmation(result.txId, CONFIRMATIONS, REQUEST_INTERVAL) + } + + return result.txId } + async transferPosition( signer: SignerProvider, index: bigint, - recipient: Address - ): Promise { - const tx = await this.transferPositionTx(signer, index, recipient) - return await signAndSend(signer, tx) - } - - async transferPositionExecute(signer: SignerProvider, index: bigint, recipient: Address) { - return await TransferPosition.execute(signer, { + recipient: Address, + options: Options = DEFAULT_OPTIONS + ) { + const result = await TransferPosition.execute(signer, { initialFields: { invariant: this.instance.contractId, index, recipient }, attoAlphAmount: MAP_ENTRY_DEPOSIT * 2n }) - } - async swapTx( - signer: SignerProvider, - poolKey: PoolKey, - xToY: boolean, - amount: TokenAmount, - byAmountIn: boolean, - sqrtPriceLimit: SqrtPrice, - approvedAmount = amount - ) { - const tokenId = xToY ? poolKey.tokenX : poolKey.tokenY - const builder = TransactionBuilder.from(web3.getCurrentNodeProvider()) - const txBytecode = Swap.script.buildByteCodeToDeploy({ - invariant: this.instance.contractId, - poolKey: wrapPoolKey(poolKey), - xToY, - amount: { v: amount }, - byAmountIn, - sqrtPriceLimit: { v: sqrtPriceLimit } - }) - const { address, publicKey } = await signer.getSelectedAccount() - const tx = await builder.buildExecuteScriptTx( - { - signerAddress: address, - bytecode: txBytecode, - tokens: [{ id: tokenId, amount: approvedAmount }], - attoAlphAmount: DUST_AMOUNT * 2n - }, - publicKey - ) - return tx - } + if (options.waitForTxConfirmation) { + await waitForTxConfirmation(result.txId, CONFIRMATIONS, REQUEST_INTERVAL) + } - async swap( - signer: SignerProvider, - poolKey: PoolKey, - xToY: boolean, - amount: TokenAmount, - byAmountIn: boolean, - sqrtPriceLimit: SqrtPrice, - approvedAmount = amount - ): Promise { - const tx = await this.swapTx( - signer, - poolKey, - xToY, - amount, - byAmountIn, - sqrtPriceLimit, - approvedAmount - ) - return await signAndSend(signer, tx) + return result.txId } - async swapExecute( + async swap( signer: SignerProvider, poolKey: PoolKey, xToY: boolean, amount: TokenAmount, byAmountIn: boolean, sqrtPriceLimit: SqrtPrice, - approvedAmount = amount + approvedAmount = amount, + options: Options = DEFAULT_OPTIONS ) { - return await Swap.execute(signer, { + const result = await Swap.execute(signer, { initialFields: { invariant: this.instance.contractId, poolKey: wrapPoolKey(poolKey), @@ -610,33 +378,12 @@ export class Invariant { tokens: [{ id: xToY ? poolKey.tokenX : poolKey.tokenY, amount: approvedAmount }], attoAlphAmount: DUST_AMOUNT * 2n }) - } - async swapWithSlippageTx( - signer: SignerProvider, - poolKey: PoolKey, - xToY: boolean, - amount: TokenAmount, - byAmountIn: boolean, - estimatedSqrtPrice: SqrtPrice, - slippage: Percentage, - approvedAmount: TokenAmount = amount - ) { - const sqrtPriceAfterSlippage = calculateSqrtPriceAfterSlippage( - estimatedSqrtPrice, - slippage, - !xToY - ) + if (options.waitForTxConfirmation) { + await waitForTxConfirmation(result.txId, CONFIRMATIONS, REQUEST_INTERVAL) + } - return this.swapTx( - signer, - poolKey, - xToY, - amount, - byAmountIn, - (xToY ? sqrtPriceAfterSlippage - 1n : sqrtPriceAfterSlippage + 1n) as SqrtPrice, - approvedAmount - ) + return result.txId } async swapWithSlippage( @@ -647,39 +394,18 @@ export class Invariant { byAmountIn: boolean, estimatedSqrtPrice: SqrtPrice, slippage: Percentage, - approvedAmount: TokenAmount = amount - ): Promise { - const tx = await this.swapWithSlippageTx( - signer, - poolKey, - xToY, - amount, - byAmountIn, - estimatedSqrtPrice, - slippage, - approvedAmount - ) - return await signAndSend(signer, tx) - } - - async swapWithSlippageExecute( - signer: SignerProvider, - poolKey: PoolKey, - xToY: boolean, - amount: TokenAmount, - byAmountIn: boolean, - estimatedSqrtPrice: SqrtPrice, - slippage: Percentage, - approvedAmount: TokenAmount = amount + approvedAmount: TokenAmount = amount, + options: Options = DEFAULT_OPTIONS ) { - return await this.swapExecute( + return await this.swap( signer, poolKey, xToY, amount, byAmountIn, calculateSqrtPriceAfterSlippage(estimatedSqrtPrice, slippage, !xToY), - approvedAmount + approvedAmount, + options ) } @@ -975,59 +701,6 @@ export class Invariant { ) } - async createPoolAndPositionTx( - signer: SignerProvider, - poolKey: PoolKey, - initSqrtPrice: SqrtPrice, - lowerTick: bigint, - upperTick: bigint, - liquidityDelta: Liquidity, - approvedTokensX: TokenAmount, - approvedTokensY: TokenAmount, - spotSqrtPrice: SqrtPrice, - slippageTolerance: Percentage - ) { - const initTick = calculateTick(initSqrtPrice, poolKey.feeTier.tickSpacing) - const slippageLimitLower = calculateSqrtPriceAfterSlippage( - spotSqrtPrice, - slippageTolerance, - false - ) - const slippageLimitUpper = calculateSqrtPriceAfterSlippage( - spotSqrtPrice, - slippageTolerance, - true - ) - - const builder = TransactionBuilder.from(web3.getCurrentNodeProvider()) - const txBytecode = CreatePoolAndPosition.script.buildByteCodeToDeploy({ - invariant: this.instance.contractId, - poolKey: wrapPoolKey(poolKey), - initSqrtPrice: { v: initSqrtPrice }, - initTick, - lowerTick, - upperTick, - liquidityDelta: { v: liquidityDelta }, - slippageLimitLower: { v: slippageLimitLower }, - slippageLimitUpper: { v: slippageLimitUpper } - }) - - const { address, publicKey } = await signer.getSelectedAccount() - const tx = await builder.buildExecuteScriptTx( - { - signerAddress: address, - bytecode: txBytecode, - attoAlphAmount: MAP_ENTRY_DEPOSIT * 11n + DUST_AMOUNT * 2n, - tokens: [ - { id: poolKey.tokenX, amount: approvedTokensX }, - { id: poolKey.tokenY, amount: approvedTokensY } - ] - }, - publicKey - ) - return tx - } - async createPoolAndPosition( signer: SignerProvider, poolKey: PoolKey, @@ -1038,34 +711,8 @@ export class Invariant { approvedTokensX: TokenAmount, approvedTokensY: TokenAmount, spotSqrtPrice: SqrtPrice, - slippageTolerance: Percentage - ): Promise { - const tx = await this.createPoolAndPositionTx( - signer, - poolKey, - initSqrtPrice, - lowerTick, - upperTick, - liquidityDelta, - approvedTokensX, - approvedTokensY, - spotSqrtPrice, - slippageTolerance - ) - return await signAndSend(signer, tx) - } - - async createPoolAndPositionExecute( - signer: SignerProvider, - poolKey: PoolKey, - initSqrtPrice: SqrtPrice, - lowerTick: bigint, - upperTick: bigint, - liquidityDelta: Liquidity, - approvedTokensX: TokenAmount, - approvedTokensY: TokenAmount, - spotSqrtPrice: SqrtPrice, - slippageTolerance: Percentage + slippageTolerance: Percentage, + options: Options = DEFAULT_OPTIONS ) { const slippageLimitLower = calculateSqrtPriceAfterSlippage( spotSqrtPrice, @@ -1078,7 +725,7 @@ export class Invariant { true ) - return await CreatePoolAndPosition.execute(signer, { + const result = await CreatePoolAndPosition.execute(signer, { initialFields: { invariant: this.instance.contractId, poolKey: wrapPoolKey(poolKey), @@ -1096,5 +743,11 @@ export class Invariant { { id: poolKey.tokenY, amount: approvedTokensY } ] }) + + if (options.waitForTxConfirmation) { + await waitForTxConfirmation(result.txId, CONFIRMATIONS, REQUEST_INTERVAL) + } + + return result.txId } } diff --git a/src/types.ts b/src/types.ts index 79dbd68..7a24786 100644 --- a/src/types.ts +++ b/src/types.ts @@ -232,3 +232,7 @@ export function decodeTick(array: [boolean, _Tick]): Tick { export function decodePosition(array: [boolean, _Position]): Position { return existsOnly(unwrapPosition(array[1]), array[0], InvariantError.PositionNotFound) } + +export type Options = { + waitForTxConfirmation?: boolean +} diff --git a/test/sdk/e2e/execute.test.ts b/test/sdk/e2e/execute.test.ts deleted file mode 100644 index 16254a2..0000000 --- a/test/sdk/e2e/execute.test.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { ONE_ALPH, web3 } from '@alephium/web3' -import { getSigner } from '@alephium/web3-test' -import { FungibleToken } from '../../../src/fungible-token' -import { PrivateKeyWallet } from '@alephium/web3-wallet' -import { - Invariant, - Liquidity, - newFeeTier, - newPoolKey, - Percentage, - PoolKey, - SqrtPrice, - TokenAmount, - toPercentage, - toSqrtPrice, - waitTxConfirmed -} from '../../../src' - -web3.setCurrentNodeProvider('http://127.0.0.1:22973') - -let admin: PrivateKeyWallet -let user: PrivateKeyWallet -let invariant: Invariant -let token: FungibleToken -let token0: string -let token1: string - -const fee = toPercentage(1n, 2n) -const feeTier = newFeeTier(fee, 1n) -const anotherFeeTier = newFeeTier(fee, 2n) -let poolKey: PoolKey -let anotherPoolKey: PoolKey -const sqrtPrice = toSqrtPrice(1n, 0n) -const lowerTick = -10n -const upperTick = 10n -const liquidity = 1_000_000_000_000_000n as Liquidity -const amount = 1_000_000_000n as TokenAmount -const slippage = toPercentage(1n, 2n) -const amountIn = 10_000n as TokenAmount -const sqrtPriceLimit = 0n as SqrtPrice - -describe('execute tests', () => { - beforeAll(async () => { - admin = await getSigner(ONE_ALPH * 1000n, 0) - user = await getSigner(ONE_ALPH * 1000n, 0) - invariant = await Invariant.deploy(admin, 0n as Percentage) - token = FungibleToken.load() - token0 = await FungibleToken.deploy(admin, (amount * 2n) as TokenAmount, 'Coin', 'COIN', 12n) - token1 = await FungibleToken.deploy(admin, (amount * 2n) as TokenAmount, 'Coin', 'COIN', 12n) - poolKey = newPoolKey(token0, token1, feeTier) - anotherPoolKey = newPoolKey(token0, token1, anotherFeeTier) - }) - - test('change protocol fee works', async () => { - await waitTxConfirmed(invariant.changeProtocolFeeExecute(admin, fee)) - - const protocolFee = await invariant.getProtocolFee() - expect(protocolFee).toBe(fee) - }) - - test('add fee tier works', async () => { - await waitTxConfirmed(invariant.addFeeTierExecute(admin, feeTier)) - await waitTxConfirmed(invariant.addFeeTierExecute(admin, anotherFeeTier)) - - const feeTiers = await invariant.getFeeTiers() - expect(feeTiers.length).toBe(2) - expect(feeTiers[0]).toMatchObject(feeTier) - }) - - test('create pool works', async () => { - await waitTxConfirmed(invariant.createPoolExecute(admin, poolKey, sqrtPrice)) - - const poolKeys = await invariant.getAllPoolKeys() - expect(poolKeys.length).toBe(1) - expect(poolKeys[0]).toMatchObject(poolKey) - }) - - test('create position works', async () => { - await waitTxConfirmed( - invariant.createPositionExecute( - admin, - poolKey, - lowerTick, - upperTick, - liquidity, - amount, - amount, - sqrtPrice, - slippage - ) - ) - - const [_, positionsCount] = await invariant.getPositions(admin.address, 100n, 0n) - expect(positionsCount).toBe(1n) - }) - - test('swap works', async () => { - await waitTxConfirmed( - invariant.swapExecute(admin, poolKey, true, amountIn, true, sqrtPriceLimit, amountIn) - ) - - const balanceX = await FungibleToken.load().getBalanceOf(admin.address, poolKey.tokenX) - expect(balanceX).toBe(1994991499n) - const balanceY = await FungibleToken.load().getBalanceOf(admin.address, poolKey.tokenY) - expect(balanceY).toBe(1995011398n) - }) - - test('claim fee works', async () => { - await waitTxConfirmed(invariant.claimFeeExecute(admin, 0n)) - - const balanceX = await FungibleToken.load().getBalanceOf(admin.address, poolKey.tokenX) - expect(balanceX).toBe(1994991598n) - const balanceY = await FungibleToken.load().getBalanceOf(admin.address, poolKey.tokenY) - expect(balanceY).toBe(1995011398n) - }) - - test('withdraw protocol fee', async () => { - await waitTxConfirmed(invariant.withdrawProtocolFeeExecute(admin, poolKey)) - - const balanceX = await FungibleToken.load().getBalanceOf(admin.address, poolKey.tokenX) - expect(balanceX).toBe(1994991599n) - const balanceY = await FungibleToken.load().getBalanceOf(admin.address, poolKey.tokenY) - expect(balanceY).toBe(1995011398n) - }) - - test('transfer position works', async () => { - await waitTxConfirmed(invariant.transferPositionExecute(admin, 0n, user.address)) - - const [_, adminPositionsCount] = await invariant.getPositions(admin.address, 100n, 0n) - expect(adminPositionsCount).toBe(0n) - - const [__, puserPositionsCount] = await invariant.getPositions(user.address, 100n, 0n) - expect(puserPositionsCount).toBe(1n) - }) - - test('remove position works', async () => { - await waitTxConfirmed(invariant.removePositionExecute(user, 0n)) - - const [_, positionsCount] = await invariant.getPositions(user.address, 100n, 0n) - expect(positionsCount).toBe(0n) - }) - - test('change fee receiver works', async () => { - await waitTxConfirmed(invariant.changeFeeReceiverExecute(admin, poolKey, user.address)) - - const pool = await invariant.getPool(poolKey) - expect(pool.feeReceiver).toBe(user.address) - }) - - test('remove fee tier works', async () => { - await waitTxConfirmed(invariant.removeFeeTierExecute(admin, feeTier)) - - const feeTiers = await invariant.getFeeTiers() - expect(feeTiers.length).toBe(1) - }) - - test('create pool and position works', async () => { - await waitTxConfirmed( - invariant.createPoolAndPositionExecute( - admin, - anotherPoolKey, - sqrtPrice, - lowerTick, - upperTick, - liquidity, - amount, - amount, - sqrtPrice, - slippage - ) - ) - - const pool = await invariant.getPool(anotherPoolKey) - const [_, positionsCount] = await invariant.getPositions(admin.address, 100n, 0n) - expect(pool).toBeTruthy() - expect(positionsCount).toBe(1n) - }) -}) From a7671f5f1918e1a15678e9c4e4fd3e1fd02bf532 Mon Sep 17 00:00:00 2001 From: zielvna Date: Tue, 8 Oct 2024 17:04:05 +0200 Subject: [PATCH 3/5] refactor fungible token methods and bump sdk --- package-lock.json | 4 +- package.json | 2 +- src/fungible-token.ts | 86 ++++++++++++++++--------------------------- src/invariant.ts | 6 +-- 4 files changed, 37 insertions(+), 61 deletions(-) diff --git a/package-lock.json b/package-lock.json index 52c7d5d..0ebdab7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@invariant-labs/alph-sdk", - "version": "0.0.13", + "version": "0.0.15", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@invariant-labs/alph-sdk", - "version": "0.0.13", + "version": "0.0.15", "license": "GPL", "dependencies": { "@alephium/cli": "^1.7.3", diff --git a/package.json b/package.json index 0de06ad..dc7ddf4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@invariant-labs/alph-sdk", - "version": "0.0.13", + "version": "0.0.15", "collaborators": [ "Invariant Labs" ], diff --git a/src/fungible-token.ts b/src/fungible-token.ts index 632bee3..a9cbefe 100644 --- a/src/fungible-token.ts +++ b/src/fungible-token.ts @@ -6,13 +6,14 @@ import { SignerProvider, stringToHex, TransactionBuilder, + waitForTxConfirmation, web3 } from '@alephium/web3' import { Network } from './network' import { Airdrop, TokenFaucet, Withdraw } from '../artifacts/ts' import { balanceOf, signAndSend, waitTxConfirmed } from './utils' -import { MAX_U256 } from './consts' -import { TokenAmount } from './types' +import { CONFIRMATIONS, DEFAULT_OPTIONS, MAX_U256, REQUEST_INTERVAL } from './consts' +import { Options, TokenAmount } from './types' export type TokenMetaData = { symbol: string @@ -59,25 +60,21 @@ export class FungibleToken { return new FungibleToken() } - async mintTx(signer: SignerProvider, value: TokenAmount, tokenId: string) { - const builder = TransactionBuilder.from(web3.getCurrentNodeProvider()) - const bytecode = Withdraw.script.buildByteCodeToDeploy({ - token: tokenId, - amount: value + async mint( + signer: SignerProvider, + value: TokenAmount, + tokenId: string, + options: Options = DEFAULT_OPTIONS + ) { + const result = await Withdraw.execute(signer, { + initialFields: { token: tokenId, amount: value } }) - const { address, publicKey } = await signer.getSelectedAccount() - - const unsignedTxBuild = await builder.buildExecuteScriptTx( - { signerAddress: address, bytecode, attoAlphAmount: DUST_AMOUNT }, - publicKey - ) - return unsignedTxBuild - } + if (options.waitForTxConfirmation) { + await waitForTxConfirmation(result.txId, CONFIRMATIONS, REQUEST_INTERVAL) + } - async mint(signer: SignerProvider, value: TokenAmount, tokenId: string) { - const tx = await this.mintTx(signer, value, tokenId) - return await signAndSend(signer, tx) + return result.txId } async getAllBalances(tokens: string[], owner: Address): Promise> { @@ -122,52 +119,31 @@ export class FungibleToken { return BigInt((await nodeProvider.fetchFungibleTokenMetaData(tokenId)).decimals) } - async airdropTx( + async airdrop( signer: SignerProvider, valueOne: TokenAmount, tokenOneId: string, valueTwo: TokenAmount, tokenTwoId: string, valueThree: TokenAmount, - tokenThreeId: string + tokenThreeId: string, + options: Options = DEFAULT_OPTIONS ) { - const builder = TransactionBuilder.from(web3.getCurrentNodeProvider()) - const bytecode = Airdrop.script.buildByteCodeToDeploy({ - tokenOne: tokenOneId, - amountOne: valueOne, - tokenTwo: tokenTwoId, - amountTwo: valueTwo, - tokenThree: tokenThreeId, - amountThree: valueThree + const result = await Airdrop.execute(signer, { + initialFields: { + tokenOne: tokenOneId, + amountOne: valueOne, + tokenTwo: tokenTwoId, + amountTwo: valueTwo, + tokenThree: tokenThreeId, + amountThree: valueThree + } }) - const { address, publicKey } = await signer.getSelectedAccount() - - const unsignedTxBuild = await builder.buildExecuteScriptTx( - { signerAddress: address, bytecode, attoAlphAmount: DUST_AMOUNT * 3n }, - publicKey - ) - return unsignedTxBuild - } + if (options.waitForTxConfirmation) { + await waitForTxConfirmation(result.txId, CONFIRMATIONS, REQUEST_INTERVAL) + } - async airdrop( - signer: SignerProvider, - valueOne: TokenAmount, - tokenOneId: string, - valueTwo: TokenAmount, - tokenTwoId: string, - valueThree: TokenAmount, - tokenThreeId: string - ) { - const tx = await this.airdropTx( - signer, - valueOne, - tokenOneId, - valueTwo, - tokenTwoId, - valueThree, - tokenThreeId - ) - return await signAndSend(signer, tx) + return result.txId } } diff --git a/src/invariant.ts b/src/invariant.ts index 4525e4e..37d2d59 100644 --- a/src/invariant.ts +++ b/src/invariant.ts @@ -159,16 +159,16 @@ export class Invariant { feeTier: FeeTier, options: Options = DEFAULT_OPTIONS ): Promise { - const { txId } = await AddFeeTier.execute(signer, { + const result = await AddFeeTier.execute(signer, { initialFields: { invariant: this.instance.contractId, feeTier: wrapFeeTier(feeTier) }, attoAlphAmount: MAP_ENTRY_DEPOSIT }) if (options.waitForTxConfirmation) { - await waitForTxConfirmation(txId, CONFIRMATIONS, REQUEST_INTERVAL) + await waitForTxConfirmation(result.txId, CONFIRMATIONS, REQUEST_INTERVAL) } - return txId + return result.txId } async removeFeeTier( From f76acae395193cc166cb5f4add097186d5347bcd Mon Sep 17 00:00:00 2001 From: zielvna Date: Tue, 8 Oct 2024 17:17:47 +0200 Subject: [PATCH 4/5] fix airdrop method and bump sdk --- package-lock.json | 4 ++-- package.json | 2 +- src/fungible-token.ts | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0ebdab7..d962e54 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@invariant-labs/alph-sdk", - "version": "0.0.15", + "version": "0.0.16", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@invariant-labs/alph-sdk", - "version": "0.0.15", + "version": "0.0.16", "license": "GPL", "dependencies": { "@alephium/cli": "^1.7.3", diff --git a/package.json b/package.json index dc7ddf4..f5e06f6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@invariant-labs/alph-sdk", - "version": "0.0.15", + "version": "0.0.16", "collaborators": [ "Invariant Labs" ], diff --git a/src/fungible-token.ts b/src/fungible-token.ts index a9cbefe..0d3bc22 100644 --- a/src/fungible-token.ts +++ b/src/fungible-token.ts @@ -137,7 +137,8 @@ export class FungibleToken { amountTwo: valueTwo, tokenThree: tokenThreeId, amountThree: valueThree - } + }, + attoAlphAmount: DUST_AMOUNT * 3n }) if (options.waitForTxConfirmation) { From a5b071f10003341d96f4126c1d83b515496944c6 Mon Sep 17 00:00:00 2001 From: zielvna Date: Tue, 8 Oct 2024 17:29:50 +0200 Subject: [PATCH 5/5] fix mint method and bump sdk --- package-lock.json | 4 ++-- package.json | 2 +- src/fungible-token.ts | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index d962e54..70985f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@invariant-labs/alph-sdk", - "version": "0.0.16", + "version": "0.0.17", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@invariant-labs/alph-sdk", - "version": "0.0.16", + "version": "0.0.17", "license": "GPL", "dependencies": { "@alephium/cli": "^1.7.3", diff --git a/package.json b/package.json index f5e06f6..942c2cc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@invariant-labs/alph-sdk", - "version": "0.0.16", + "version": "0.0.17", "collaborators": [ "Invariant Labs" ], diff --git a/src/fungible-token.ts b/src/fungible-token.ts index 0d3bc22..3d7cf58 100644 --- a/src/fungible-token.ts +++ b/src/fungible-token.ts @@ -67,7 +67,8 @@ export class FungibleToken { options: Options = DEFAULT_OPTIONS ) { const result = await Withdraw.execute(signer, { - initialFields: { token: tokenId, amount: value } + initialFields: { token: tokenId, amount: value }, + attoAlphAmount: DUST_AMOUNT }) if (options.waitForTxConfirmation) {