From 19a954c50c52663a1abeeb99e7dc8881083bb085 Mon Sep 17 00:00:00 2001 From: Maxence Raballand Date: Tue, 23 Jul 2024 17:32:32 +0200 Subject: [PATCH 01/28] feat(addresses): added arbitrum tokens and markets (#117) * feat(addresses): added arbitrum tokens and markets * chore: format --------- Co-authored-by: maxencerb --- .changeset/curly-chicken-repair.md | 5 +++++ src/addresses/index.ts | 8 ++++++++ src/addresses/markets/arbitrum.ts | 26 ++++++++++++++++++++++++++ src/addresses/markets/index.ts | 7 +++++++ src/addresses/tokens/arbitrum.ts | 26 ++++++++++++++++++++++++++ src/addresses/tokens/index.ts | 7 +++++++ 6 files changed, 79 insertions(+) create mode 100644 .changeset/curly-chicken-repair.md create mode 100644 src/addresses/markets/arbitrum.ts create mode 100644 src/addresses/tokens/arbitrum.ts diff --git a/.changeset/curly-chicken-repair.md b/.changeset/curly-chicken-repair.md new file mode 100644 index 0000000..33123c7 --- /dev/null +++ b/.changeset/curly-chicken-repair.md @@ -0,0 +1,5 @@ +--- +"@mangrovedao/mgv": patch +--- + +Add arbitrum tokens and markets diff --git a/src/addresses/index.ts b/src/addresses/index.ts index 43832e2..201f094 100644 --- a/src/addresses/index.ts +++ b/src/addresses/index.ts @@ -14,6 +14,10 @@ export { baseSepoliaMarkets, baseSepoliaWBTCDAI, baseSepoliaWETHUSDC, + arbitrumMarkets, + arbitrumWETHUSDC, + arbitrumWETHUSDT, + arbitrumUSDCUSDT, } from './markets/index.js' // --- tokens --- @@ -32,6 +36,10 @@ export { baseSepoliaDAI, baseSepoliaWBTC, baseSepoliaTokens, + arbitrumWETH, + arbitrumUSDC, + arbitrumUSDT, + arbitrumTokens, } from './tokens/index.js' // --- mangrove --- diff --git a/src/addresses/markets/arbitrum.ts b/src/addresses/markets/arbitrum.ts new file mode 100644 index 0000000..b0f7980 --- /dev/null +++ b/src/addresses/markets/arbitrum.ts @@ -0,0 +1,26 @@ +import type { MarketParams } from '../../types/index.js' +import { arbitrumUSDC, arbitrumUSDT, arbitrumWETH } from '../tokens/arbitrum.js' + +export const arbitrumWETHUSDC = { + base: arbitrumWETH, + quote: arbitrumUSDC, + tickSpacing: 1n, +} as const satisfies MarketParams + +export const arbitrumWETHUSDT = { + base: arbitrumWETH, + quote: arbitrumUSDT, + tickSpacing: 1n, +} as const satisfies MarketParams + +export const arbitrumUSDCUSDT = { + base: arbitrumUSDC, + quote: arbitrumUSDT, + tickSpacing: 1n, +} as const satisfies MarketParams + +export const arbitrumMarkets = [ + arbitrumWETHUSDC, + arbitrumWETHUSDT, + arbitrumUSDCUSDT, +] as const satisfies MarketParams[] diff --git a/src/addresses/markets/index.ts b/src/addresses/markets/index.ts index 369b2e1..d9632d7 100644 --- a/src/addresses/markets/index.ts +++ b/src/addresses/markets/index.ts @@ -13,3 +13,10 @@ export { baseSepoliaWBTCDAI, baseSepoliaWETHUSDC, } from './base-sepolia.js' + +export { + arbitrumMarkets, + arbitrumWETHUSDC, + arbitrumWETHUSDT, + arbitrumUSDCUSDT, +} from './arbitrum.js' diff --git a/src/addresses/tokens/arbitrum.ts b/src/addresses/tokens/arbitrum.ts new file mode 100644 index 0000000..2c5eccf --- /dev/null +++ b/src/addresses/tokens/arbitrum.ts @@ -0,0 +1,26 @@ +import { buildToken } from './utils.js' + +export const arbitrumWETH = buildToken({ + address: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', + symbol: 'WETH', +}) + +export const arbitrumUSDC = buildToken({ + address: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', + symbol: 'USDC', + displayDecimals: 2, + priceDisplayDecimals: 4, +}) + +export const arbitrumUSDT = buildToken({ + address: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9', + symbol: 'USDT', + displayDecimals: 2, + priceDisplayDecimals: 4, +}) + +export const arbitrumTokens = [ + arbitrumWETH, + arbitrumUSDC, + arbitrumUSDT, +] as const diff --git a/src/addresses/tokens/index.ts b/src/addresses/tokens/index.ts index b3f5918..bfe7730 100644 --- a/src/addresses/tokens/index.ts +++ b/src/addresses/tokens/index.ts @@ -16,6 +16,13 @@ export { baseSepoliaTokens, } from './base-sepolia.js' +export { + arbitrumWETH, + arbitrumUSDC, + arbitrumUSDT, + arbitrumTokens, +} from './arbitrum.js' + export type { Token, BuildTokenParms, From 5a5a533c42d88b97c2d8af9cd982faaf9ff67e8c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 23 Jul 2024 17:35:08 +0200 Subject: [PATCH 02/28] Version Packages (#118) Co-authored-by: github-actions[bot] --- .changeset/curly-chicken-repair.md | 5 ----- src/CHANGELOG.md | 6 ++++++ src/package.json | 15 ++++++++++++--- 3 files changed, 18 insertions(+), 8 deletions(-) delete mode 100644 .changeset/curly-chicken-repair.md diff --git a/.changeset/curly-chicken-repair.md b/.changeset/curly-chicken-repair.md deleted file mode 100644 index 33123c7..0000000 --- a/.changeset/curly-chicken-repair.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@mangrovedao/mgv": patch ---- - -Add arbitrum tokens and markets diff --git a/src/CHANGELOG.md b/src/CHANGELOG.md index b9b8c47..00b029f 100644 --- a/src/CHANGELOG.md +++ b/src/CHANGELOG.md @@ -1,5 +1,11 @@ # @mangrovedao/mgv +## 0.9.2 + +### Patch Changes + +- 19a954c: Add arbitrum tokens and markets + ## 0.9.1 ### Patch Changes diff --git a/src/package.json b/src/package.json index 12ba9f2..365ebc3 100644 --- a/src/package.json +++ b/src/package.json @@ -1,7 +1,7 @@ { "name": "@mangrovedao/mgv", "description": "Utils and functions to interact with mangrove protocol", - "version": "0.9.1", + "version": "0.9.2", "repository": { "url": "https://github.com/mangrovedao/mgv" }, @@ -64,6 +64,15 @@ }, "license": "MIT", "homepage": "https://mangrove.exchange", - "authors": ["maxencerb.eth"], - "keywords": ["eth", "ethereum", "dapp", "mangrove", "order", "book"] + "authors": [ + "maxencerb.eth" + ], + "keywords": [ + "eth", + "ethereum", + "dapp", + "mangrove", + "order", + "book" + ] } From c73ec33019746a2257898c18fbe559ebb2df22d1 Mon Sep 17 00:00:00 2001 From: maxencerb Date: Tue, 23 Jul 2024 15:35:29 +0000 Subject: [PATCH 03/28] chore: format --- src/package.json | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/package.json b/src/package.json index 365ebc3..032073b 100644 --- a/src/package.json +++ b/src/package.json @@ -64,15 +64,6 @@ }, "license": "MIT", "homepage": "https://mangrove.exchange", - "authors": [ - "maxencerb.eth" - ], - "keywords": [ - "eth", - "ethereum", - "dapp", - "mangrove", - "order", - "book" - ] + "authors": ["maxencerb.eth"], + "keywords": ["eth", "ethereum", "dapp", "mangrove", "order", "book"] } From 5c63a0c7eae9baad32e705ab143b84eb26746912 Mon Sep 17 00:00:00 2001 From: Maxence Raballand Date: Mon, 5 Aug 2024 16:51:46 +0200 Subject: [PATCH 04/28] fix: token decimals (#120) * fix: token decimals * chore: changeset --- .changeset/six-masks-lie.md | 5 +++++ src/addresses/tokens/arbitrum.ts | 2 ++ 2 files changed, 7 insertions(+) create mode 100644 .changeset/six-masks-lie.md diff --git a/.changeset/six-masks-lie.md b/.changeset/six-masks-lie.md new file mode 100644 index 0000000..4f9adbb --- /dev/null +++ b/.changeset/six-masks-lie.md @@ -0,0 +1,5 @@ +--- +"@mangrovedao/mgv": patch +--- + +Fixed USDC and USDT decimals on arbitrum diff --git a/src/addresses/tokens/arbitrum.ts b/src/addresses/tokens/arbitrum.ts index 2c5eccf..b4327a0 100644 --- a/src/addresses/tokens/arbitrum.ts +++ b/src/addresses/tokens/arbitrum.ts @@ -8,6 +8,7 @@ export const arbitrumWETH = buildToken({ export const arbitrumUSDC = buildToken({ address: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', symbol: 'USDC', + decimals: 6, displayDecimals: 2, priceDisplayDecimals: 4, }) @@ -15,6 +16,7 @@ export const arbitrumUSDC = buildToken({ export const arbitrumUSDT = buildToken({ address: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9', symbol: 'USDT', + decimals: 6, displayDecimals: 2, priceDisplayDecimals: 4, }) From abb8f421d63c6b0e313b764b52157573d62df9a7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 16:52:17 +0200 Subject: [PATCH 05/28] chore: version package (#121) Co-authored-by: github-actions[bot] --- .changeset/six-masks-lie.md | 5 ----- src/CHANGELOG.md | 6 ++++++ src/package.json | 15 ++++++++++++--- 3 files changed, 18 insertions(+), 8 deletions(-) delete mode 100644 .changeset/six-masks-lie.md diff --git a/.changeset/six-masks-lie.md b/.changeset/six-masks-lie.md deleted file mode 100644 index 4f9adbb..0000000 --- a/.changeset/six-masks-lie.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@mangrovedao/mgv": patch ---- - -Fixed USDC and USDT decimals on arbitrum diff --git a/src/CHANGELOG.md b/src/CHANGELOG.md index 00b029f..0f02d2d 100644 --- a/src/CHANGELOG.md +++ b/src/CHANGELOG.md @@ -1,5 +1,11 @@ # @mangrovedao/mgv +## 0.9.3 + +### Patch Changes + +- 5c63a0c: Fixed USDC and USDT decimals on arbitrum + ## 0.9.2 ### Patch Changes diff --git a/src/package.json b/src/package.json index 032073b..5fb6498 100644 --- a/src/package.json +++ b/src/package.json @@ -1,7 +1,7 @@ { "name": "@mangrovedao/mgv", "description": "Utils and functions to interact with mangrove protocol", - "version": "0.9.2", + "version": "0.9.3", "repository": { "url": "https://github.com/mangrovedao/mgv" }, @@ -64,6 +64,15 @@ }, "license": "MIT", "homepage": "https://mangrove.exchange", - "authors": ["maxencerb.eth"], - "keywords": ["eth", "ethereum", "dapp", "mangrove", "order", "book"] + "authors": [ + "maxencerb.eth" + ], + "keywords": [ + "eth", + "ethereum", + "dapp", + "mangrove", + "order", + "book" + ] } From eae01e501d4513458b45278058f76489d1977b25 Mon Sep 17 00:00:00 2001 From: maxencerb Date: Mon, 5 Aug 2024 14:52:46 +0000 Subject: [PATCH 06/28] chore: format --- src/package.json | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/package.json b/src/package.json index 5fb6498..1139c7a 100644 --- a/src/package.json +++ b/src/package.json @@ -64,15 +64,6 @@ }, "license": "MIT", "homepage": "https://mangrove.exchange", - "authors": [ - "maxencerb.eth" - ], - "keywords": [ - "eth", - "ethereum", - "dapp", - "mangrove", - "order", - "book" - ] + "authors": ["maxencerb.eth"], + "keywords": ["eth", "ethereum", "dapp", "mangrove", "order", "book"] } From c88d3a1f5015689d2383caafb4730566230ffbb9 Mon Sep 17 00:00:00 2001 From: Maxence Raballand Date: Tue, 13 Aug 2024 15:51:14 +0200 Subject: [PATCH 07/28] Fix/invalid param names (#122) * Fix: invalid param names * chore: changeset * chore: format * chore: delete test * Revert "chore: delete test" This reverts commit f722419beb3e72a59b3d360b8a82d3aa18011233. * chore: remove test --------- Co-authored-by: maxencerb --- .changeset/lemon-gifts-boil.md | 5 +++++ src/builder/kandel/populate.ts | 29 ++++++++++++++++++----------- 2 files changed, 23 insertions(+), 11 deletions(-) create mode 100644 .changeset/lemon-gifts-boil.md diff --git a/.changeset/lemon-gifts-boil.md b/.changeset/lemon-gifts-boil.md new file mode 100644 index 0000000..f06ae40 --- /dev/null +++ b/.changeset/lemon-gifts-boil.md @@ -0,0 +1,5 @@ +--- +"@mangrovedao/mgv": patch +--- + +Fix invalid param names on kandel diff --git a/src/builder/kandel/populate.ts b/src/builder/kandel/populate.ts index 50a7307..d72d46d 100644 --- a/src/builder/kandel/populate.ts +++ b/src/builder/kandel/populate.ts @@ -8,8 +8,8 @@ export const populateABI = parseAbi([ ]) export type PopulateFromOffsetParams = { - from?: bigint | undefined - to?: bigint | undefined + fromIndex?: bigint | undefined + toIndex?: bigint | undefined baseQuoteTickIndex0: bigint baseQuoteTickOffset: bigint firstAskIndex: bigint @@ -23,8 +23,8 @@ export type PopulateFromOffsetParams = { quoteAmount?: bigint | undefined } export type PopulateChunkFromOffsetParams = { - from?: bigint | undefined - to: bigint + fromIndex?: bigint | undefined + toIndex: bigint baseQuoteTickIndex0: bigint firstAskIndex: bigint bidGives: bigint @@ -40,8 +40,8 @@ export function populateFromOffsetParams(params: PopulateFromOffsetParams) { gasreq, pricePoints, stepSize, - from = 0n, - to = pricePoints, + fromIndex = 0n, + toIndex = pricePoints, baseAmount = 0n, quoteAmount = 0n, gasprice = 0n, @@ -50,8 +50,8 @@ export function populateFromOffsetParams(params: PopulateFromOffsetParams) { abi: populateABI, functionName: 'populateFromOffset', args: [ - from, - to, + fromIndex, + toIndex, baseQuoteTickIndex0, baseQuoteTickOffset, firstAskIndex, @@ -84,13 +84,20 @@ export function populateChunkFromOffsetParams( firstAskIndex, bidGives, askGives, - from = 0n, - to, + fromIndex = 0n, + toIndex, } = params return { abi: populateABI, functionName: 'populateChunkFromOffset', - args: [from, to, baseQuoteTickIndex0, firstAskIndex, bidGives, askGives], + args: [ + fromIndex, + toIndex, + baseQuoteTickIndex0, + firstAskIndex, + bidGives, + askGives, + ], } satisfies Omit< ContractFunctionParameters< typeof populateABI, From 87dbcdcafd788709befe07a37e4ca13cd258fabb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 15:52:11 +0200 Subject: [PATCH 08/28] chore: version package (#123) Co-authored-by: github-actions[bot] --- .changeset/lemon-gifts-boil.md | 5 ----- src/CHANGELOG.md | 6 ++++++ src/package.json | 15 ++++++++++++--- 3 files changed, 18 insertions(+), 8 deletions(-) delete mode 100644 .changeset/lemon-gifts-boil.md diff --git a/.changeset/lemon-gifts-boil.md b/.changeset/lemon-gifts-boil.md deleted file mode 100644 index f06ae40..0000000 --- a/.changeset/lemon-gifts-boil.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@mangrovedao/mgv": patch ---- - -Fix invalid param names on kandel diff --git a/src/CHANGELOG.md b/src/CHANGELOG.md index 0f02d2d..e1e3798 100644 --- a/src/CHANGELOG.md +++ b/src/CHANGELOG.md @@ -1,5 +1,11 @@ # @mangrovedao/mgv +## 0.9.4 + +### Patch Changes + +- c88d3a1: Fix invalid param names on kandel + ## 0.9.3 ### Patch Changes diff --git a/src/package.json b/src/package.json index 1139c7a..34b78d5 100644 --- a/src/package.json +++ b/src/package.json @@ -1,7 +1,7 @@ { "name": "@mangrovedao/mgv", "description": "Utils and functions to interact with mangrove protocol", - "version": "0.9.3", + "version": "0.9.4", "repository": { "url": "https://github.com/mangrovedao/mgv" }, @@ -64,6 +64,15 @@ }, "license": "MIT", "homepage": "https://mangrove.exchange", - "authors": ["maxencerb.eth"], - "keywords": ["eth", "ethereum", "dapp", "mangrove", "order", "book"] + "authors": [ + "maxencerb.eth" + ], + "keywords": [ + "eth", + "ethereum", + "dapp", + "mangrove", + "order", + "book" + ] } From e6a3c429dc13a170853129523527490437340a61 Mon Sep 17 00:00:00 2001 From: maxencerb Date: Tue, 13 Aug 2024 13:52:48 +0000 Subject: [PATCH 09/28] chore: format --- src/package.json | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/package.json b/src/package.json index 34b78d5..8a087aa 100644 --- a/src/package.json +++ b/src/package.json @@ -64,15 +64,6 @@ }, "license": "MIT", "homepage": "https://mangrove.exchange", - "authors": [ - "maxencerb.eth" - ], - "keywords": [ - "eth", - "ethereum", - "dapp", - "mangrove", - "order", - "book" - ] + "authors": ["maxencerb.eth"], + "keywords": ["eth", "ethereum", "dapp", "mangrove", "order", "book"] } From f838392ac9e13bc1e343aab59ca62ade42f1e382 Mon Sep 17 00:00:00 2001 From: Maxence Raballand Date: Wed, 14 Aug 2024 15:08:30 +0200 Subject: [PATCH 10/28] Feat/maxence/aave check (#124) * feat: test aave markets * chore: changeset * chore: format --------- Co-authored-by: maxencerb --- .changeset/rich-trees-hope.md | 5 ++ src/actions/kandel/aave.ts | 114 ++++++++++++++++++++++++++++ src/builder/kandel/aave.ts | 24 ++++++ src/bundle/index.ts | 2 + src/bundle/public/index.ts | 2 + src/bundle/public/kandel-actions.ts | 26 +++++++ src/index.ts | 2 + 7 files changed, 175 insertions(+) create mode 100644 .changeset/rich-trees-hope.md create mode 100644 src/actions/kandel/aave.ts create mode 100644 src/builder/kandel/aave.ts diff --git a/.changeset/rich-trees-hope.md b/.changeset/rich-trees-hope.md new file mode 100644 index 0000000..0476c90 --- /dev/null +++ b/.changeset/rich-trees-hope.md @@ -0,0 +1,5 @@ +--- +"@mangrovedao/mgv": patch +--- + +Added check for aave markets diff --git a/src/actions/kandel/aave.ts b/src/actions/kandel/aave.ts new file mode 100644 index 0000000..c29e1ee --- /dev/null +++ b/src/actions/kandel/aave.ts @@ -0,0 +1,114 @@ +import type { + Address, + Client, + MulticallParameters, + ReadContractParameters, +} from 'viem' +import { multicall, readContract } from 'viem/actions' +import { + type CheckAaveAssetParams, + type aaveRouterCheckAssetABI, + checkAaveAssetParams, +} from '../../builder/kandel/aave.js' +import type { BuiltArgs, MarketParams } from '../../index.js' +import { getAction } from '../../utils/getAction.js' + +type ReadSingleParams = ReadContractParameters< + typeof aaveRouterCheckAssetABI, + 'checkAsset' +> + +export type CheckAaveAssetArgs = CheckAaveAssetParams & + Omit + +export async function checkAaveAsset( + client: Client, + aaveRouter: Address, + args: CheckAaveAssetArgs, +): Promise { + return getAction( + client, + readContract, + 'readContract', + )({ + ...(args as unknown as ReadSingleParams), + address: aaveRouter, + ...checkAaveAssetParams(args), + }) +} + +export type CheckAaveAssetsArgs = { tokens: Address[] } & Omit< + MulticallParameters, + 'contracts' | 'allowFailure' +> + +export async function checkAaveAssets( + client: Client, + aaveRouter: Address, + args: CheckAaveAssetsArgs, +): Promise { + return getAction( + client, + multicall, + 'multicall', + )({ + ...args, + contracts: args.tokens.map((token) => ({ + address: aaveRouter, + ...checkAaveAssetParams({ token }), + })), + allowFailure: false, + }) +} + +export type CheckAaveMarketArgs = { market: MarketParams } & Omit< + MulticallParameters, + 'contracts' | 'allowFailure' +> + +export async function checkAaveMarket( + client: Client, + aaveRouter: Address, + args: CheckAaveMarketArgs, +): Promise { + const tokens = [args.market.base.address, args.market.quote.address] + const available = await checkAaveAssets(client, aaveRouter, { + ...args, + tokens, + }) + return available.every((a) => a) +} + +export type CheckAaveMarketsArgs = { markets: MarketParams[] } & Omit< + MulticallParameters, + 'contracts' | 'allowFailure' +> + +export async function checkAaveMarkets( + client: Client, + aaveRouter: Address, + args: CheckAaveMarketsArgs, +): Promise { + const tokens = [ + ...new Set( + args.markets.flatMap((m) => [ + m.base.address.toLowerCase() as Address, + m.quote.address.toLowerCase() as Address, + ]), + ), + ] + const available = await checkAaveAssets(client, aaveRouter, { + ...args, + tokens, + }) + const tokensMap = tokens.reduce((acc, token, i) => { + acc.set(token, available[i] || false) + return acc + }, new Map()) + + return args.markets.filter( + (m) => + tokensMap.get(m.base.address.toLowerCase() as Address) && + tokensMap.get(m.quote.address.toLowerCase() as Address), + ) +} diff --git a/src/builder/kandel/aave.ts b/src/builder/kandel/aave.ts new file mode 100644 index 0000000..ba7b675 --- /dev/null +++ b/src/builder/kandel/aave.ts @@ -0,0 +1,24 @@ +import { type Address, type ContractFunctionParameters, parseAbi } from 'viem' + +export type CheckAaveAssetParams = { + token: Address +} + +export const aaveRouterCheckAssetABI = parseAbi([ + 'function checkAsset(address token) public view returns (bool)', +]) + +export function checkAaveAssetParams(params: CheckAaveAssetParams) { + return { + abi: aaveRouterCheckAssetABI, + functionName: 'checkAsset', + args: [params.token], + } satisfies Omit< + ContractFunctionParameters< + typeof aaveRouterCheckAssetABI, + 'view', + 'checkAsset' + >, + 'address' + > +} diff --git a/src/bundle/index.ts b/src/bundle/index.ts index 6becc96..7296b89 100644 --- a/src/bundle/index.ts +++ b/src/bundle/index.ts @@ -5,6 +5,7 @@ export type { KandelActions, KandelSeederActions, UserRouterActions, + AaveKandelActions, } from './public/index.js' export { @@ -14,4 +15,5 @@ export { kandelActions, kandelSeederActions, userRouterActions, + aaveKandelActions, } from './public/index.js' diff --git a/src/bundle/public/index.ts b/src/bundle/public/index.ts index 8d46f07..71e2dfe 100644 --- a/src/bundle/public/index.ts +++ b/src/bundle/public/index.ts @@ -7,8 +7,10 @@ export { mangroveActions, type MangroveActions } from './mangrove-actions.js' export { kandelActions, kandelSeederActions, + aaveKandelActions, type KandelActions, type KandelSeederActions, + type AaveKandelActions, } from './kandel-actions.js' export { type UserRouterActions, diff --git a/src/bundle/public/kandel-actions.ts b/src/bundle/public/kandel-actions.ts index 38898b9..249a1c9 100644 --- a/src/bundle/public/kandel-actions.ts +++ b/src/bundle/public/kandel-actions.ts @@ -1,4 +1,14 @@ import { type Address, type Client, zeroAddress } from 'viem' +import { + type CheckAaveAssetArgs, + type CheckAaveAssetsArgs, + type CheckAaveMarketArgs, + type CheckAaveMarketsArgs, + checkAaveAsset, + checkAaveAssets, + checkAaveMarket, + checkAaveMarkets, +} from '../../actions/kandel/aave.js' import { type SetLogicsArgs, type SetLogicsResult, @@ -91,3 +101,19 @@ export function kandelActions( getKandelState(client, actionParams, market, kandel, args), }) } + +export type AaveKandelActions = { + checkAaveAsset: (args: CheckAaveAssetArgs) => Promise + checkAaveAssets: (args: CheckAaveAssetsArgs) => Promise + checkAaveMarket: (args: CheckAaveMarketArgs) => Promise + checkAaveMarkets: (args: CheckAaveMarketsArgs) => Promise +} + +export function aaveKandelActions(aaveRouter: Address) { + return (client: Client): AaveKandelActions => ({ + checkAaveAsset: (args) => checkAaveAsset(client, aaveRouter, args), + checkAaveAssets: (args) => checkAaveAssets(client, aaveRouter, args), + checkAaveMarket: (args) => checkAaveMarket(client, aaveRouter, args), + checkAaveMarkets: (args) => checkAaveMarkets(client, aaveRouter, args), + }) +} diff --git a/src/index.ts b/src/index.ts index eb432d3..9e24602 100644 --- a/src/index.ts +++ b/src/index.ts @@ -120,6 +120,7 @@ export type { KandelActions, KandelSeederActions, UserRouterActions, + AaveKandelActions, } from './bundle/index.js' export { @@ -129,6 +130,7 @@ export { kandelActions, kandelSeederActions, userRouterActions, + aaveKandelActions, } from './bundle/index.js' // --- addresses --- From b73457513806cb3131fcd6dcf050e4a488cb3ff7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2024 15:09:09 +0200 Subject: [PATCH 11/28] chore: version package (#125) Co-authored-by: github-actions[bot] --- .changeset/rich-trees-hope.md | 5 ----- src/CHANGELOG.md | 6 ++++++ src/package.json | 15 ++++++++++++--- 3 files changed, 18 insertions(+), 8 deletions(-) delete mode 100644 .changeset/rich-trees-hope.md diff --git a/.changeset/rich-trees-hope.md b/.changeset/rich-trees-hope.md deleted file mode 100644 index 0476c90..0000000 --- a/.changeset/rich-trees-hope.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@mangrovedao/mgv": patch ---- - -Added check for aave markets diff --git a/src/CHANGELOG.md b/src/CHANGELOG.md index e1e3798..b2579d6 100644 --- a/src/CHANGELOG.md +++ b/src/CHANGELOG.md @@ -1,5 +1,11 @@ # @mangrovedao/mgv +## 0.9.5 + +### Patch Changes + +- f838392: Added check for aave markets + ## 0.9.4 ### Patch Changes diff --git a/src/package.json b/src/package.json index 8a087aa..7f27665 100644 --- a/src/package.json +++ b/src/package.json @@ -1,7 +1,7 @@ { "name": "@mangrovedao/mgv", "description": "Utils and functions to interact with mangrove protocol", - "version": "0.9.4", + "version": "0.9.5", "repository": { "url": "https://github.com/mangrovedao/mgv" }, @@ -64,6 +64,15 @@ }, "license": "MIT", "homepage": "https://mangrove.exchange", - "authors": ["maxencerb.eth"], - "keywords": ["eth", "ethereum", "dapp", "mangrove", "order", "book"] + "authors": [ + "maxencerb.eth" + ], + "keywords": [ + "eth", + "ethereum", + "dapp", + "mangrove", + "order", + "book" + ] } From ee45564e41f09d8e1da135d87c5400f38324648f Mon Sep 17 00:00:00 2001 From: maxencerb Date: Wed, 14 Aug 2024 13:09:50 +0000 Subject: [PATCH 12/28] chore: format --- src/package.json | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/package.json b/src/package.json index 7f27665..05597ea 100644 --- a/src/package.json +++ b/src/package.json @@ -64,15 +64,6 @@ }, "license": "MIT", "homepage": "https://mangrove.exchange", - "authors": [ - "maxencerb.eth" - ], - "keywords": [ - "eth", - "ethereum", - "dapp", - "mangrove", - "order", - "book" - ] + "authors": ["maxencerb.eth"], + "keywords": ["eth", "ethereum", "dapp", "mangrove", "order", "book"] } From 48ee6b946b56072dbd322e54eb33835b400184ba Mon Sep 17 00:00:00 2001 From: Maxence Raballand Date: Tue, 27 Aug 2024 16:54:44 +0200 Subject: [PATCH 13/28] Feat/arbitrum/markets (#126) * feat(addresses): add new arbitrum markets * chore: changeset * chore: format --------- Co-authored-by: maxencerb --- .changeset/plenty-paws-cheat.md | 5 +++++ src/addresses/index.ts | 5 +++++ src/addresses/markets/arbitrum.ts | 29 ++++++++++++++++++++++++++++- src/addresses/markets/index.ts | 3 +++ src/addresses/tokens/arbitrum.ts | 15 +++++++++++++++ src/addresses/tokens/index.ts | 2 ++ 6 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 .changeset/plenty-paws-cheat.md diff --git a/.changeset/plenty-paws-cheat.md b/.changeset/plenty-paws-cheat.md new file mode 100644 index 0000000..67d9837 --- /dev/null +++ b/.changeset/plenty-paws-cheat.md @@ -0,0 +1,5 @@ +--- +"@mangrovedao/mgv": patch +--- + +Added WETH/WBTC WBTC/USDT and weETH/WETH on arbitrum diff --git a/src/addresses/index.ts b/src/addresses/index.ts index 201f094..7ff7b8c 100644 --- a/src/addresses/index.ts +++ b/src/addresses/index.ts @@ -18,6 +18,9 @@ export { arbitrumWETHUSDC, arbitrumWETHUSDT, arbitrumUSDCUSDT, + arbitrumWETHWBTC, + arbitrumWBTCUSDT, + arbitrumWETHweETH, } from './markets/index.js' // --- tokens --- @@ -37,8 +40,10 @@ export { baseSepoliaWBTC, baseSepoliaTokens, arbitrumWETH, + arbitrumWBTC, arbitrumUSDC, arbitrumUSDT, + arbitrumweETH, arbitrumTokens, } from './tokens/index.js' diff --git a/src/addresses/markets/arbitrum.ts b/src/addresses/markets/arbitrum.ts index b0f7980..5f42ad3 100644 --- a/src/addresses/markets/arbitrum.ts +++ b/src/addresses/markets/arbitrum.ts @@ -1,5 +1,11 @@ import type { MarketParams } from '../../types/index.js' -import { arbitrumUSDC, arbitrumUSDT, arbitrumWETH } from '../tokens/arbitrum.js' +import { + arbitrumUSDC, + arbitrumUSDT, + arbitrumWBTC, + arbitrumWETH, + arbitrumweETH, +} from '../tokens/arbitrum.js' export const arbitrumWETHUSDC = { base: arbitrumWETH, @@ -19,8 +25,29 @@ export const arbitrumUSDCUSDT = { tickSpacing: 1n, } as const satisfies MarketParams +export const arbitrumWETHWBTC = { + base: arbitrumWETH, + quote: arbitrumWBTC, + tickSpacing: 1n, +} as const satisfies MarketParams + +export const arbitrumWBTCUSDT = { + base: arbitrumWBTC, + quote: arbitrumUSDT, + tickSpacing: 1n, +} as const satisfies MarketParams + +export const arbitrumWETHweETH = { + base: arbitrumWETH, + quote: arbitrumweETH, + tickSpacing: 1n, +} as const satisfies MarketParams + export const arbitrumMarkets = [ arbitrumWETHUSDC, arbitrumWETHUSDT, arbitrumUSDCUSDT, + arbitrumWETHWBTC, + arbitrumWBTCUSDT, + arbitrumWETHweETH, ] as const satisfies MarketParams[] diff --git a/src/addresses/markets/index.ts b/src/addresses/markets/index.ts index d9632d7..40e062f 100644 --- a/src/addresses/markets/index.ts +++ b/src/addresses/markets/index.ts @@ -19,4 +19,7 @@ export { arbitrumWETHUSDC, arbitrumWETHUSDT, arbitrumUSDCUSDT, + arbitrumWETHWBTC, + arbitrumWBTCUSDT, + arbitrumWETHweETH, } from './arbitrum.js' diff --git a/src/addresses/tokens/arbitrum.ts b/src/addresses/tokens/arbitrum.ts index b4327a0..6167718 100644 --- a/src/addresses/tokens/arbitrum.ts +++ b/src/addresses/tokens/arbitrum.ts @@ -5,6 +5,14 @@ export const arbitrumWETH = buildToken({ symbol: 'WETH', }) +export const arbitrumWBTC = buildToken({ + address: '0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f', + symbol: 'WBTC', + decimals: 8, + displayDecimals: 5, + priceDisplayDecimals: 6, +}) + export const arbitrumUSDC = buildToken({ address: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', symbol: 'USDC', @@ -21,8 +29,15 @@ export const arbitrumUSDT = buildToken({ priceDisplayDecimals: 4, }) +export const arbitrumweETH = buildToken({ + address: '0x35751007a407ca6FEFfE80b3cB397736D2cf4dbe', + symbol: 'weETH', +}) + export const arbitrumTokens = [ arbitrumWETH, + arbitrumWBTC, arbitrumUSDC, arbitrumUSDT, + arbitrumweETH, ] as const diff --git a/src/addresses/tokens/index.ts b/src/addresses/tokens/index.ts index bfe7730..ae37985 100644 --- a/src/addresses/tokens/index.ts +++ b/src/addresses/tokens/index.ts @@ -18,8 +18,10 @@ export { export { arbitrumWETH, + arbitrumWBTC, arbitrumUSDC, arbitrumUSDT, + arbitrumweETH, arbitrumTokens, } from './arbitrum.js' From 1194e7bb84db87f51cc6c3a52aebdec347d3fcb2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 16:55:44 +0200 Subject: [PATCH 14/28] chore: version package (#127) Co-authored-by: github-actions[bot] --- .changeset/plenty-paws-cheat.md | 5 ----- src/CHANGELOG.md | 6 ++++++ src/package.json | 15 ++++++++++++--- 3 files changed, 18 insertions(+), 8 deletions(-) delete mode 100644 .changeset/plenty-paws-cheat.md diff --git a/.changeset/plenty-paws-cheat.md b/.changeset/plenty-paws-cheat.md deleted file mode 100644 index 67d9837..0000000 --- a/.changeset/plenty-paws-cheat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@mangrovedao/mgv": patch ---- - -Added WETH/WBTC WBTC/USDT and weETH/WETH on arbitrum diff --git a/src/CHANGELOG.md b/src/CHANGELOG.md index b2579d6..81466d2 100644 --- a/src/CHANGELOG.md +++ b/src/CHANGELOG.md @@ -1,5 +1,11 @@ # @mangrovedao/mgv +## 0.9.6 + +### Patch Changes + +- 48ee6b9: Added WETH/WBTC WBTC/USDT and weETH/WETH on arbitrum + ## 0.9.5 ### Patch Changes diff --git a/src/package.json b/src/package.json index 05597ea..5d278a1 100644 --- a/src/package.json +++ b/src/package.json @@ -1,7 +1,7 @@ { "name": "@mangrovedao/mgv", "description": "Utils and functions to interact with mangrove protocol", - "version": "0.9.5", + "version": "0.9.6", "repository": { "url": "https://github.com/mangrovedao/mgv" }, @@ -64,6 +64,15 @@ }, "license": "MIT", "homepage": "https://mangrove.exchange", - "authors": ["maxencerb.eth"], - "keywords": ["eth", "ethereum", "dapp", "mangrove", "order", "book"] + "authors": [ + "maxencerb.eth" + ], + "keywords": [ + "eth", + "ethereum", + "dapp", + "mangrove", + "order", + "book" + ] } From 0fca35a90f51c324e738288f77eb6540ce7d0f95 Mon Sep 17 00:00:00 2001 From: maxencerb Date: Tue, 27 Aug 2024 14:56:20 +0000 Subject: [PATCH 15/28] chore: format --- src/package.json | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/package.json b/src/package.json index 5d278a1..ddb6a79 100644 --- a/src/package.json +++ b/src/package.json @@ -64,15 +64,6 @@ }, "license": "MIT", "homepage": "https://mangrove.exchange", - "authors": [ - "maxencerb.eth" - ], - "keywords": [ - "eth", - "ethereum", - "dapp", - "mangrove", - "order", - "book" - ] + "authors": ["maxencerb.eth"], + "keywords": ["eth", "ethereum", "dapp", "mangrove", "order", "book"] } From fd0b7bff060a529d881db02fcacd8ad97c6fce86 Mon Sep 17 00:00:00 2001 From: Maxence Raballand Date: Thu, 29 Aug 2024 15:59:11 +0200 Subject: [PATCH 16/28] fix(addresses): reverse market order (#128) --- .changeset/nervous-cars-divide.md | 5 +++++ src/addresses/markets/arbitrum.ts | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .changeset/nervous-cars-divide.md diff --git a/.changeset/nervous-cars-divide.md b/.changeset/nervous-cars-divide.md new file mode 100644 index 0000000..fb08a6a --- /dev/null +++ b/.changeset/nervous-cars-divide.md @@ -0,0 +1,5 @@ +--- +"@mangrovedao/mgv": patch +--- + +Reverse base quote order for WETH/weETH diff --git a/src/addresses/markets/arbitrum.ts b/src/addresses/markets/arbitrum.ts index 5f42ad3..8bb9ab3 100644 --- a/src/addresses/markets/arbitrum.ts +++ b/src/addresses/markets/arbitrum.ts @@ -38,8 +38,8 @@ export const arbitrumWBTCUSDT = { } as const satisfies MarketParams export const arbitrumWETHweETH = { - base: arbitrumWETH, - quote: arbitrumweETH, + base: arbitrumweETH, + quote: arbitrumWETH, tickSpacing: 1n, } as const satisfies MarketParams From 76d221f713d0dd8b222b1cc0fb0b21d99aa7793a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 29 Aug 2024 15:59:56 +0200 Subject: [PATCH 17/28] chore: version package (#129) Co-authored-by: github-actions[bot] --- .changeset/nervous-cars-divide.md | 5 ----- src/CHANGELOG.md | 6 ++++++ src/package.json | 15 ++++++++++++--- 3 files changed, 18 insertions(+), 8 deletions(-) delete mode 100644 .changeset/nervous-cars-divide.md diff --git a/.changeset/nervous-cars-divide.md b/.changeset/nervous-cars-divide.md deleted file mode 100644 index fb08a6a..0000000 --- a/.changeset/nervous-cars-divide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@mangrovedao/mgv": patch ---- - -Reverse base quote order for WETH/weETH diff --git a/src/CHANGELOG.md b/src/CHANGELOG.md index 81466d2..d7ac925 100644 --- a/src/CHANGELOG.md +++ b/src/CHANGELOG.md @@ -1,5 +1,11 @@ # @mangrovedao/mgv +## 0.9.7 + +### Patch Changes + +- fd0b7bf: Reverse base quote order for WETH/weETH + ## 0.9.6 ### Patch Changes diff --git a/src/package.json b/src/package.json index ddb6a79..74da48d 100644 --- a/src/package.json +++ b/src/package.json @@ -1,7 +1,7 @@ { "name": "@mangrovedao/mgv", "description": "Utils and functions to interact with mangrove protocol", - "version": "0.9.6", + "version": "0.9.7", "repository": { "url": "https://github.com/mangrovedao/mgv" }, @@ -64,6 +64,15 @@ }, "license": "MIT", "homepage": "https://mangrove.exchange", - "authors": ["maxencerb.eth"], - "keywords": ["eth", "ethereum", "dapp", "mangrove", "order", "book"] + "authors": [ + "maxencerb.eth" + ], + "keywords": [ + "eth", + "ethereum", + "dapp", + "mangrove", + "order", + "book" + ] } From d1632989c86c56545bfc809edcd1de2c0fda1290 Mon Sep 17 00:00:00 2001 From: maxencerb Date: Thu, 29 Aug 2024 14:00:41 +0000 Subject: [PATCH 18/28] chore: format --- src/package.json | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/package.json b/src/package.json index 74da48d..07ffdb2 100644 --- a/src/package.json +++ b/src/package.json @@ -64,15 +64,6 @@ }, "license": "MIT", "homepage": "https://mangrove.exchange", - "authors": [ - "maxencerb.eth" - ], - "keywords": [ - "eth", - "ethereum", - "dapp", - "mangrove", - "order", - "book" - ] + "authors": ["maxencerb.eth"], + "keywords": ["eth", "ethereum", "dapp", "mangrove", "order", "book"] } From 61623e35504b53f96c1b24d92e5b2d74ba5baf54 Mon Sep 17 00:00:00 2001 From: Maxence Raballand Date: Fri, 6 Sep 2024 16:14:46 +0200 Subject: [PATCH 19/28] fix: market order simulation (#130) * fix: market order simulation * chore: format --------- Co-authored-by: maxencerb --- .changeset/gold-fireants-smoke.md | 5 + src/lib/market-order-simulation.test.ts | 191 ++++++++++++++++++++++++ src/lib/market-order-simulation.ts | 51 ++++--- 3 files changed, 228 insertions(+), 19 deletions(-) create mode 100644 .changeset/gold-fireants-smoke.md create mode 100644 src/lib/market-order-simulation.test.ts diff --git a/.changeset/gold-fireants-smoke.md b/.changeset/gold-fireants-smoke.md new file mode 100644 index 0000000..72ecb71 --- /dev/null +++ b/.changeset/gold-fireants-smoke.md @@ -0,0 +1,5 @@ +--- +"@mangrovedao/mgv": patch +--- + +Fixed market order simulation and added tests diff --git a/src/lib/market-order-simulation.test.ts b/src/lib/market-order-simulation.test.ts new file mode 100644 index 0000000..c0f2c89 --- /dev/null +++ b/src/lib/market-order-simulation.test.ts @@ -0,0 +1,191 @@ +import { parseEther, parseUnits } from 'viem' +import { beforeAll, describe, expect, inject, it } from 'vitest' +import { getBook } from '~mgv/actions/book.js' +import { simulatePopulate } from '~mgv/actions/kandel/populate.js' +import { simulateSow } from '~mgv/actions/kandel/sow.js' +import { validateKandelParams } from '~mgv/index.js' +import { getClient } from '~test/src/client.js' +import { mintAndApprove } from '~test/src/contracts/index.js' +import type { Book } from '../types/index.js' +import { BS } from './enums.js' +import { marketOrderSimulation } from './market-order-simulation.js' +import { inboundFromOutbound, outboundFromInbound } from './tick.js' + +const client = getClient() +const actionParams = inject('mangrove') +const kandelSeeder = inject('kandel') +const { wethUSDC } = inject('markets') + +const KANDEL_GASREQ = 128_000n + +describe('marketOrderSimulation', () => { + let book: Book + + beforeAll(async () => { + // Get the book + book = await getBook(client, actionParams, wethUSDC) + + const { params, minProvision } = validateKandelParams({ + minPrice: 2990, + midPrice: 3000, + maxPrice: 3010, + pricePoints: 5n, + market: wethUSDC, + baseAmount: parseEther('10'), + quoteAmount: parseUnits('30000', 6), + stepSize: 1n, + gasreq: KANDEL_GASREQ, + factor: 3, + asksLocalConfig: book.asksConfig, + bidsLocalConfig: book.bidsConfig, + marketConfig: book.marketConfig, + deposit: true, + }) + + const { request: sowReq, result: kandel } = await simulateSow( + client, + wethUSDC, + kandelSeeder.kandelSeeder, + { + account: client.account.address, + }, + ) + const sowTx = await client.writeContract(sowReq) + await client.waitForTransactionReceipt({ hash: sowTx }) + + await mintAndApprove( + client, + wethUSDC.base.address, + client.account.address, + params.baseAmount || 0n, + kandel, + ) + await mintAndApprove( + client, + wethUSDC.quote.address, + client.account.address, + params.quoteAmount || 0n, + kandel, + ) + + const { request: populateReq } = await simulatePopulate(client, kandel, { + ...params, + account: client.account.address, + value: minProvision, + }) + const populateTx = await client.writeContract(populateReq) + await client.waitForTransactionReceipt({ hash: populateTx }) + + book = await getBook(client, actionParams, wethUSDC) + }) + + it('should simulate a buy market order', () => { + const baseAmount = parseEther('4') + const quoteAmount = inboundFromOutbound( + book.asks[0]!.offer.tick, + baseAmount, + ) + const fee = (baseAmount * book.asksConfig.fee) / 10_000n + + const result = marketOrderSimulation({ + book, + bs: BS.buy, + base: baseAmount, // 5 tokens + }) + + expect(result.baseAmount).toBe(baseAmount - fee) + expect(result.quoteAmount).toBe(quoteAmount) + expect(result.gas).toBe(KANDEL_GASREQ + book.asksConfig.offer_gasbase) + expect(result.feePaid).toBe(fee) + expect(result.maxTickEncountered).toBe(book.asks[0]?.offer.tick) + expect(result.minSlippage).toBe(0) + expect(result.fillWants).toBe(true) + expect(result.rawPrice).approximately(3000 / 1e12, 10e-12) + expect(result.fillVolume).toBe(baseAmount) + }) + + it('should simulate a sell market order', () => { + const baseAmount = parseEther('4') + const quoteAmount = outboundFromInbound( + book.bids[0]!.offer.tick, + baseAmount, + ) + const fee = (quoteAmount * book.bidsConfig.fee) / 10_000n + + const result = marketOrderSimulation({ + book, + bs: BS.sell, + base: baseAmount, + }) + + expect(result.baseAmount).toBe(baseAmount) + expect(result.quoteAmount).toBe(quoteAmount - fee) + expect(result.gas).toBe(KANDEL_GASREQ + book.bidsConfig.offer_gasbase) + expect(result.feePaid).toBe(fee) + expect(result.maxTickEncountered).toBe(book.bids[0]?.offer.tick) + expect(result.minSlippage).toBe(0) + expect(result.fillWants).toBe(false) + expect(result.rawPrice).approximately(3000 / 1e12, 10e-12) + expect(result.fillVolume).toBe(baseAmount) + }) + + it('should simulate a buy market order with quote amount', () => { + const quoteAmount = parseUnits('12000', 6) // 12000 USDC + const baseAmount = outboundFromInbound( + book.asks[0]!.offer.tick, + quoteAmount, + ) + const fee = (baseAmount * book.asksConfig.fee) / 10_000n + + const result = marketOrderSimulation({ + book, + bs: BS.buy, + quote: quoteAmount, // 12000 USDC + }) + + expect(result.baseAmount).toBe(baseAmount - fee) + expect(result.quoteAmount).toBe(quoteAmount) + expect(result.gas).toBe(KANDEL_GASREQ + book.asksConfig.offer_gasbase) + expect(result.feePaid).toBe(fee) + expect(result.maxTickEncountered).toBe(book.asks[0]!.offer.tick) + expect(result.minSlippage).toBe(0) + expect(result.fillWants).toBe(false) + expect(result.rawPrice).approximately(3000 / 1e12, 10e-12) + expect(result.fillVolume).toBe(quoteAmount) + }) + + it('should simulate a sell order with quote amount', () => { + const quoteAmount = parseUnits('12000', 6) // 12000 USDC + const baseAmount = inboundFromOutbound( + book.bids[0]!.offer.tick, + quoteAmount, + ) + const fee = (quoteAmount * book.bidsConfig.fee) / 10_000n + + const result = marketOrderSimulation({ + book, + bs: BS.sell, + quote: quoteAmount, // 12000 USDC + }) + + expect(result.baseAmount).toBe(baseAmount) + expect(result.quoteAmount).toBe(quoteAmount - fee) + expect(result.gas).toBe(KANDEL_GASREQ + book.bidsConfig.offer_gasbase) + expect(result.feePaid).toBe(fee) + expect(result.maxTickEncountered).toBe(book.bids[0]!.offer.tick) + expect(result.minSlippage).toBe(0) + expect(result.fillWants).toBe(true) + expect(result.rawPrice).approximately(3000 / 1e12, 10e-12) + expect(result.fillVolume).toBe(quoteAmount) + }) + + it('should throw an error if neither base nor quote is specified', () => { + expect(() => + // @ts-expect-error + marketOrderSimulation({ + book, + bs: BS.buy, + }), + ).toThrow('either base or quote must be specified') + }) +}) diff --git a/src/lib/market-order-simulation.ts b/src/lib/market-order-simulation.ts index f4a50c4..24d0161 100644 --- a/src/lib/market-order-simulation.ts +++ b/src/lib/market-order-simulation.ts @@ -55,20 +55,15 @@ export type RawMarketOrderSimulationResult = { export function rawMarketOrderSimulation( params: RawMarketOrderSimulationParams, ): RawMarketOrderSimulationResult { - const { + let { orderBook, - fillVolume: _fillVolume, + fillVolume, localConfig, globalConfig, fillWants = true, maxTick = MAX_TICK, } = params - // if fillWants is true, then we need to multiply the fillVolume by 10_000n / (10_000n - fee) in order to account for the fee - let fillVolume = fillWants - ? (_fillVolume * 10_000n) / (10_000n - localConfig.fee) - : _fillVolume - const result: RawMarketOrderSimulationResult = { totalGot: 0n, totalGave: 0n, @@ -84,18 +79,36 @@ export function rawMarketOrderSimulation( i < globalConfig.maxRecursionDepth; i++ ) { - const offer = orderBook[i]! - if (offer.offer.tick > maxTick) break - const maxGot = fillWants - ? fillVolume - : outboundFromInbound(offer.offer.tick, fillVolume) - const got = maxGot < offer.offer.gives ? maxGot : offer.offer.gives - const gave = inboundFromOutbound(offer.offer.tick, got) - result.totalGot += got - result.totalGave += gave - result.gas += localConfig.offer_gasbase + offer.detail.gasreq - result.maxTickEncountered = offer.offer.tick - fillVolume -= fillWants ? got : gave + if (orderBook[i]!.offer.tick > maxTick) break + + const offerGives = orderBook[i]!.offer.gives + const offerWants = inboundFromOutbound(orderBook[i]!.offer.tick, offerGives) + + result.gas += localConfig.offer_gasbase + orderBook[i]!.detail.gasreq + result.maxTickEncountered = orderBook[i]!.offer.tick + + let takerWants = 0n + let takerGives = 0n + + if ( + (fillWants && offerGives <= fillVolume) || + (!fillWants && offerWants <= fillVolume) + ) { + // We can take the entire offer + takerWants = offerGives + takerGives = offerWants + } else if (fillWants) { + takerWants = fillVolume + takerGives = inboundFromOutbound(orderBook[i]!.offer.tick, fillVolume) + } else { + takerWants = outboundFromInbound(orderBook[i]!.offer.tick, fillVolume) + takerGives = fillVolume + } + + result.totalGot += takerWants + result.totalGave += takerGives + + fillVolume -= fillWants ? takerWants : takerGives } result.feePaid = (result.totalGot * localConfig.fee) / 10_000n From 5434c85e1dd0b6156fbac70caf650fde9f1713ff Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 6 Sep 2024 16:15:26 +0200 Subject: [PATCH 20/28] chore: version package (#131) Co-authored-by: github-actions[bot] --- .changeset/gold-fireants-smoke.md | 5 ----- src/CHANGELOG.md | 6 ++++++ src/package.json | 15 ++++++++++++--- 3 files changed, 18 insertions(+), 8 deletions(-) delete mode 100644 .changeset/gold-fireants-smoke.md diff --git a/.changeset/gold-fireants-smoke.md b/.changeset/gold-fireants-smoke.md deleted file mode 100644 index 72ecb71..0000000 --- a/.changeset/gold-fireants-smoke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@mangrovedao/mgv": patch ---- - -Fixed market order simulation and added tests diff --git a/src/CHANGELOG.md b/src/CHANGELOG.md index d7ac925..9e5e6c4 100644 --- a/src/CHANGELOG.md +++ b/src/CHANGELOG.md @@ -1,5 +1,11 @@ # @mangrovedao/mgv +## 0.9.8 + +### Patch Changes + +- 61623e3: Fixed market order simulation and added tests + ## 0.9.7 ### Patch Changes diff --git a/src/package.json b/src/package.json index 07ffdb2..5205108 100644 --- a/src/package.json +++ b/src/package.json @@ -1,7 +1,7 @@ { "name": "@mangrovedao/mgv", "description": "Utils and functions to interact with mangrove protocol", - "version": "0.9.7", + "version": "0.9.8", "repository": { "url": "https://github.com/mangrovedao/mgv" }, @@ -64,6 +64,15 @@ }, "license": "MIT", "homepage": "https://mangrove.exchange", - "authors": ["maxencerb.eth"], - "keywords": ["eth", "ethereum", "dapp", "mangrove", "order", "book"] + "authors": [ + "maxencerb.eth" + ], + "keywords": [ + "eth", + "ethereum", + "dapp", + "mangrove", + "order", + "book" + ] } From 5d71c948e1852f04bb78813786ef40d3983e1f95 Mon Sep 17 00:00:00 2001 From: maxencerb Date: Fri, 6 Sep 2024 14:16:01 +0000 Subject: [PATCH 21/28] chore: format --- src/package.json | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/package.json b/src/package.json index 5205108..42413c5 100644 --- a/src/package.json +++ b/src/package.json @@ -64,15 +64,6 @@ }, "license": "MIT", "homepage": "https://mangrove.exchange", - "authors": [ - "maxencerb.eth" - ], - "keywords": [ - "eth", - "ethereum", - "dapp", - "mangrove", - "order", - "book" - ] + "authors": ["maxencerb.eth"], + "keywords": ["eth", "ethereum", "dapp", "mangrove", "order", "book"] } From 692f50fff33e63dc18db683ea006c2cf293878a3 Mon Sep 17 00:00:00 2001 From: Maxence Raballand Date: Fri, 6 Sep 2024 17:17:06 +0200 Subject: [PATCH 22/28] feat: added serializable functions helper (#132) * feat: added serializable functions helper * chore: format --------- Co-authored-by: maxencerb --- .changeset/polite-mayflies-cheat.md | 5 ++++ biome.json | 4 +-- bun.lockb | Bin 186296 -> 116720 bytes package.json | 20 +++++++-------- src/actions/book.test.ts | 3 ++- src/actions/kandel/populate.test.ts | 7 +++--- src/actions/kandel/retract.test.ts | 3 ++- src/actions/kandel/sow.test.ts | 3 ++- src/actions/kandel/view.test.ts | 3 ++- src/actions/order/new.test.ts | 3 ++- src/actions/order/remove.test.ts | 3 ++- src/actions/order/update.test.ts | 3 ++- src/actions/order/view.test.ts | 3 ++- src/index.ts | 2 ++ src/lib/human-readable.test.ts | 3 ++- src/lib/index.ts | 2 +- src/lib/kandel/distribution.test.ts | 3 ++- src/lib/kandel/logs.test.ts | 3 ++- src/lib/kandel/params.test.ts | 3 ++- src/lib/market-order-simulation.test.ts | 3 ++- src/lib/utils.ts | 31 ++++++++++++++++++++++++ src/types/actions/index.ts | 12 +++++++++ src/types/index.ts | 2 ++ src/types/lib.ts | 12 +++++++++ test/globalSetup.ts | 14 ++++++----- test/src/markets.ts | 10 ++++++++ 26 files changed, 125 insertions(+), 35 deletions(-) create mode 100644 .changeset/polite-mayflies-cheat.md create mode 100644 test/src/markets.ts diff --git a/.changeset/polite-mayflies-cheat.md b/.changeset/polite-mayflies-cheat.md new file mode 100644 index 0000000..e7abca7 --- /dev/null +++ b/.changeset/polite-mayflies-cheat.md @@ -0,0 +1,5 @@ +--- +"@mangrovedao/mgv": patch +--- + +Added serialize helper functions for markets and offer list keys diff --git a/biome.json b/biome.json index 3f38d0a..53f8700 100644 --- a/biome.json +++ b/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/1.7.2/schema.json", + "$schema": "https://biomejs.dev/schemas/1.8.3/schema.json", "files": { "ignore": [ "_cjs", @@ -61,7 +61,7 @@ "javascript": { "formatter": { "quoteStyle": "single", - "trailingComma": "all", + "trailingCommas": "all", "semicolons": "asNeeded" } }, diff --git a/bun.lockb b/bun.lockb index 7972031edbe8287537e1d18a8a20e2ac8dd97ed3..37431af1aa1b16d5dcf89a69c672367346ee028a 100755 GIT binary patch delta 32005 zcmeIbbzBwO*FS#FkViO(3KD`KC8(s7K^Q2CC|D@hI+TEvlwcj~&QZ7Bf!$(Y2V(bC z6x(Ze_xHVK&RqE1k3Qe$_j-QM_pi^~`)2L6_S&`f+A(v+**iBHyqcmn)y-v9>&83E z{FaaOuGeAH%O2L%H`;hU%UiiK=Ek3Vv+-{~+4m`t%jj~S7Gw=f#5#(Gt@&)0WAU(9Z@>>GM@7DM?w` zvJ;RKpB0~)8=onYwM9OLY6yZ6r~>K?Y7Uy=nv#^7lr76}&Cbk6#_2iPaq(%{vee8Z za57m!mTP=ocBZPY9x{M_a#ng;LQ+b65Aak`3*>}i(w+?ay3ldaW+5OR`%=nzFq=bA~w`ww34ahC3i!F)=MV9J<`I-cANtr@Up3tHS zY(vGU0Lg^Iz#vUQLQ-0sYfgqN-$aa0(@=CpdaNot-c`eAX>;ROguR}q}a%(mGPqc>e8H&tnf%XMO8wwsluBJc%N~46c zk$`IMf?TN!vXc4=&NLQsvP36P^2Q8Rb|MXGd+ z^aDVtg4s!FA^BMu`7*e767*yxj-n}xK}jJlK0d=WzJCf8n5#;Wc_SfJEG|AKr!VwV zvnZ8J#uq5e)n8m>GHXQM1chY^4r=&9P-^~oP^2$N07ZogT7lYvI&0)QplCqBJ!jFZ zM?hhrf)yG%29(k#YWQ}bRE{So%&IQPNYBcS&B~J1^AMXaO%`dYwDhDnnJhjlCMPK+ zPWIYMbex-@WbPB7aN2^M8u@Y!KTQ*_*2w#5CstAt>pM1Ev1Y(Zu)G#D{74Mxc~FRuwClyS}Cy(0}FFt1*>SG% z>a<)LX4hC1RCYGjR6sK;rKiPbC#A;AQZZS^x+bKpL%ifjuKnWk7i)M1sz5>GLCK*C zKxqj5a~}Wa{7wF$ri^lv;!~-LF9XFE-UX$ME^Fv<4J`(x3N#NE9Zibgq>-y)lCtyR zyGdE;1IWjOKb2?%!Zac>lTtHP2{Ku7bJ(9uD$Iq5v}++Mn1PZfrzWK(WvH^Uq?N`9 za%yQpOVQ)^fYMOX(a1Z8i0NB{QUiQjiTby}Q^j9_r=j}*l=7e3N-YMiwH6aNhl-|? z68>|+kTQ}o=+j25z+6p+)R35bnQUlVF~6)7RaPPzCQZ+=kke!m02%p^gSwr#Bvu6_ zXZjWOKYoS5ozsxigeYG{yHd`*r|iq;Eoj(T ztjJIB6#qs;`^IOxW~66kYn)y!`={4pUJ$%jx2xE9(#kXmJhi0lKjfCsCm(3oO`OQg zL8)Rtqr?g9At-G+&T8mRP&4pLL2JsiWd#$X__kHuc=H;G5n1Cxe{5UfF!*Yw)$Bz( z+S?HPQqSss7R%wK)&NBy@i zbK3HYLd^N4 ziu>L9ae9NZw{o_XljX)C{d`{O29ArLuH#%)-91R_GOXR@?U7*prS;c;3RHJEq;7vF{nnmdfxWomOE2|rRm*#_%5^pQKcfBRw2`)*y5>xpc<1bn-s(|D0=oVctkv7%?WL;g z0^E~By9^s^bmjO*_RPU!+xC0i+u1ff9AbGW@AjUP?Y`$s9wGC-V|n7(@@+>hZ~t>j zsIzvGacc#iX1-Kk>LlLS;<)XLV%;A(PpSrfnp88-q;x1<#15`VQ;g!%Ur`KAoJmSyYGJ-4O2h+X(BF1ky3(*rH1 zo0{{sR{ne+D-U&S#?T1W*!iXNstnjwnsD^&jzL2n+Z;DM7jif@_H^C1nf#5!-O6s) zPw?M!o_L>oI>5BegRaWy4)Pjio+z?P`NAaG=LZC-`y?yACDb_wKOR^g+&plnR9sLw) za6V8m5c>5vI5M4>=NE9^;N*NA2R|J*%=q4XxkD2!#)!9!3epEp`JmpgTut0qh>mmz zHH`pADYd9S6_de{CM+K8uMdVAAvWF-3nwMh6B^MQ9GOCgEU2>poG)+f*hEo|5M=-d zv+?6Bs_~ZH0=a(G_~>qdIt#0jR!{pTic1J#(?MF^emaiT(HlaCXCp)%h|a3*r?VSe z5Z|*-6UAqQsB6%%_58K5HGpS2HBn?EB#r{I@)mHUq%YX#3Ah$QRut!ty|8HBB`y8s zCVaG0pyCi@ViQzOembNQCY zMMVUK1o;wYKW;%yKDtMs;xdF(3&GzMO01=%qAM6J3|zFBk&T~n1voO543{qIT%X3B z;tiyt7gD|Jng2aNqW$IOd|A&x#azh9c~Ko3KSdch(YN4DI@Y);X)KKBWQ5v7>+cb* zI0OlmC)Vr-I5C$c@a6WF{M_DwidmLoW*krMehyq`B!Jt%H5{;Qpahs?;EZwLdVmu~ zkm3NiUf|G7dq17pRw$b9*}JJe0^J~n%fRona3d884kNawpCSxgb0G=ZsWS;&AHKY2 z6P=F;c~htBSmH*htq_Vs2wj00VJSl7AaEQzKhE5ikDe6B&9UVzvjcUW*^>T(I!!p= zI(+oxK%JSm1;zB{*+g+2Au@)LOyOlGwo)dzd{vq{;DjM)+%k5wo&& z6vhRz>PjI|9i2ZBoCi3}sz`hVoY-{g8!IQ#u~2KoMT7GfG-(h|2UpRe%izR{R;?>m z1|1ORr)UPQflwKmu=2opie)wPQ)~xE`Js2~`*Gju@?{BuI%f4`vOoBqNlmp8=+2iX zG|@RJh$!S#Unc7$$np{DDTJ;dlq7_LP>@OpEk;O;{f^2!#rv%YVmOAf+f%AtdUpLP$*e^zYb)n4;SWY1IgcDM}D( zFUU;MFJi0;Ax$m_iFz*)67y^5C&>mOB&H}qNKBzb-?SC-i$X|Dy9gms@9E#NhK)4& zaRVChWtoB8N{|78irbJw2|h}$WRB?r1;c+;o&6N;!I8&e7De1ia5Qj)RYu1GjxA1o zQ3z2QabBAQj`GvymkjjR2Sf7&Rt79ZI_Ls_VX_WDh~&aJQw#xzyv2+aMc`;)(Eh<+ z9}NE2)W9b|babppj+iIFdGX~~LiQun>hJ7yFv(H|;%e9$9MuOL=2&w`ZeRrh6XA&K>+7RqgCDfxbkMa<*+~^ zx3+DfUw}|&L{~WTB}k}M2ExkX)dGG1PH3@WAUHHyW9psY$QpXWzUdV>sv~w0=->J+ zMXn03vh~*oBbt<=O2OfOYB1&$Wr)~c;{GHqgr7SiP_a@Y)E89xAT4z%${Fmh4JMc` zuiZqU*IFj)3bEKVJ;71c&`u{mZeD9X+AdJ>7lddI>WhLouTZ`W!ZfspvJ;o{LU801 za0ASY_cfd@f2Y5{KDJ#juDBIZq=BOX6ub(1;dS7s5i-6`oS;Ziy8IGPKZRRcaXV#1 zmDisDj;g>SqvW;tKDAAS#rQO&G#JFK^iOb9BXO78C`=}c04J;|+_*44x=x^?X*+Sn zg9^3;GurWUCkE+5CXOL8Yx8iiMz9iw$7pcE@X*;Og%lqU!gNA*Y>gcNl2W1KNQ(nU zUW6$;$X_3f*f{K#6gR<%F0iDxpNKgC#Z!XBWCdx*t)j z(n#Qd=-0>K$fwbj7`Ju0h}|UERM80>rG<^Kvzgh2HuB2z5Mmbo*8y9tDjZW7urdr| zz><%wU6*>E>(rHxo{p_WSKe}ZprQiS3`;#2*qop?g~Q)VYSM_+WN{4NR58^hjigDrOzRSxBNr5akNYZ=d2_>*F$0W zZ$dOvc>_`!Ryt_B4raajyz$J5>e}G2b_*9#4saD&fodV_0g%byKf*N#pRi;$YV1VMA;i;=| zH4_O0|IX_|gh@-L4ZzgGRhd$T=osN5N*QB(3KvmI4;L1$f1}tK$*4onxq=D*5Y({w z0;P<4XlPHO{G?enLWTQ)qhe!73d90*5hZy%K=K5DuF90;eFf>iQnF(*K+Q}8D4z^~ zE~1pKKYpeV0+}SmMU*14h{08vQbq#=>8~jjkPqkq1pwtU44{iB#g70;J`x}^jiU}D z!FYh^1b{B0#7`s!7g4IfRDe2Q7C;3p07$+BAo(hQ(iH+qU^_t9uc!s&XUP7P;4DBF zQL5>A4ZR3TS7l13y&_0OO8Qqdd}T^{*8$4^h9;gU>E8sX;_AEjLkjl*x`+~gpBP+3 zN%0Xt^2Y#OM9G9Nh{5$=QPO)0Q2KWOT|^D2|9=XsOrX^fu0~q_&nWEws{+Y%CYp-; zikcx_`uYEVM5+J(M;ZJN)4L%3FDsyc+*@M-f%4yHo2g0QtC9Y9DA}CM{74&d|`A8aj)FxGGa> z+-!}UC{>Q$M}_B{W+y${*( zKYO14?0Mh@|F8Buv}2Y1@9lZ`=&f17!}!cGcgx!zYI2T724>IquH; z+ly0-kG_A^r*r8kzjWJK>k`!URR#(5tfua^jlJWr%I)roBP-V2vm3(}87=Dc349h9A;ZH>3Eg;EsYb;H%w=;>Q*# z`J7u}awUHdoc~TG?|3^byeir+UE1MQb7$9>b)o0Uypk=hy;h!ezaPEdaoVjrtMd5~ z#pd;kOGn@CxI;BjJ5`=@`&!t??j;@Frmgs*8=iBzwD0w~K3aVFPV;s()QuXs1Qhz2 zjXE6A;L^goC90BRduMAs?K{EHHGTa&i~NK;>jPR_*L*p#>Gx%6`m^Kv^J~y%Sh9ZFC%@)fN^(qBE1#KpkLr|Aqt2SkyNg=s-B;|rF^7BX z{dVo*N=2CRkH19mpLQ$x@ULNVGk(t3D86uylAn1mOm4xy2G^}v$#?k{h9}fZzD4o+ zdzHM-_b|CNANf6s-wAFzI2&I6BZ^Plr{v4Ghso>kE<2)l^Zn?VA7OHPely~ZgERdZ zCU@jjKhbLk&@V$s6*I@54|>(WVDsavy%q1GEs_cW{1u zo4?S)V@h_JQZ{0BIHc@WqGY2u@J-lx;`NU!nU5Cu05(hu{7&!>h!0|(+TasQm29Rq z_-5=5@#ZI#EJO!<3pPy${BiK_i4S4TbiwDHRI(Mi;9K+6_MyS2;EMag)s}HG_Y50AGWux@3U_w zdXKG_w`|2|r?N&FrCO`qbnELDo`3mmu&&c4x7QuY+x)aJ?pWJu#Phu_-R--Zxbx*_ z%-MEiE05%-!2@QWRWhXlrF8nc4D7mXF5Gk7JaG2GliQ|SeZO=5-IaY_bxX^8q!#?V z`Z@FA?P`Ozc^~nMJRNiXOK^#O&>x#uUAIr{>h#B{SXyV09l77glsah70_KRzcmP*e>D=&nwl93?gr>j9+;Fb-gt;@6`WN zGE0Btle;PbldbYuR<{f7{LlPxCMH8G zl|nZJ(xtuPs$TzsF#V4P15VEkIXU@4yXMN`pyMM~G`{9^NY%7anU8y6$8mEHS~NZq z|8~smTAR)*R{k8De`Z?p$YZPg4_o-U^5GZE*=YlL825*aSsKc_^7+bq<_)bmsUDpqL^G7AK zsQ#sLg~oBG#%A7iIehJj$@os8H|GD;clx$x`>AF5#lc%%PT2dvP~ClFAB*do%L`s8 zr$tzRdlu6c?jcM=0x0XMA#49hGtGDRswF(^;dv)jO1o@#ZnVhHLhXW0_PBf3= zI_KsdcFSp1ZYWE3+V0i9!>cp**DpU4cWa||%dd+LZP9CGrq~?mTB&xim20dee$kV|tNRQLQO+q{ zW?|jhRJoj2@t*#FmhHZ4o$r#bKF<$}bQv+VQ{(9PYJoM4eHX^Y$;aOE zXq#5NzlM7IzN~xg+aH?Sd$H5TMLK0eon~vhzxL-(|7orsQyO?>)EKq){nAFY-al%1 z;G&I#-1F|oxdASx@>X7M+cBgZ00~MO!R2 z-@o(F<`GA_d*(m5G$DAx8x!l;1`~Y-P2-1LHD}i0utKVgRcQw+_+L}9o-Poi%UBEv z9zl>p8vSMLP&fp$%aq}x+WwW)r)OtB&%NVUov;o#^Z3N+8nY(t=rs05adtKPX>K=1 z2A>Vxj-vkkXz{$tOo&fb4L&`$fa z!I7M)p&h?9>^s;g*UqfPApZGvbEZS>86acRols8U4W&B#UGMk}!=`tLJ2|22#ei+y zRvfKi=pJp-?dutCK^@n%Q;HH^J*eyCS2m)P)5Rw8^q5iU+1u6cTkN+=UlP06AaH!8 z>eJ5^(xpA%;EA-QR;3$9x8CsAv{_HjnCzNbn7XPY!nJf-+-83B!I>k9$Ic1zwa6HM zXUC8UZy%&h>g)ArWkz0=KEAQf+G*{Neh&b!=?$?wQJ~&q~_6zU}hqRqnhU z(R&WXy?M21!N`--4QxMJElDlPx%D>KqRqXJstKE}_&FY0UMXAYfe~3kJL^;V4&yq- zENRFea|fBMU9irxZ{X^aicSG9{au4|=lyWKxhr=_;{Xfq$YHg{*BrL^hpE4ovUi=j zpYNsy9v?iUbTHrZmbrTEoWG3AI@Q*n(xtqIgU5~cM-%(p?0@&w#)APzyk~B z?XG3O?|DR^kBC(7J4u5AC0Q&?fFKhB%D{q^t?fr{G^lXMMUg*&=TM z@2}1@9v*jKe!;`kQlC8*4LXd;^2@rSU8!~>D%Z|m@yF-D%eS8lX`&Z5InhmJxvKxQ z{`Y4WscWrmpycRT$$b-pQ?|a? zHGc1*?cT5BymY)iTWr1mpi=EdRjyre`%)LHkzpO$j(=RXYv{uPaRJ}Gnv-hWk)ez;4Wos9;L8GPZ#_1kC1ul4ntw(!f9 z{OqQ2y)Mlgwe5VR+Ks7PyXt#aWxf8|YO7gXwdU1bcDHkx$cN8)^XAaCns+ZPkq@tu zc>djP)^fYW!raw|V(e}??*9}!E_p%Q4Mra4@|s>KSTl^bzH1)-sC(9~Z71VWORsG_ z-PzVLFEsYdhQwp?H;Ik`rK4VE>~UYxb&S)-XAez}KYjma(Auo$HvMH!TQ9KRzkN_h zd0Z#)QM`E5FiysjYs$NF<1up*oq%138R$ezc|<2+(?N8yjM6yO5C^1l`^$ z!y{{U8*f^xZPvro?vm)^&>az6MqIDa&Ur z-#hflo*`P=L$xF8Z)z6r-^{p@-|ay?+>x#RJKSA(qMW@ju@?k8Az0xB!G0NgLxRNj zxQA#6!9m!zAq3_hU|Vkp4#T$I5FCeKI|+`$xIPf%eN?h|9|%fdT@pBdQZiFt2ufjI zUkI*3aFhfmVP8K8#(q|Y4{Q3$W!IL0)3hS0v#tHt&GfUqzv7nRSmVK&la7oUl6mIO zrBCzk_f{6?h38cpHuRZ~eQndT$A)`E&OSIa`f0)Fnw6S&8s=?`Z2i9|)wx@BePR|Q zH|Xv=`eq~3&@~(G-)~aQ;C$a&sXq1N?DL)&yY=g+T)wUgiyN`}^Q2${hgGs#FSlyH ziJJRk$;mcXDrYPGKu&{JJE~)w%W>yBE-6`ef62p}cOuOUS9hFgzW-*weD@35tEyes z?%UxKdsk*DK5&d5Cq>V;MMkPo2|YeVkF zDaI7{ZnUjY;!Y?3U6Z0hdTPB?{Dr|eDFs1Lg(vab-E88o-sV2;OR^! ziw+}i%T15X%&Xg~#WYusi|d{biEI1(<$bdPKH!_VdUF?-tdQiJmk&+#oZUq=Hr~7U z!mq_{BM&D%{kpff&V_N)_ja1*U-tcJ3*+t=);M@f%Nyp@KHx03?_-nP!jixUogQ^zV#%*Ft zmYH7PaVWD_oMZRU(ulf6cVD|L*~&lvX3mEA!@vK;JmC*VEc~tvf1IteH~W6-d!x9! z<83#$y7cPA)pO^zj^65`$bB4O9yWFJ>X_R-3$F((T5?yn*VOsb3cEC2`T4qh$FrZ= zw_ASOXHluT*D!4~LAKp~DAn(;x%V?}z<&$xX_w_CPcA&Lp{7CZr?p0=kNw&my0z?Z z^MLoOb(1$8zxFxyUev?oCaPEKw>pQEuUncn%(-Js+_y^E(u*3>rJb$)Qt?;#&r`;m zQZ@|=NH{g9(~R#sYz~hc{N=L8)_(Usj_&tp*|uFjEgqfeef~<*h-Li@V)sTkHf~c- z_vZ4p3vXWNz<>W?9{xU*eX|%?H)XC)aofegk@E_ct}=J#ON~{Br&KqYAEY;LPmFi% zkw&}5gf>}TG;+_ZC9_?O=Hq_wqM5l1@9_0WrP`HaPHu)q@BFC@KQV3G7S;Nk9HYE5 zX&C=P^?>^^etb2cJId5yc6Xi;E6}=7a^9tt`{)~^l{Ve^*rBP2prYL$2)f;a1=D{%U z>Xzos`aR749=Gf3uGgh=8eGt-m#s?Jek;d4ZGD%`9}n2k&&*P`cO*lCY7e{`)xP|K zdEC&Cv)yu&$6dasYWd=(d9$&@3Ig_Jm2KM)H1Ng;kB}A?3n#boaGsZaqWf09@)GCguQnS9V-3SB7W)^=*A&+?bX@>B^zCYJ5$)zwr9i z*0qPsyqmgrVa9{vy79w06h)-OB`m!5Fz;k!S=3FxPEFdB#s@#RRqJ~9)2#9n!Uve& z1CehYC&vUIghwI|`Dr_&n6nn1bB@BZxX1jk9Z~Emu@82H$)EC%5j$2JZ1}D)`E!2G zt|;cOgTy_Nn0tv8Hwf81B8f5>lGj*sgCUu%3&~!Ryv3@E9%Z3=kfb)lz0>>3R@>S? z`Civ9WAsHXXMnkexa}QjsQu1cty+cz&(q`2QoAD~9 zR+{zLo^Pr(Z#ZYcfEtU~b3GU>r#VXch;_R;N-ESxNscWb`HYpj1ti@R==arkdX(BO zzTff8waq1M*1O858qL?KwQBg6Z$@p59)173C;!Hbmh5f8$NtNfhua#SHLb>2mo1OII~Zroz^%oXkv&NTMTKhkHV*^yOE zJN%`LXBNZXwLJQ#OBMYgGqy$c>wT-u_}mjo^Xf*2eq!GhXo3Zrh^5JFCFr zU9N}8fAUMNN3lc$v}k)%ab0A^K|(1d3XI`S|7CRm1)XmD`pRIEnX6NFD`%C(GP~V zn#Ri(*qh#5b~Pv7!*J|9{hiYd9}n8ctc_6DaQLWP!R|CcL064XP)Jh<4A|qQ5R9z~ zL6<-Xlgu^3J-;#Dw`7u!6OoUhk&al=fAv$w^XAfZJ<$uEuq?nR)@f`Jp`t# ze|rc%k>D~3%vhZc5ERycU{nVPEZBJxbTfg#Cjx?6Y*+*Y`lb*(fFPW{L?K<;O(&X~ za#QxXG#KynD82rT=!@VB>tOu$Ghe>$e!$B0^}7AMdTPoO^P<^n z&z>!-y1e$dKi)ieH0`J-uu78c&|)^*cDF(j;QT9(P1_6KkTn3p%3y zb=aLq2+YkO2!TLtU%CE29+=)ZI&{CS)v1>o>uoJ>l&YWfbWq34cAZ9__Hwyh5Yvgv z=rFOKLA5bnF<10_bCfZEd^*bOJuwXpwsk(U(fT1?fso_<2xAz;p>n<-oi7c(6aKQw z)y;#{9X@Gy|9bhz>b_$J4_le7o`3k3zS$F_Jf~U@Km7c(;i~V_X^na=z0|1PA6-e|BHT&~}lpJ8txGSm%}#y87CQLFQlZubI3$F8?@c?v++8bAL?KzjLg% z;g%)61{qYXd1_zZ8$yNg&&QE(-OBmaz4PLIzo$A+Z=Gzvuv4e#3+m_|FYXW7;q=#s zgfS;Fk1y6gnJ~lT#;AS%OD+}JeOtBh3!c+<_L*Q*u@8O!v^5-yc9j(o85_WlFgcTG0i!Wz^w{Ce?1!542^Kd)-7 zk1u|3HBT$rC+F6Om`ewHw#w@{IlAf6toipIxg4l_eJdYx@#fE0TAF;x67?$Q`|VBs zhgUx8mD#mj3VY32Zgcy_vS-6*6vtK1%I$UKuI_{f8#nd{d9k@LwcY_)s!xYcCWfio z_XR(TAK`uDoKv7<+!{^BWC`izLj+~o_6K(~?>F+wpJT%uzKwg{y4Ufet%Jg@r1gE9 zF1oWeXE7&jy!Da+x6WzeDVjF*i*yb4L``1s=av-;&VZf|Qn`f2aiGfq^ykbkViqvL7q zB8BbOAq#tF)|zqcPrWgpE9Fa1i-n8r>MHLbccXvT(t({FjDMOrwYE0Ph>{yKlPq}z zyWK}_$W7E@FZ#&G>$JctLdv2#-Q_l%p!Q8A*H?5i6u)pl|4PxlXmLLD8U7Xv8pX+S zeVxfQ@jVPmc27=1rSxr^=hpa^0`p9fui|FcX1ZDOCzNoppd)=1=C~7zWYePX6PT2g z>YZ8_FTRr4Ob}JOF|3#O?MGCTd z9sH_fO2znz#cUdcq@nn?3<}a&R@7&(T+x`F8Z7rTko#-;f2SLJ`=>le{o|*2kqHci z?i8v{?B6^!74HNKmr|ocZ&Qrabh(j6Mgy(0MkczP_zGB%Mkd~Ei?5CCgf8+`3$MIT zW_my^fG&E?g=F-qpztOvsF|h^deN|0lcBjrMz5*TXs1iOClD(<2QtdX5|kPNzmiSW z=vjf%o2$aRg3%fwEqxR;0;GLcZHXC~yoY0geOonkl`cx(-+mYyilf1;8+1I4}YrHys6x2F3tmfpNfi zARibA3<3rNLjd}*5^fwN>w-X2AON5{WAfu*pc&8{XaQ_T)pr29fIUDluou_|90U#l zhk+x&Q2@_>EN{sNs>y}-0$YHsz&2nzumd1h-U;jib^{xMO~7X0E%I0ax)N9gTn9fJ zbS^LpAor#fm7dF-1L!r(E4 zmVGam*HF{cL-nV2P%o1QkgI%wD*1gGa1A&GBmsyLY!(Yj23G-OBQks+fZUG;7- zJZfG?fI83-Z~*K8d%%V|!2$tOzyzoP&~#2~N>#uJPy$*22j~FW04;X3w&()1?kE65 zzyPQY&@5~Q)YOD&CZ-wJ5~u}O15^oH>V!H7kZX{e(deY!qYiNbC?glZ8K?_Tuhs(^ z0MygX0qPVFfI5#lu^B*v+8-bpTum6%IW7@!y!0PF#F1G|9HfSNkF9|E~RK0vc|9*_fM0x3W; z!0x<|o2h@2Y6g%BqyuR{e+?yBHjo8SS(GIuB^}}^`@sO^F%Y1*LBJ4z>PntKaimuO zknSj8BrpOPPL8(|0jW`xDJ2*SEC%KS6M*r6P+#`yg0Na6Wz*b-juo>6{Yy>s{>#14m5LgSW0agQr zz$#!RumV^PECZGTOMnvK7;qFg0vrYo0{ek|z+T`0a0oaFlmf>AJ>Udz6}SRi1}>3V z&m(XSI18KsP6M>#mt-Wr2wc!Wl51`Ye*US^mw2K#AiECS0`3EMfSW)$KyqpXRrWT@ z7nJUT-UI#u9ssX_N5D(K7I*#A^#}YJ1MC54rR@ir3(%5G%QG$0-@%ise*?Y(NkA$<=}1PPng0jKPYNO|E%UUz zQ+g#RE$_6WDF4f=9VH-EQ=NaPGuLCgtVm%U*g!`|u3dwIGck`=y{ zyX$zldARwo8*k;7mR>UHi}@nlkV9A&U=sX45^+B6)}rm z@8p)&{=&GbjnbCO>z>@b*meUa@8w5Dz^B>9ck-J4s4I281$0!4NBA`_HNOfS4>#l@ zZnCN)rp(@V$hBd&w<4w?QllBxtZH4(qLw=u1q$t)-UTdn>OK()UTBOLR%_<@US3lh z+4p1ac3jQcaI1mPrtECb9PxQwcXyFc5E_r^fz@&3YWgEI4?4!s>GppG}v0aF4NELa;UORFYR&q{Q3YDq^TV(8_ErW>kYS=Z0u8=Neo)E9q<7RUSuK2Q8lX zMW}@b<$gk&O|8LsSxKMKmcCLclrL!X&|?KwoEO(ukKMFIp5V_ApP|n(!s)h6k?axZP?WVztUvY95G|>=kk-nK-wA$&>r1OVPp)_x_3Elcq z!Fqg_n_Kz7Xk<6(i5HyAvunX2)JB~MCwzb6? z_c7sIS>M{64fnu+Z7@M(BPIK0g2-)3wzM{yvtP-s)aDYn`9>_z2H8tr)85};URjea z=53KZ`kb7-*obA?AVrB0J7>?C%Dq`1Q_hN0R%Js?Idfm>v)uu24{SbR?30LuG{#VK z=|kSy-Ml)V)?IV7qD@xD>=e?neKwq>mGnLDDhuwEjnuo>5NhNK$e>#__Ra>~DSfzm zLfnka#bu?pIJq#&Y2a38JaLV z&ea63qM!`vzI@7=L#w$d{(_kVQ;VI8w~K_~X(fGG{B`#0`qQHCbmB09 z3!_7}(}LA@;LK}D-x%L=?7o%dv+do4Hh9w1swq-5kyvs=xj5g}#)L?>uIQ~_%&Ouv zNK@tSr@_j(TFAwse11O<|Gp0@XVg&R+fu)3%vJN(W@YU^%4`4Mi~he%*cO^XSZ-a; zQq!4Mzqid1D|Wjs9Is+I`ETaW{bj{Mo#FUzt=M2^uAN+*hs~uAp-Uf`75p8pz^b@% zR?NeNGn4zUP#1*n`f!$N;ltbb?zZ%eSg1F2!#Ky>tNApy^c7jbrvzV-KHe>TXBIJD z*ajet^c-FKa&5(cmOj`neG69;gOyJveQaL(j;~OVu;_HE%?ez&nrcrE>HF}SZ|`=f z$T$W20NRpBAKR^nk-jZ2eYdwFM*14P^rhd580q`<(zk(yruktt!a|LLOi_^ZnPDMH zh>^aUFMW_$h@lNnM19UmeHrO!MeJkjmJ@lh@dXokJv5rGn~0&cK{u>q(YmsigG~OO z{-p0bOW&3?kw1cN4d{Lar3tZ`gH4Uj^S?c+NK*y2G)7GSX@@hN8uZQAl=EL(_>m2} z-3+V8PaD>&8AltNzP4-~Nru|8;D*@Xc*_dvhz*y%o2=;{3}=mwwuelU-p5-eeeqxV znzF_iFraiMfb_j(P7ZIP^_AW6;c9W3&aUn0@$dGOmIbju3wFCT z)S;oc^8DV{PT8~K`dD#(UwADCHn9OLGt+_H_U260Fu?By8=hI9pJ=!94}V9m|Go;p zZxD?|I^{t6f;2S<1CHEMIw9d7ZAFZ9L+0ShCHww< z1f;~{fCOA~n_hTyLaFttVmz&`%Z|8m1spo6l^f^ft2tJoV2?@7>OH5-8LlnI=1iEU zq=Of>|9tkQiscE%ikL)aw!sa%AL*zD-Rui-DQDht6&liE4qF|ydwM(?Rih&2h%@_! zv{v_UGh~OppD^Y7{382mPbxHYT$qPDa+eN__^X+t{`(d!k5p*1auIJm{OYdv_xgIS zRYgpK3mZylr9&t>-`u}s%)4Qh6&lhp6+wED37KO{<0@hfy0BA7Yb70QQT6V5&j#O4 zeyh;<;xfwvi%BrYCR@PA>v^#D9w>TATogU%Qd~WX|-KsFMl)H4kgwm|##CtM1%TKH=Vy3JvM#jFdi0e_olkt64=%KQH!z(n^PIJe-yN zW=YV8Jrx?#aU5f{*CqC@);O;sX1Nz@>V=v~2Y4KOFs-nLS?iA#8YjG11~ek1BR}-3 z1=imwpK+=}Lpl`1x8127OQvrdSP}D!TQAzm;~t=(F>B|Ga;1X@R?J%XOvkoQGYm$W zATW!t#;gDu++xrcpz9j5xh-&`^^2P}y7fVdosAiXFJ043Te2V5&g&O9ZFGAFP3cSw z>2va&+~3U;6A4T9$Nr?FKbC&%J7v<58aXF=e6lXgt3sPrVOK4a4&fO3=vruGNp8)G z80nY}>2vlKG15UD(g*J=Vx*%!q)*~wfwD$LsS46*AktU!DUqiKoA1xnR{wiZxWf|a z_wPmhK1OQNzo)@%sBlN$JXl;({%l;Y!O&BIvxOT}PqzqvJZ}GWlrJ6t28s^|D(ryl zep5M346Dq|9ZvL&Rqh((wvNOV3p>foZi8TlwkBpu|JnW~|`k&!B6D zc1uUONat}Njpp9o8EL3*?TLI;I2J;i)9~oI^X|x48o8AP>aQ z)PR}jpU-rS|GwK1Zyvm?ewnM_6jj@?55X{-cQ{*Fo%3QHKgumRJRCgagq27--bXs) z14U|vIT~^!7!BIefh`W?>PKN-z#8?d8KV*E2vh!kg#LcU&}qSpFhV(_7RS)%2 z^&lSA%WcfntCAKWJU1`UjAY;6k7ViiAnAA!p_9D4QL*8XESMUi*_K&J2M$RGl1Q4K zvU||P2F^k{qNth4+9b=3T?hSb6xO>r=fkb-#AY?e7FjxpXw|sq@lGn|)=;Bq6<#i# zRkU>T&njOBUJ;+X2{DG9*^lP%|KH4q!mdKRv2}iZ^JSLVD|#X>JmJR=ZrE?oF5)-4 z3Jx>r8N^3O$A4vXIos`FRHhm^Xl4QFD5OEp`ko$J{VATLVSU@uv^*@FbjDs5#4} zB1Sr1NjmMNB1SrZNjeWk6Qh>l$1W6Pj;T>PGNvMtbc&O7s7ytSbk5W6x$)+$XCxo4 zh>=c$>M|vG_=dK3?JHu8Q5HRYSmapHp#G@%7;miNtRRH5H3+6xCU6}{mb0}XT##3M zR)z|vZOKkG58VB=^&bZo;Mq5l7aSkI<=f$i$d&8CQ&#t~hbI}wwYgqo$n08ifwj)z zhjMyMKGNUklswPq$16?7`p4vKP%G}Dj=x)W-+@J{*4X*i$VyDlNr`jK>X($^nxTsA zr|KKex`%STgT*k8PE3O+(-mh}#>Zx-XJ#bDrpKnIq-SPj#%HCcIK(kaS zILMO8+u|;H?PxBKt;^(sitNX5Yve4f4d=*S4d8Uyt%F>3cC0Oz%pQ&9ZYpC`G4Uzx znK@~6{vA6yj*C-dWoN=jG3lwyvn^*+WYeFUEN6v>Ipd-(<2fritDnU=7LCq=v}oc4 z?wZ!BdXb!*y&A>o6{Sq!+~v3rnm?6GVD6(ie-<)|u?433ZME0jeYujQVb%9@Sitn4(E z|Cjv9Y3VRhu{`#35?7a<+sJ8KXxfJON-Y(9b=naaw)JVQ8LNGYQ?t}f+<3O@6xWvi z7_g0}xjC%wR<3~-rd>8`D>s|zZo)koH<`o3l}*Urw;!DN`UWhgx?GKpz76GOc>|j zN)Omjc_OZ~tM#NFjZKV??dKYwnVFu6^AO`Rvy;-(h{em@D5cI;&JGSQ=yK`Xd_pR$)m~tHgbmYhDBTEbH)nx zXC#`9X~VYY&giy$- zf!t4=6iWAUNjAc&PtBC3R64y_n5S`mV^%hc+{szH3RAu7zs8{YzezG|Gv`>7?joT= z#8te*qC(iu|V6 z!G5l$u5inU>1#iCLr1u=WP=ZI3k=2Sz;!@UTy`R}ILNhC49HYvxT?~!l346Pt|KdN z2X`pwgK0bJ5Lc5Ww&iel5XQ}5sdM3aPT}0{qQwWf{c^VIFn(h7D%yLP>nlh9I@^prdn zu72_aLSXAN``+kLfrVZO>u?f!S59zF%rS*q!OTu#p5K&?`E~tFZW_}%&9xvE1si&T z3u8-8a>4AxQO-(7xFujuk8-W?d9K$9uuarH^!U~B80xTHclIg`ZTw?D%%?hlcLtgq zL&VtQ+(}k26y=-b=cl>9PKw}ork%w1{vyO%Jalc zkrx|qp37qH=ec%_yTIXlJ#Gz!z8rcHMeV+Tc67Rc(8)_&6*hs`TNgOpSr<7S*5d-# zfGxU!Dm=QtWuuH?iSSpXYbb^HT|$$gp~FUBg1=U1C|S}4n9lw(`lLcyL&)~&#R?%Z zs9#Az$@*T#`dX2qCiB0<)zCtn+33q$h#g*%p~?%pC;S=EAu}v1Le(Pln7E;ez`p1C zWelW!SGXz$vo+G=kYbx<$pWr$b@2f`oG)YWNfW1EQjwy%K}3m|56(1a*ROEp{|CIN B&I14d literal 186296 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 { diff --git a/src/actions/kandel/populate.test.ts b/src/actions/kandel/populate.test.ts index 0a53d77..35848b4 100644 --- a/src/actions/kandel/populate.test.ts +++ b/src/actions/kandel/populate.test.ts @@ -1,9 +1,10 @@ -import { type Client, erc20Abi, parseEther, parseUnits } from 'viem' +import { parseEther, parseUnits } from 'viem' import { describe, expect, inject, it } from 'vitest' -import { minVolume, validateKandelParams } from '~mgv/index.js' +import { validateKandelParams } from '~mgv/index.js' import { BS } from '~mgv/lib/enums.js' import { getClient } from '~test/src/client.js' import { mintAndApprove } from '~test/src/contracts/index.js' +import { getMarkets } from '~test/src/markets.js' import { getBook } from '../book.js' import { simulateMarketOrderByVolumeAndMarket } from '../market-order.js' import { simulateBind, simulateDeployRouter } from '../smart-router.js' @@ -11,7 +12,7 @@ import { simulatePopulate } from './populate.js' import { simulateSow } from './sow.js' const { smartKandelSeeder, kandelSeeder } = inject('kandel') -const { wethUSDC } = inject('markets') +const { wethUSDC } = getMarkets() const actionParams = inject('mangrove') const client = getClient() diff --git a/src/actions/kandel/retract.test.ts b/src/actions/kandel/retract.test.ts index b350dd9..cdfc3dd 100644 --- a/src/actions/kandel/retract.test.ts +++ b/src/actions/kandel/retract.test.ts @@ -3,6 +3,7 @@ import { describe, expect, inject, it } from 'vitest' import { validateKandelParams } from '~mgv/index.js' import { getClient } from '~test/src/client.js' import { mintAndApprove } from '~test/src/contracts/index.js' +import { getMarkets } from '~test/src/markets.js' import { getBook } from '../book.js' import { simulateBind, simulateDeployRouter } from '../smart-router.js' import { simulatePopulate } from './populate.js' @@ -10,7 +11,7 @@ import { simulateRetract } from './retract.js' import { simulateSow } from './sow.js' const { smartKandelSeeder, kandelSeeder } = inject('kandel') -const { wethUSDC } = inject('markets') +const { wethUSDC } = getMarkets() const actionParams = inject('mangrove') const client = getClient() diff --git a/src/actions/kandel/sow.test.ts b/src/actions/kandel/sow.test.ts index 0cff982..6787ae6 100644 --- a/src/actions/kandel/sow.test.ts +++ b/src/actions/kandel/sow.test.ts @@ -1,10 +1,11 @@ import { isAddress } from 'viem' import { describe, expect, inject, it } from 'vitest' import { getClient } from '~test/src/client.js' +import { getMarkets } from '~test/src/markets.js' import { simulateSow } from './sow.js' const { smartKandelSeeder, kandelSeeder } = inject('kandel') -const { wethUSDC } = inject('markets') +const { wethUSDC } = getMarkets() const client = getClient() describe('sow Smart Kandel', () => { diff --git a/src/actions/kandel/view.test.ts b/src/actions/kandel/view.test.ts index fc04c2e..2299fb8 100644 --- a/src/actions/kandel/view.test.ts +++ b/src/actions/kandel/view.test.ts @@ -4,6 +4,7 @@ import { validateKandelParams } from '~mgv/index.js' import type { KandelParams, MarketParams } from '~mgv/index.js' import { BS, Order } from '~mgv/lib/enums.js' import { getClient } from '~test/src/client.js' +import { getMarkets } from '~test/src/markets.js' import { getBook } from '../book.js' import { simulateLimitOrder } from '../index.js' import { simulateBind, simulateDeployRouter } from '../smart-router.js' @@ -12,7 +13,7 @@ import { simulateSow } from './sow.js' import { KandelStatus, getKandelState } from './view.js' const { smartKandelSeeder } = inject('kandel') -const { wethUSDC } = inject('markets') +const { wethUSDC } = getMarkets() const actionParams = inject('mangrove') const client = getClient() diff --git a/src/actions/order/new.test.ts b/src/actions/order/new.test.ts index 475a977..156d5c4 100644 --- a/src/actions/order/new.test.ts +++ b/src/actions/order/new.test.ts @@ -11,13 +11,14 @@ import { describe, expect, inject, it } from 'vitest' import { limitOrderResultFromLogs } from '~mgv/lib/limit-order.js' import { tickFromVolumes } from '~mgv/lib/tick.js' import { getClient } from '~test/src/client.js' +import { getMarkets } from '~test/src/markets.js' import { BS, Order } from '../../lib/enums.js' import { getBook } from '../book.js' import { getUserRouter } from '../smart-router.js' import { getLimitOrderSteps, simulateLimitOrder } from './new.js' const params = inject('mangrove') -const { wethUSDC } = inject('markets') +const { wethUSDC } = getMarkets() const client = getClient() describe('new order', () => { diff --git a/src/actions/order/remove.test.ts b/src/actions/order/remove.test.ts index f315e32..0391934 100644 --- a/src/actions/order/remove.test.ts +++ b/src/actions/order/remove.test.ts @@ -6,12 +6,13 @@ import { removeOrderResultFromLogs, } from '~mgv/lib/limit-order.js' import { getClient } from '~test/src/client.js' +import { getMarkets } from '~test/src/markets.js' import { getBook } from '../book.js' import { simulateLimitOrder } from './new.js' import { simulateRemoveOrder } from './remove.js' const client = getClient() -const { wethUSDC } = inject('markets') +const { wethUSDC } = getMarkets() const params = inject('mangrove') describe('remove order', () => { diff --git a/src/actions/order/update.test.ts b/src/actions/order/update.test.ts index e6f2aa0..9a70bca 100644 --- a/src/actions/order/update.test.ts +++ b/src/actions/order/update.test.ts @@ -8,12 +8,13 @@ import { } from '~mgv/lib/limit-order.js' import { tickFromVolumes } from '~mgv/lib/tick.js' import { getClient } from '~test/src/client.js' +import { getMarkets } from '~test/src/markets.js' import { getBook } from '../book.js' import { simulateLimitOrder } from './new.js' import { simulateSetExpiration, simulateUpdateOrder } from './update.js' const client = getClient() -const { wethUSDC } = inject('markets') +const { wethUSDC } = getMarkets() const params = inject('mangrove') describe('update order', () => { diff --git a/src/actions/order/view.test.ts b/src/actions/order/view.test.ts index 7e078b6..0aee23b 100644 --- a/src/actions/order/view.test.ts +++ b/src/actions/order/view.test.ts @@ -3,6 +3,7 @@ import { describe, expect, inject, it } from 'vitest' import { limitOrderResultFromLogs } from '~mgv/lib/limit-order.js' import { tickFromVolumes } from '~mgv/lib/tick.js' import { getClient } from '~test/src/client.js' +import { getMarkets } from '~test/src/markets.js' import { BA, BS, Order } from '../../lib/enums.js' import { getBook } from '../book.js' import { getUserRouter } from '../smart-router.js' @@ -11,7 +12,7 @@ import { getOrder, getOrders } from './view.js' const client = getClient() const params = inject('mangrove') -const { wethUSDC } = inject('markets') +const { wethUSDC } = getMarkets() describe('view order', () => { it('single order', async () => { diff --git a/src/index.ts b/src/index.ts index 9e24602..d57d652 100644 --- a/src/index.ts +++ b/src/index.ts @@ -87,6 +87,7 @@ export { export type { MangroveActionsDefaultParams, MarketParams, + SerializableMarketParams, BuiltArgs, BuiltArgsWithValue, Book, @@ -105,6 +106,7 @@ export type { LocalConfig, CompleteToken, OLKey, + SerializableOLKey, RpcOffer, RpcOfferDetail, RpcCompleteOffer, diff --git a/src/lib/human-readable.test.ts b/src/lib/human-readable.test.ts index 01da759..519b483 100644 --- a/src/lib/human-readable.test.ts +++ b/src/lib/human-readable.test.ts @@ -1,5 +1,6 @@ import { parseUnits } from 'viem' import { describe, expect, inject, it } from 'vitest' +import { getMarkets } from '~test/src/markets.js' import { BA } from './enums.js' import { amounts, @@ -10,7 +11,7 @@ import { } from './human-readable.js' import { tickFromVolumes } from './tick.js' -const { wethUSDC } = inject('markets') +const { wethUSDC } = getMarkets() const { USDC, WETH } = inject('tokens') // price is quote/base diff --git a/src/lib/index.ts b/src/lib/index.ts index bc00ca4..070c7f5 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -142,4 +142,4 @@ export { // utils -export { mask } from './utils.js' +export { mask, fromSerializable, toSerializable } from './utils.js' diff --git a/src/lib/kandel/distribution.test.ts b/src/lib/kandel/distribution.test.ts index 031b431..4140090 100644 --- a/src/lib/kandel/distribution.test.ts +++ b/src/lib/kandel/distribution.test.ts @@ -2,12 +2,13 @@ import { parseAbi } from 'viem' import { expect, it } from 'vitest' import { describe, inject } from 'vitest' import { getClient } from '~test/src/client.js' +import { getMarkets } from '~test/src/markets.js' import { createGeometricDistribution } from './distribution.js' import { getKandelPositionRawParams } from './params.js' const { kandelLib, smartKandelSeeder } = inject('kandel') const { routerProxyFactory, mgv } = inject('mangrove') -const { wethUSDC } = inject('markets') +const { wethUSDC } = getMarkets() const client = getClient() diff --git a/src/lib/kandel/logs.test.ts b/src/lib/kandel/logs.test.ts index ad33249..3648c5f 100644 --- a/src/lib/kandel/logs.test.ts +++ b/src/lib/kandel/logs.test.ts @@ -2,11 +2,12 @@ import { isAddress } from 'viem' import { describe, expect, inject, it } from 'vitest' import { simulateSow } from '~mgv/actions/kandel/sow.js' import { getClient } from '~test/src/client.js' +import { getMarkets } from '~test/src/markets.js' import { hash } from '../ol-key.js' import { getKandelsFromLogs } from './logs.js' const { smartKandelSeeder } = inject('kandel') -const { wethUSDC } = inject('markets') +const { wethUSDC } = getMarkets() const client = getClient() describe('Kandel logs', () => { diff --git a/src/lib/kandel/params.test.ts b/src/lib/kandel/params.test.ts index 5493946..0ca6e25 100644 --- a/src/lib/kandel/params.test.ts +++ b/src/lib/kandel/params.test.ts @@ -1,6 +1,7 @@ import { describe, expect, inject, it } from 'vitest' import { getBook } from '~mgv/actions/book.js' import { getClient } from '~test/src/client.js' +import { getMarkets } from '~test/src/markets.js' import { humanPriceToRawPrice, rawPriceToHumanPrice, @@ -8,7 +9,7 @@ import { import { priceFromTick, tickFromPrice } from '../tick.js' import { getKandelPositionRawParams, validateKandelParams } from './params.js' -const { wethUSDC } = inject('markets') +const { wethUSDC } = getMarkets() const params = inject('mangrove') const client = getClient() diff --git a/src/lib/market-order-simulation.test.ts b/src/lib/market-order-simulation.test.ts index c0f2c89..04972a9 100644 --- a/src/lib/market-order-simulation.test.ts +++ b/src/lib/market-order-simulation.test.ts @@ -6,6 +6,7 @@ import { simulateSow } from '~mgv/actions/kandel/sow.js' import { validateKandelParams } from '~mgv/index.js' import { getClient } from '~test/src/client.js' import { mintAndApprove } from '~test/src/contracts/index.js' +import { getMarkets } from '~test/src/markets.js' import type { Book } from '../types/index.js' import { BS } from './enums.js' import { marketOrderSimulation } from './market-order-simulation.js' @@ -14,7 +15,7 @@ import { inboundFromOutbound, outboundFromInbound } from './tick.js' const client = getClient() const actionParams = inject('mangrove') const kandelSeeder = inject('kandel') -const { wethUSDC } = inject('markets') +const { wethUSDC } = getMarkets() const KANDEL_GASREQ = 128_000n diff --git a/src/lib/utils.ts b/src/lib/utils.ts index b52b34e..4f4f153 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,3 +1,34 @@ +import type { + MarketParams, + OLKey, + SerializableMarketParams, + SerializableOLKey, +} from '../types/index.js' + export function mask(n: bigint): bigint { return (1n << n) - 1n } + +export function toSerializable< + TType extends MarketParams | OLKey = MarketParams | OLKey, +>( + args: TType, +): TType extends MarketParams ? SerializableMarketParams : SerializableOLKey { + return { + ...args, + tickSpacing: Number(args.tickSpacing), + } as unknown as TType extends MarketParams + ? SerializableMarketParams + : SerializableOLKey +} + +export function fromSerializable< + TType extends SerializableMarketParams | SerializableOLKey = + | SerializableMarketParams + | SerializableOLKey, +>(args: TType): TType extends SerializableMarketParams ? MarketParams : OLKey { + return { + ...args, + tickSpacing: BigInt(args.tickSpacing), + } as unknown as TType extends SerializableMarketParams ? MarketParams : OLKey +} diff --git a/src/types/actions/index.ts b/src/types/actions/index.ts index ceefa91..d0dc7c5 100644 --- a/src/types/actions/index.ts +++ b/src/types/actions/index.ts @@ -28,6 +28,18 @@ export type MarketParams = { tickSpacing: bigint } +/** + * A serializable version of a market + * @param base The base token + * @param quote The quote token + * @param tickSpacing The tick spacing + */ +export type SerializableMarketParams = { + base: Token + quote: Token + tickSpacing: number +} + /** * List of args that are built by the transaction builders */ diff --git a/src/types/index.ts b/src/types/index.ts index b9d171b..d904bc8 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,6 +1,7 @@ export type { MangroveActionsDefaultParams, MarketParams, + SerializableMarketParams, BuiltArgs, BuiltArgsWithValue, Book, @@ -22,6 +23,7 @@ export type { LocalConfig, CompleteToken, OLKey, + SerializableOLKey, RpcOffer, RpcOfferDetail, RpcCompleteOffer, diff --git a/src/types/lib.ts b/src/types/lib.ts index d39ad99..afc7579 100644 --- a/src/types/lib.ts +++ b/src/types/lib.ts @@ -54,6 +54,18 @@ export type OLKey = { tickSpacing: bigint } +/** + * An OLKey object that is serializable. + * @param outbound_tkn The address of the outbound token. + * @param inbound_tkn The address of the inbound token. + * @param tickSpacing The tick spacing. + */ +export type SerializableOLKey = { + outbound_tkn: Address + inbound_tkn: Address + tickSpacing: number +} + /** * The offer object returned by the RPC endpoint. */ diff --git a/test/globalSetup.ts b/test/globalSetup.ts index 363465b..e3babf1 100644 --- a/test/globalSetup.ts +++ b/test/globalSetup.ts @@ -2,7 +2,8 @@ import { createAnvil, startProxy } from '@viem/anvil' import { type Address, parseAbi, parseEther, parseUnits } from 'viem' import { foundry } from 'viem/chains' import type { GlobalSetupContext } from 'vitest/node' -import type { MarketParams, Token } from '~mgv/index.js' +import type { SerializableMarketParams, Token } from '~mgv/index.js' +import { toSerializable } from '~mgv/lib/utils.js' import { globalTestClient } from '~test/src/client.js' import { accounts } from './src/constants.js' import { @@ -96,7 +97,6 @@ export default async function ({ provide }: GlobalSetupContext) { smartRouter: routerImplementation, routerProxyFactory, multicall, - tickSpacing: 60n, }) provide('kandel', { kandelLib, kandelSeeder, smartKandelSeeder }) @@ -124,7 +124,10 @@ export default async function ({ provide }: GlobalSetupContext) { parseEther('0.0001'), ) - provide('markets', { wethUSDC, wethDAI }) + provide('markets', { + wethUSDC: toSerializable(wethUSDC), + wethDAI: toSerializable(wethDAI), + }) // starts a proxy pool from there const shutdown = await startProxy({ @@ -164,11 +167,10 @@ declare module 'vitest' { smartRouter: Address routerProxyFactory: Address multicall: Address - tickSpacing: bigint } markets: { - wethUSDC: MarketParams - wethDAI: MarketParams + wethUSDC: SerializableMarketParams + wethDAI: SerializableMarketParams } kandel: { kandelLib: Address diff --git a/test/src/markets.ts b/test/src/markets.ts new file mode 100644 index 0000000..33ace4f --- /dev/null +++ b/test/src/markets.ts @@ -0,0 +1,10 @@ +import { inject } from 'vitest' +import { fromSerializable } from '~mgv/lib/utils.js' + +export function getMarkets() { + const markets = inject('markets') + return { + wethUSDC: fromSerializable(markets.wethUSDC), + wethDAI: fromSerializable(markets.wethDAI), + } +} From d20af2a3eb9f3834c1ce480067f1ea96dc32a0a9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 6 Sep 2024 17:18:05 +0200 Subject: [PATCH 23/28] chore: version package (#133) Co-authored-by: github-actions[bot] --- .changeset/polite-mayflies-cheat.md | 5 ----- src/CHANGELOG.md | 6 ++++++ src/package.json | 15 ++++++++++++--- 3 files changed, 18 insertions(+), 8 deletions(-) delete mode 100644 .changeset/polite-mayflies-cheat.md diff --git a/.changeset/polite-mayflies-cheat.md b/.changeset/polite-mayflies-cheat.md deleted file mode 100644 index e7abca7..0000000 --- a/.changeset/polite-mayflies-cheat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@mangrovedao/mgv": patch ---- - -Added serialize helper functions for markets and offer list keys diff --git a/src/CHANGELOG.md b/src/CHANGELOG.md index 9e5e6c4..ae419e1 100644 --- a/src/CHANGELOG.md +++ b/src/CHANGELOG.md @@ -1,5 +1,11 @@ # @mangrovedao/mgv +## 0.9.9 + +### Patch Changes + +- 692f50f: Added serialize helper functions for markets and offer list keys + ## 0.9.8 ### Patch Changes diff --git a/src/package.json b/src/package.json index 42413c5..318a9ab 100644 --- a/src/package.json +++ b/src/package.json @@ -1,7 +1,7 @@ { "name": "@mangrovedao/mgv", "description": "Utils and functions to interact with mangrove protocol", - "version": "0.9.8", + "version": "0.9.9", "repository": { "url": "https://github.com/mangrovedao/mgv" }, @@ -64,6 +64,15 @@ }, "license": "MIT", "homepage": "https://mangrove.exchange", - "authors": ["maxencerb.eth"], - "keywords": ["eth", "ethereum", "dapp", "mangrove", "order", "book"] + "authors": [ + "maxencerb.eth" + ], + "keywords": [ + "eth", + "ethereum", + "dapp", + "mangrove", + "order", + "book" + ] } From a96bb29b67c1852a8a29f1b73fac145ab28babca Mon Sep 17 00:00:00 2001 From: maxencerb Date: Fri, 6 Sep 2024 15:18:35 +0000 Subject: [PATCH 24/28] chore: format --- src/package.json | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/package.json b/src/package.json index 318a9ab..4147685 100644 --- a/src/package.json +++ b/src/package.json @@ -64,15 +64,6 @@ }, "license": "MIT", "homepage": "https://mangrove.exchange", - "authors": [ - "maxencerb.eth" - ], - "keywords": [ - "eth", - "ethereum", - "dapp", - "mangrove", - "order", - "book" - ] + "authors": ["maxencerb.eth"], + "keywords": ["eth", "ethereum", "dapp", "mangrove", "order", "book"] } From bcadd890ecf1adbe6377e9f6c02fd22222268eab Mon Sep 17 00:00:00 2001 From: Maxence Raballand Date: Fri, 6 Sep 2024 17:37:01 +0200 Subject: [PATCH 25/28] chore: update actions (#134) --- .github/actions/install-dependencies/action.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/install-dependencies/action.yml b/.github/actions/install-dependencies/action.yml index 49e699c..f1b6929 100644 --- a/.github/actions/install-dependencies/action.yml +++ b/.github/actions/install-dependencies/action.yml @@ -7,12 +7,12 @@ runs: - name: Set up Bun uses: oven-sh/setup-bun@v1 with: - bun-version: 1.1.6 + bun-version: 1.1.26 - name: Set up Node uses: actions/setup-node@v4 with: - node-version: 21 + node-version: 22 - name: Set up Foundry uses: foundry-rs/foundry-toolchain@v1 From d939aeb593c5ec142832dcac2fcb49cb6cf38c57 Mon Sep 17 00:00:00 2001 From: Maxence Raballand Date: Mon, 16 Sep 2024 18:27:48 +0200 Subject: [PATCH 26/28] feat: kandel reverve balance (#135) --- .changeset/mighty-glasses-cover.md | 5 +++++ src/actions/kandel/view.ts | 21 +++++++++++++++++++++ src/builder/kandel/view.ts | 12 ++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 .changeset/mighty-glasses-cover.md diff --git a/.changeset/mighty-glasses-cover.md b/.changeset/mighty-glasses-cover.md new file mode 100644 index 0000000..8881899 --- /dev/null +++ b/.changeset/mighty-glasses-cover.md @@ -0,0 +1,5 @@ +--- +"@mangrovedao/mgv": patch +--- + +Added base and quote reserve to kandel view function diff --git a/src/actions/kandel/view.ts b/src/actions/kandel/view.ts index 77c9fe7..945856e 100644 --- a/src/actions/kandel/view.ts +++ b/src/actions/kandel/view.ts @@ -16,6 +16,7 @@ import { offeredVolumeParams, provisionOfParams, quoteParams, + reserveBalanceParams, tickSpacingParams, } from '../../builder/kandel/view.js' import { @@ -59,6 +60,8 @@ export type GetKandelStateResult = { pricePoints: number quoteAmount: bigint baseAmount: bigint + reserveBalanceQuote: bigint + reserveBalanceBase: bigint unlockedProvision: bigint totalProvision: bigint kandelStatus: KandelStatus @@ -77,6 +80,8 @@ type KandelInitCallResult = { } baseAmount: bigint quoteAmount: bigint + reserveBalanceQuote: bigint + reserveBalanceBase: bigint unlockedProvision: bigint // mid price from the book midPrice: number @@ -99,6 +104,8 @@ async function kandelInitCall( params, _quoteAmount, _baseAmount, + _reserveBalanceQuote, + _reserveBalanceBase, _base, _quote, _tickSpacing, @@ -140,6 +147,14 @@ async function kandelInitCall( address: kandel, ...offeredVolumeParams(BA.asks), }, + { + address: kandel, + ...reserveBalanceParams(BA.bids), + }, + { + address: kandel, + ...reserveBalanceParams(BA.asks), + }, { address: kandel, ...baseParams, @@ -187,6 +202,10 @@ async function kandelInitCall( const baseAmount = _baseAmount.status === 'success' ? _baseAmount.result : 0n const quoteAmount = _quoteAmount.status === 'success' ? _quoteAmount.result : 0n + const reserveBalanceQuote = + _reserveBalanceQuote.status === 'success' ? _reserveBalanceQuote.result : 0n + const reserveBalanceBase = + _reserveBalanceBase.status === 'success' ? _reserveBalanceBase.result : 0n const asks = bestAsk.status === 'success' @@ -231,6 +250,8 @@ async function kandelInitCall( : { gasprice: 0, gasreq: 0, stepSize: 0, pricePoints: 0 }, baseAmount: reversed ? quoteAmount : baseAmount, quoteAmount: reversed ? baseAmount : quoteAmount, + reserveBalanceQuote: reversed ? reserveBalanceBase : reserveBalanceQuote, + reserveBalanceBase: reversed ? reserveBalanceQuote : reserveBalanceBase, reversed, unlockedProvision: unlockedProvision.status === 'success' ? unlockedProvision.result : 0n, diff --git a/src/builder/kandel/view.ts b/src/builder/kandel/view.ts index 39349c8..e3dee26 100644 --- a/src/builder/kandel/view.ts +++ b/src/builder/kandel/view.ts @@ -12,6 +12,7 @@ export const viewKandelABI = parseAbi([ olKeyABIRaw, 'function baseQuoteTickOffset() public view returns (uint)', 'function params() public view returns (Params memory)', + 'function reserveBalance(uint8 ba) public view returns (uint balance)', 'function offeredVolume(uint8 ba) public view returns (uint volume)', 'function getOffer(uint8 ba, uint index) public view returns (uint offer)', 'function offerIdOfIndex(uint8 ba, uint index) public view returns (uint offerId)', @@ -69,6 +70,17 @@ function parseBA(ba: BA) { return ba === BA.bids ? 0 : 1 } +export function reserveBalanceParams(ba: BA) { + return { + abi: viewKandelABI, + functionName: 'reserveBalance', + args: [parseBA(ba)], + } satisfies Omit< + ContractFunctionParameters, + 'address' + > +} + export function offeredVolumeParams(ba: BA) { return { abi: viewKandelABI, From 554629ad0b7168f1b7b305b88d63fe4a8724f28a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 18:28:22 +0200 Subject: [PATCH 27/28] chore: version package (#136) Co-authored-by: github-actions[bot] --- .changeset/mighty-glasses-cover.md | 5 ----- src/CHANGELOG.md | 6 ++++++ src/package.json | 15 ++++++++++++--- 3 files changed, 18 insertions(+), 8 deletions(-) delete mode 100644 .changeset/mighty-glasses-cover.md diff --git a/.changeset/mighty-glasses-cover.md b/.changeset/mighty-glasses-cover.md deleted file mode 100644 index 8881899..0000000 --- a/.changeset/mighty-glasses-cover.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@mangrovedao/mgv": patch ---- - -Added base and quote reserve to kandel view function diff --git a/src/CHANGELOG.md b/src/CHANGELOG.md index ae419e1..32882cc 100644 --- a/src/CHANGELOG.md +++ b/src/CHANGELOG.md @@ -1,5 +1,11 @@ # @mangrovedao/mgv +## 0.9.10 + +### Patch Changes + +- d939aeb: Added base and quote reserve to kandel view function + ## 0.9.9 ### Patch Changes diff --git a/src/package.json b/src/package.json index 4147685..c1c37f8 100644 --- a/src/package.json +++ b/src/package.json @@ -1,7 +1,7 @@ { "name": "@mangrovedao/mgv", "description": "Utils and functions to interact with mangrove protocol", - "version": "0.9.9", + "version": "0.9.10", "repository": { "url": "https://github.com/mangrovedao/mgv" }, @@ -64,6 +64,15 @@ }, "license": "MIT", "homepage": "https://mangrove.exchange", - "authors": ["maxencerb.eth"], - "keywords": ["eth", "ethereum", "dapp", "mangrove", "order", "book"] + "authors": [ + "maxencerb.eth" + ], + "keywords": [ + "eth", + "ethereum", + "dapp", + "mangrove", + "order", + "book" + ] } From 4a775b10468c8e66ed376c2512d047e192a50cc2 Mon Sep 17 00:00:00 2001 From: maxencerb Date: Mon, 16 Sep 2024 16:28:56 +0000 Subject: [PATCH 28/28] chore: format --- src/package.json | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/package.json b/src/package.json index c1c37f8..e451ead 100644 --- a/src/package.json +++ b/src/package.json @@ -64,15 +64,6 @@ }, "license": "MIT", "homepage": "https://mangrove.exchange", - "authors": [ - "maxencerb.eth" - ], - "keywords": [ - "eth", - "ethereum", - "dapp", - "mangrove", - "order", - "book" - ] + "authors": ["maxencerb.eth"], + "keywords": ["eth", "ethereum", "dapp", "mangrove", "order", "book"] }