diff --git a/src/lib/kandel/distribution.test.ts b/src/lib/kandel/distribution.test.ts new file mode 100644 index 0000000..ad6faf8 --- /dev/null +++ b/src/lib/kandel/distribution.test.ts @@ -0,0 +1,87 @@ +import { parseAbi } from 'viem' +import { expect, it } from 'vitest' +import { describe, inject } from 'vitest' +import { getClient } from '~test/src/client.js' +import { getKandelPositionRawParams } from './params.js' +import { createGeometricDistribution } from './distribution.js' + +const { kandelLib, smartKandelSeeder } = inject('kandel') +const { routerProxyFactory, mangrove } = inject('mangrove') +const { wethUSDC } = inject('markets') + +const client = getClient() + +const kandelLibAbi = parseAbi([ + 'struct DistributionOffer {uint index;int tick;uint gives;}', + 'struct Distribution {DistributionOffer[] asks;DistributionOffer[] bids;}', + 'function createGeometricDistribution(uint from,uint to,int baseQuoteTickIndex0,uint _baseQuoteTickOffset,uint firstAskIndex,uint bidGives,uint askGives,uint pricePoints,uint stepSize) external pure returns (Distribution memory distribution)', +]) + +describe('distribution', () => { + it('checks kandel deployment', async () => { + const [factory, mgv] = await client.multicall({ + contracts: [ + { + address: smartKandelSeeder, + abi: parseAbi(['function PROXY_FACTORY() view returns (address)']), + functionName: 'PROXY_FACTORY', + }, + { + address: smartKandelSeeder, + abi: parseAbi(['function MGV() view returns (address)']), + functionName: 'MGV', + }, + ], + }) + expect(factory.status).toEqual('success') + expect(factory.result).toAddressEqual(routerProxyFactory) + expect(mgv.status).toEqual('success') + expect(mgv.result).toAddressEqual(mangrove) + }) + + it('checks kandel distribution', async () => { + const params = getKandelPositionRawParams({ + minPrice: 2500, + midPrice: 3000, + maxPrice: 3500, + pricePoints: 10n, + market: wethUSDC, + }) + + const distrib = createGeometricDistribution({ + ...params, + stepSize: 1n, + market: wethUSDC, + bidGives: 1n, + askGives: 1n, + }) + + const fromChain = await client.readContract({ + address: kandelLib, + abi: kandelLibAbi, + functionName: 'createGeometricDistribution', + args: [ + 0n, + 10n, + params.baseQuoteTickIndex0, + params.baseQuoteTickOffset, + params.firstAskIndex, + 1n, + 1n, + params.pricePoints, + 1n, + ], + }) + + for (let i = 0; i < distrib.asks.length; i++) { + expect(distrib.asks[i].gives).toEqual(fromChain.asks[i].gives) + expect(distrib.asks[i].tick).toEqual(fromChain.asks[i].tick) + expect(distrib.asks[i].index).toEqual(fromChain.asks[i].index) + } + for (let i = 0; i < distrib.bids.length; i++) { + expect(distrib.bids[i].gives).toEqual(fromChain.bids[i].gives) + expect(distrib.bids[i].tick).toEqual(fromChain.bids[i].tick) + expect(distrib.bids[i].index).toEqual(fromChain.bids[i].index) + } + }) +}) diff --git a/test/globalSetup.ts b/test/globalSetup.ts index 6215ec6..1ef9dca 100644 --- a/test/globalSetup.ts +++ b/test/globalSetup.ts @@ -1,5 +1,5 @@ import { createAnvil, startProxy } from '@viem/anvil' -import { type Address, parseEther, parseUnits } from 'viem' +import { type Address, parseEther, parseUnits, parseAbi } from 'viem' import { foundry } from 'viem/chains' import type { GlobalSetupContext } from 'vitest/node' import type { MarketParams, Token } from '~mgv/index.js' @@ -11,10 +11,13 @@ import { deployMangroveOrder, deployMangroveReader, deployRouterProxyFactory, + deploySmartKandel, openMarket, setMulticall, } from './src/contracts/index.js' import { getMangroveBytecodes } from './src/contracts/mangrove.js' +import { kandellibBytecode } from './src/contracts/kandellib.bytecode.js' +import { smartKandelSeederBytecode } from './src/contracts/smart-kandel-seeder.bytecode.js' export const multicall: Address = '0xcA11bde05977b3631167028862bE2a173976CA11' @@ -60,15 +63,32 @@ export default async function ({ provide }: GlobalSetupContext) { data.mangroveOrder, ) + const routerImplementation = await globalTestClient.readContract({ + address: mangroveOrder, + abi: parseAbi(['function ROUTER_IMPLEMENTATION() view returns (address)']), + functionName: 'ROUTER_IMPLEMENTATION', + }) + + const { kandelLib, smartKandelSeeder } = await deploySmartKandel( + mangrove, + 250_000n, + routerProxyFactory, + routerImplementation, + kandellibBytecode, + smartKandelSeederBytecode, + ) + provide('tokens', { WETH, USDC, DAI }) provide('mangrove', { mangrove, reader: mangroveReader, order: mangroveOrder, routerProxyFactory, + routerImplementation, multicall, tickSpacing: 60n, }) + provide('kandel', { kandelLib, smartKandelSeeder }) // open markets @@ -112,6 +132,7 @@ export default async function ({ provide }: GlobalSetupContext) { interface CustomMatchers { toApproximateEqual: (expected: number, percentage?: number) => R + toAddressEqual: (expected: Address) => R } declare module 'vitest' { @@ -128,6 +149,7 @@ declare module 'vitest' { reader: Address order: Address routerProxyFactory: Address + routerImplementation: Address multicall: Address tickSpacing: bigint } @@ -135,5 +157,9 @@ declare module 'vitest' { wethUSDC: MarketParams wethDAI: MarketParams } + kandel: { + kandelLib: Address + smartKandelSeeder: Address + } } } diff --git a/test/setup.ts b/test/setup.ts index 0ff0943..7a61601 100644 --- a/test/setup.ts +++ b/test/setup.ts @@ -1,3 +1,4 @@ +import { Address, isAddressEqual } from 'viem' import { expect } from 'vitest' expect.extend({ @@ -19,4 +20,11 @@ expect.extend({ `expected ${received} to be approximately equal to ${expected}`, } }, + + toAddressEqual: (received: Address, expected: Address) => { + return { + pass: isAddressEqual(received, expected), + message: () => `expected ${received} to be equal to ${expected}`, + } + }, }) diff --git a/test/src/contracts/index.ts b/test/src/contracts/index.ts index 922bdcf..df7f67e 100644 --- a/test/src/contracts/index.ts +++ b/test/src/contracts/index.ts @@ -45,7 +45,25 @@ export async function deploySmartKandel( chain: globalTestClient.chain, bytecode: kandelLibBytecode, abi: parseAbi(["constructor()"]), - }) + } as any) + const libReceipt = await globalTestClient.waitForTransactionReceipt({ + hash: libTx, + }); + const libAddress = libReceipt.contractAddress; + const seederTx = await globalTestClient.deployContract({ + account: globalTestClient.account, + chain: globalTestClient.chain, + bytecode: smartKandelSeederBytecode.replace(/__\$[a-fA-F0-9]{34}\$__/g, libAddress.slice(2)), + abi: parseAbi([ + "constructor(address mgv, uint gasreq, address routerProxyFactory, address routerImplementation)", + ]), + args: [mgv, kandelGasreq, routerProxyFactory, routerImplementation], + } as any); + const seederReceipt = await globalTestClient.waitForTransactionReceipt({ + hash: seederTx, + }); + const seederAddress = seederReceipt.contractAddress; + return {kandelLib: libAddress, smartKandelSeeder: seederAddress}; } export async function deployMangroveCore(bytecode: Hex): Promise
{ diff --git a/test/src/contracts/mangrove.ts b/test/src/contracts/mangrove.ts index 8c1e634..a5587e9 100644 --- a/test/src/contracts/mangrove.ts +++ b/test/src/contracts/mangrove.ts @@ -1,18 +1,9 @@ import { Hex } from "viem"; -import { join } from "node:path"; -import { readFile } from "node:fs/promises"; import { bytecode as mangrove } from "./mangrove.bytecode.js"; import { bytecode as mangroveReader } from "./mgv-reader.bytecode.js"; import { bytecode as mangroveOrder } from "./mgv-order.bytecode.js"; import { bytecode as routerProxyFactory } from "./router-proxy-factory.bytecode.js"; -async function readBytecode(src: string): Promise { - // return file(join(import.meta.dir, src)).text() as any; - const path = join(import.meta.url, "..", src); - const data = await readFile(path); - return data.toString() as Hex; -} - export async function getMangroveBytecodes() { return { mangrove: mangrove as Hex,