Skip to content

Commit 7cf3297

Browse files
authored
fix, test: circuit breaker param (#647)
* fix, test: circuit breaker param * test: simplify * test: sanity check unimind constant params * test: fix build * test: update Unimind config checks to Zod
1 parent d4e9d1b commit 7cf3297

5 files changed

Lines changed: 121 additions & 3 deletions

File tree

lib/util/constants.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { ChainId } from './chain'
22
import { BigNumber } from 'ethers'
3+
import { z } from 'zod'
34

45
export const WEBHOOK_CONFIG_BUCKET = 'order-webhook-notification-config'
56
export const PRODUCTION_WEBHOOK_CONFIG_KEY = 'production.json'
@@ -46,8 +47,9 @@ export const DEFAULT_UNIMIND_PARAMETERS = JSON.stringify({
4647
export const UNIMIND_UPDATE_THRESHOLD = 25
4748
export const UNIMIND_CIRCUIT_BREAKER_MAX_BATCH = 5 // Circuit breaker active for batches 0-5
4849
export const UNIMIND_CIRCUIT_BREAKER_MIN_ORDERS = 4 // Order count to begin checking circuit breaker
49-
export const UNIMIND_CIRCUIT_BREAKER_FILL_RATE_THRESHOLD = 25 // Fill rate at or below this value triggers circuit breaker
50+
export const UNIMIND_CIRCUIT_BREAKER_FILL_RATE_THRESHOLD = 0.25 // Fill rate at or below this value triggers circuit breaker
5051
export const UNIMIND_DEV_SWAPPER_ADDRESS = '0x2b813964306D8F12bdaB5504073a52e5802f049D'
52+
5153
// Direct pi and tau to use for curve; Not intrinsicValues
5254
export const PUBLIC_STATIC_PARAMETERS = {
5355
pi: 15,
@@ -58,6 +60,25 @@ export const PUBLIC_STATIC_PARAMETERS = {
5860
export const UNIMIND_MAX_TAU_BPS = 25
5961
export const UNIMIND_LARGE_PRICE_IMPACT_THRESHOLD = 1.25 // 1.25% price impact threshold
6062

63+
// Sanity-check unimind constants at module load time so misconfigurations fail fast.
64+
const UnimindConstantsSchema = z.object({
65+
updateThreshold: z.number().int().positive(),
66+
circuitBreakerMaxBatch: z.number().int().nonnegative(),
67+
circuitBreakerMinOrders: z.number().int().positive(),
68+
circuitBreakerFillRateThreshold: z.number().min(0).max(1),
69+
maxTauBps: z.number().positive(),
70+
devSwapperAddress: z.string().regex(/^0x[0-9a-fA-F]{40}$/),
71+
})
72+
73+
UnimindConstantsSchema.parse({
74+
updateThreshold: UNIMIND_UPDATE_THRESHOLD,
75+
circuitBreakerMaxBatch: UNIMIND_CIRCUIT_BREAKER_MAX_BATCH,
76+
circuitBreakerMinOrders: UNIMIND_CIRCUIT_BREAKER_MIN_ORDERS,
77+
circuitBreakerFillRateThreshold: UNIMIND_CIRCUIT_BREAKER_FILL_RATE_THRESHOLD,
78+
maxTauBps: UNIMIND_MAX_TAU_BPS,
79+
devSwapperAddress: UNIMIND_DEV_SWAPPER_ADDRESS,
80+
})
81+
6182
// When pi = 0, AMM will be favored over Dutch Auction
6283
export const USE_CLASSIC_PARAMETERS = {
6384
pi: 0,

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@
8080
"express": "^4.18.2",
8181
"joi": "^17.7.0",
8282
"source-map-support": "^0.5.21",
83-
"uuid": "^9.0.0"
83+
"uuid": "^9.0.0",
84+
"zod": "3"
8485
},
8586
"prettier": {
8687
"printWidth": 120,

test/integ/crons/unimind-algorithm.test.ts

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { DynamoUnimindParametersRepository } from '../../../lib/repositories/uni
55
import { DutchOrdersRepository } from '../../../lib/repositories/dutch-orders-repository'
66
import { DutchV3OrderEntity, ORDER_STATUS } from '../../../lib/entities'
77
import { OrderType } from '@uniswap/uniswapx-sdk'
8-
import { DEFAULT_UNIMIND_PARAMETERS, UNIMIND_ALGORITHM_VERSION, UNIMIND_DEV_SWAPPER_ADDRESS, UNIMIND_UPDATE_THRESHOLD } from '../../../lib/util/constants'
8+
import { DEFAULT_UNIMIND_PARAMETERS, UNIMIND_ALGORITHM_VERSION, UNIMIND_DEV_SWAPPER_ADDRESS, UNIMIND_UPDATE_THRESHOLD, UNIMIND_CIRCUIT_BREAKER_MIN_ORDERS } from '../../../lib/util/constants'
99

1010
const dynamoConfig = {
1111
convertEmptyValues: true,
@@ -281,6 +281,90 @@ describe('updateParameters Test', () => {
281281
expect(pairData?.lastUpdatedAt).toBeDefined()
282282
})
283283

284+
it('should not trigger circuit breaker when fill rate is above threshold', async () => {
285+
const cbPair = '0xCB01-0xCB02-42161'
286+
const filledOrder: DutchV3OrderEntity = {
287+
...mockOrder,
288+
pair: cbPair,
289+
orderStatus: ORDER_STATUS.FILLED,
290+
fillBlock: 315641562,
291+
cosignerData: {
292+
...mockOrder.cosignerData,
293+
decayStartBlock: 315641558,
294+
},
295+
priceImpact: 0.5,
296+
usedUnimind: true,
297+
}
298+
299+
// Insert UNIMIND_CIRCUIT_BREAKER_MIN_ORDERS filled orders
300+
const orders: DutchV3OrderEntity[] = []
301+
for (let i = 0; i < UNIMIND_CIRCUIT_BREAKER_MIN_ORDERS; i++) {
302+
const order = { ...filledOrder, orderHash: `0xCB_FILLED_${i}` }
303+
orders.push(order)
304+
await ordersTable.putOrderAndUpdateNonceTransaction(order)
305+
}
306+
307+
// Pair exists in early batch, no orders counted yet
308+
await unimindParametersRepository.put({
309+
pair: cbPair,
310+
intrinsicValues: DEFAULT_UNIMIND_PARAMETERS,
311+
count: 0,
312+
version: UNIMIND_ALGORITHM_VERSION,
313+
batchNumber: 2, // Within circuit breaker window (<= 5)
314+
lastUpdatedAt: Math.floor(Date.now() / 1000),
315+
})
316+
317+
await updateParameters(unimindParametersRepository, ordersTable, log)
318+
319+
const pairData = await unimindParametersRepository.getByPair(cbPair)
320+
// Fill rate is 1.0 (100%), well above 25% threshold — circuit breaker should NOT fire
321+
expect(pairData?.batchNumber).toBe(2) // Unchanged — no update triggered
322+
expect(pairData?.count).toBe(UNIMIND_CIRCUIT_BREAKER_MIN_ORDERS) // Just incremented
323+
324+
// Cleanup
325+
await ordersTable.deleteOrders(orders.map((o) => o.orderHash))
326+
})
327+
328+
it('should trigger circuit breaker when fill rate is below threshold', async () => {
329+
const cbPair = '0xCB03-0xCB04-42161'
330+
const expiredOrder: DutchV3OrderEntity = {
331+
...mockOrder,
332+
pair: cbPair,
333+
orderStatus: ORDER_STATUS.EXPIRED,
334+
fillBlock: undefined as any,
335+
priceImpact: 0.5,
336+
usedUnimind: true,
337+
}
338+
339+
// Insert UNIMIND_CIRCUIT_BREAKER_MIN_ORDERS expired orders
340+
const orders: DutchV3OrderEntity[] = []
341+
for (let i = 0; i < UNIMIND_CIRCUIT_BREAKER_MIN_ORDERS; i++) {
342+
const order = { ...expiredOrder, orderHash: `0xCB_EXPIRED_${i}` }
343+
orders.push(order)
344+
await ordersTable.putOrderAndUpdateNonceTransaction(order)
345+
}
346+
347+
// Pair exists in early batch, no orders counted yet
348+
await unimindParametersRepository.put({
349+
pair: cbPair,
350+
intrinsicValues: DEFAULT_UNIMIND_PARAMETERS,
351+
count: 0,
352+
version: UNIMIND_ALGORITHM_VERSION,
353+
batchNumber: 2,
354+
lastUpdatedAt: Math.floor(Date.now() / 1000),
355+
})
356+
357+
await updateParameters(unimindParametersRepository, ordersTable, log)
358+
359+
const pairData = await unimindParametersRepository.getByPair(cbPair)
360+
// Fill rate is 0.0, below 25% threshold — circuit breaker SHOULD fire
361+
expect(pairData?.batchNumber).toBe(3) // Incremented — update was triggered
362+
expect(pairData?.count).toBe(0) // Reset after update
363+
364+
// Cleanup
365+
await ordersTable.deleteOrders(orders.map((o) => o.orderHash))
366+
})
367+
284368
it('should reset batchNumber to 0 on version mismatch', async () => {
285369
await unimindParametersRepository.put({
286370
pair: testPair,
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// The Zod schema in constants.ts validates unimind constants at module load time.
2+
// This test simply verifies the module loads without throwing.
3+
describe('Unimind constants sanity checks', () => {
4+
it('should pass Zod validation on import', () => {
5+
expect(() => require('../../../lib/util/constants')).not.toThrow()
6+
})
7+
})

yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9258,3 +9258,8 @@ yocto-queue@^0.1.0:
92589258
version "0.1.0"
92599259
resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz"
92609260
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
9261+
9262+
zod@3:
9263+
version "3.25.76"
9264+
resolved "https://registry.yarnpkg.com/zod/-/zod-3.25.76.tgz#26841c3f6fd22a6a2760e7ccb719179768471e34"
9265+
integrity sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==

0 commit comments

Comments
 (0)