-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #98 from invariant-labs/init-invariant-class
Init invariant class
- Loading branch information
Showing
3 changed files
with
368 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,331 @@ | ||
import { | ||
AddFeeTier, | ||
ChangeFeeReceiver, | ||
ChangeProtocolFee, | ||
ClaimFee, | ||
CLAMM, | ||
CreatePool, | ||
CreatePosition, | ||
Invariant as InvariantFactory, | ||
InvariantInstance, | ||
RemoveFeeTier, | ||
RemovePosition, | ||
Reserve, | ||
Swap, | ||
TransferPosition, | ||
WithdrawProtocolFee | ||
} from '../artifacts/ts' | ||
import { FeeTier, Pool, PoolKey, Position, QuoteResult, Tick } from '../artifacts/ts/types' | ||
import { calculateTick } from './math' | ||
import { Network } from './network' | ||
import { getReserveAddress } from './testUtils' | ||
import { | ||
balanceOf, | ||
decodeFeeTiers, | ||
decodePool, | ||
decodePosition, | ||
decodeTick, | ||
deployCLAMM, | ||
deployReserve, | ||
MAP_ENTRY_DEPOSIT, | ||
waitTxConfirmed | ||
} from './utils' | ||
import { Address, DUST_AMOUNT, SignerProvider, ZERO_ADDRESS } from '@alephium/web3' | ||
|
||
export class Invariant { | ||
instance: InvariantInstance | ||
network: Network | ||
address: Address | ||
|
||
private constructor(address: Address, network: Network) { | ||
this.address = address | ||
this.instance = InvariantFactory.at(address) | ||
this.network = network | ||
} | ||
|
||
static async deploy( | ||
signer: SignerProvider, | ||
network: Network, | ||
protocolFee: bigint = 0n | ||
): Promise<Invariant> { | ||
const account = await signer.getSelectedAccount() | ||
const clamm = await deployCLAMM(signer) | ||
const reserve = await deployReserve(signer) | ||
|
||
const deployResult = await waitTxConfirmed( | ||
InvariantFactory.deploy(signer, { | ||
initialFields: { | ||
config: { admin: account.address, protocolFee }, | ||
reserveTemplateId: reserve.contractId, | ||
lastReserveId: reserve.contractId, | ||
clamm: clamm.contractId, | ||
feeTierCount: 0n, | ||
poolKeyCount: 0n | ||
} | ||
}) | ||
) | ||
|
||
return new Invariant(deployResult.contractInstance.address, network) | ||
} | ||
|
||
static async load(address: Address, network: Network): Promise<Invariant> { | ||
return new Invariant(address, network) | ||
} | ||
|
||
async addFeeTier(signer: SignerProvider, feeTier: FeeTier): Promise<string> { | ||
const { txId } = await waitTxConfirmed( | ||
AddFeeTier.execute(signer, { | ||
initialFields: { | ||
invariant: this.instance.contractId, | ||
feeTier | ||
}, | ||
attoAlphAmount: MAP_ENTRY_DEPOSIT | ||
}) | ||
) | ||
return txId | ||
} | ||
|
||
async removeFeeTier(signer: SignerProvider, feeTier: FeeTier): Promise<string> { | ||
const { txId } = await waitTxConfirmed( | ||
RemoveFeeTier.execute(signer, { | ||
initialFields: { | ||
invariant: this.instance.contractId, | ||
feeTier | ||
} | ||
}) | ||
) | ||
return txId | ||
} | ||
|
||
async createPool( | ||
signer: SignerProvider, | ||
token0Id: string, | ||
token1Id: string, | ||
feeTier: FeeTier, | ||
initSqrtPrice: bigint | ||
): Promise<string> { | ||
const initTick = await calculateTick(initSqrtPrice, feeTier.tickSpacing) | ||
|
||
const { txId } = await waitTxConfirmed( | ||
CreatePool.execute(signer, { | ||
initialFields: { | ||
invariant: this.instance.contractId, | ||
token0: token0Id, | ||
token1: token1Id, | ||
feeTier, | ||
initSqrtPrice, | ||
initTick | ||
}, | ||
attoAlphAmount: MAP_ENTRY_DEPOSIT * 5n | ||
}) | ||
) | ||
return txId | ||
} | ||
|
||
async withdrawProtocolFee(signer: SignerProvider, poolKey: PoolKey): Promise<string> { | ||
const { txId } = await waitTxConfirmed( | ||
WithdrawProtocolFee.execute(signer, { | ||
initialFields: { | ||
invariant: this.instance.contractId, | ||
poolKey | ||
} | ||
}) | ||
) | ||
return txId | ||
} | ||
async changeFeeReceiver( | ||
signer: SignerProvider, | ||
poolKey: PoolKey, | ||
newFeeReceiver: Address | ||
): Promise<string> { | ||
const { txId } = await waitTxConfirmed( | ||
ChangeFeeReceiver.execute(signer, { | ||
initialFields: { | ||
invariant: this.instance.contractId, | ||
poolKey, | ||
newFeeReceiver | ||
} | ||
}) | ||
) | ||
return txId | ||
} | ||
async changeProtocolFee(signer: SignerProvider, fee: bigint): Promise<string> { | ||
const { txId } = await waitTxConfirmed( | ||
ChangeProtocolFee.execute(signer, { | ||
initialFields: { | ||
invariant: this.instance.contractId, | ||
newFee: fee | ||
} | ||
}) | ||
) | ||
return txId | ||
} | ||
|
||
async createPosition( | ||
signer: SignerProvider, | ||
poolKey: PoolKey, | ||
lowerTick: bigint, | ||
upperTick: bigint, | ||
liquidityDelta: bigint, | ||
approvedTokensX: bigint, | ||
approvedTokensY: bigint, | ||
slippageLimitLower: bigint, | ||
slippageLimitUpper: bigint | ||
): Promise<string> { | ||
const { txId } = await waitTxConfirmed( | ||
CreatePosition.execute(signer, { | ||
initialFields: { | ||
invariant: this.instance.contractId, | ||
poolKey, | ||
lowerTick, | ||
upperTick, | ||
liquidityDelta, | ||
approvedTokensX, | ||
approvedTokensY, | ||
slippageLimitLower, | ||
slippageLimitUpper | ||
}, | ||
tokens: [ | ||
{ id: poolKey.tokenX, amount: approvedTokensX }, | ||
{ id: poolKey.tokenY, amount: approvedTokensY } | ||
], | ||
attoAlphAmount: MAP_ENTRY_DEPOSIT * 6n + DUST_AMOUNT * 2n | ||
}) | ||
) | ||
|
||
return txId | ||
} | ||
async removePosition(signer: SignerProvider, index: bigint): Promise<string> { | ||
const { txId } = await waitTxConfirmed( | ||
RemovePosition.execute(signer, { | ||
initialFields: { | ||
invariant: this.instance.contractId, | ||
index | ||
} | ||
}) | ||
) | ||
return txId | ||
} | ||
async claimFee(signer: SignerProvider, index: bigint): Promise<string> { | ||
const { txId } = await waitTxConfirmed( | ||
ClaimFee.execute(signer, { | ||
initialFields: { | ||
invariant: this.instance.contractId, | ||
index | ||
}, | ||
attoAlphAmount: DUST_AMOUNT | ||
}) | ||
) | ||
return txId | ||
} | ||
async transferPosition( | ||
signer: SignerProvider, | ||
index: bigint, | ||
recipient: Address | ||
): Promise<string> { | ||
const { txId } = await waitTxConfirmed( | ||
TransferPosition.execute(signer, { | ||
initialFields: { | ||
invariant: this.instance.contractId, | ||
index, | ||
recipient | ||
}, | ||
attoAlphAmount: 2n * MAP_ENTRY_DEPOSIT | ||
}) | ||
) | ||
return txId | ||
} | ||
async swap( | ||
signer: SignerProvider, | ||
poolKey: PoolKey, | ||
xToY: boolean, | ||
amount: bigint, | ||
byAmountIn: boolean, | ||
sqrtPriceLimit: bigint, | ||
approvedAmount = amount | ||
): Promise<string> { | ||
const tokenId = xToY ? poolKey.tokenX : poolKey.tokenY | ||
const { txId } = await waitTxConfirmed( | ||
Swap.execute(signer, { | ||
initialFields: { | ||
invariant: this.instance.contractId, | ||
poolKey, | ||
xToY, | ||
amount, | ||
byAmountIn, | ||
sqrtPriceLimit, | ||
approvedAmount | ||
}, | ||
tokens: [{ id: tokenId, amount: approvedAmount }], | ||
attoAlphAmount: DUST_AMOUNT * 2n | ||
}) | ||
) | ||
return txId | ||
} | ||
|
||
async feeTierExist(feeTier: FeeTier): Promise<boolean> { | ||
return (await this.instance.view.feeTierExist({ args: { feeTier } })).returns | ||
} | ||
|
||
async getFeeTiers(): Promise<FeeTier[]> { | ||
return decodeFeeTiers((await this.instance.view.getAllFeeTiers()).returns) | ||
} | ||
|
||
async getPool(poolKey: PoolKey): Promise<Pool> { | ||
return decodePool((await this.instance.view.getPool({ args: { poolKey } })).returns) | ||
} | ||
|
||
async getPosition(owner: Address, index: bigint): Promise<Position> { | ||
return decodePosition( | ||
(await this.instance.view.getPosition({ args: { owner, index } })).returns | ||
) | ||
} | ||
|
||
async quote( | ||
poolKey: PoolKey, | ||
xToY: boolean, | ||
amount: bigint, | ||
byAmountIn: boolean, | ||
sqrtPriceLimit: bigint | ||
): Promise<QuoteResult> { | ||
return ( | ||
await this.instance.view.quote({ | ||
args: { poolKey, xToY, amount, byAmountIn, sqrtPriceLimit } | ||
}) | ||
).returns | ||
} | ||
|
||
async getTick(poolKey: PoolKey, index: bigint): Promise<Tick> { | ||
return decodeTick((await this.instance.view.getTick({ args: { poolKey, index } })).returns) | ||
} | ||
|
||
async isTickInitialized(poolKey: PoolKey, index: bigint): Promise<boolean> { | ||
return (await this.instance.view.isTickInitialized({ args: { poolKey, index } })).returns | ||
} | ||
|
||
async getReserveBalances(poolKey: PoolKey): Promise<{ x: bigint; y: bigint }> { | ||
const pool = await this.getPool(poolKey) | ||
const [reserveX, reserveY] = getReserveAddress(pool) | ||
const x = await balanceOf(poolKey.tokenX, reserveX) | ||
const y = await balanceOf(poolKey.tokenY, reserveY) | ||
return { x, y } | ||
} | ||
|
||
async getProtocolFee(): Promise<bigint> { | ||
return (await this.instance.view.getProtocolFee()).returns | ||
} | ||
|
||
// async getPositions() {} | ||
// async getAllPositions() {} | ||
// async getPoolKeys() {} | ||
// async getAllPoolKeys() {} | ||
// async swapWithSlippage() {} | ||
// async getPositionTicks() {} | ||
// async getRawTickmap() {} | ||
// async getFullTickmap() {} | ||
// async getLiquidityTicks() {} | ||
// async getAllLiquidityTicks() {} | ||
// async getUserPositionAmount() {} | ||
// async getLiquidityTicksAmount() {} | ||
// async getAllPoolsForPair() {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export enum Network { | ||
Local = 'Local', | ||
Devnet = 'Devnet', | ||
Testnet = 'Testnet', | ||
Mainnet = 'Mainnet' | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { ONE_ALPH, web3 } from '@alephium/web3' | ||
import { getSigner } from '@alephium/web3-test' | ||
import { PrivateKeyWallet } from '@alephium/web3-wallet' | ||
import { InvariantError, PercentageScale } from '../src/consts' | ||
import { expectError, feeTierExists, getFeeTiers, initFeeTier } from '../src/testUtils' | ||
import { deployInvariant, newFeeTier } from '../src/utils' | ||
import { FeeTier } from '../artifacts/ts/types' | ||
import { Invariant } from '../src/invariant' | ||
import { Network } from '../src/network' | ||
import { AddFeeTier } from '../artifacts/ts' | ||
|
||
web3.setCurrentNodeProvider('http://127.0.0.1:22973') | ||
|
||
describe('init invariant test', () => { | ||
test('deploy invariant works', async () => { | ||
const initialFee = 0n | ||
const deployer = await getSigner(ONE_ALPH * 1000n, 0) | ||
const invariant = await Invariant.deploy(deployer, Network.Local, initialFee) | ||
const protocolFee = await invariant.getProtocolFee() | ||
expect(protocolFee).toBe(0n) | ||
}) | ||
test('load invariant from address', async () => { | ||
const initialFee = 0n | ||
const deployer = await getSigner(ONE_ALPH * 1000n, 0) | ||
const invariant = await Invariant.deploy(deployer, Network.Local, initialFee) | ||
|
||
const loadedInvariant = await Invariant.load(invariant.address, Network.Local) | ||
const protocolFee = await loadedInvariant.getProtocolFee() | ||
expect(protocolFee).toBe(0n) | ||
}) | ||
}) |