Skip to content

Commit

Permalink
fix: cabinet comparitor (#172)
Browse files Browse the repository at this point in the history
* fix: cabinet comparitor

* test: overflow coverage
  • Loading branch information
mfw78 authored Oct 10, 2023
1 parent 8652d2f commit dde6143
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 25 deletions.
31 changes: 23 additions & 8 deletions src/composable/orderTypes/Twap.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ describe('To String', () => {
describe('Poll Validate', () => {
const blockNumber = 123456
const blockTimestamp = 1700000000
const mockStartTimestamp: jest.MockedFunction<(params: OwnerContext) => Promise<number>> = jest.fn()
const mockCabinet: jest.MockedFunction<(params: OwnerContext) => Promise<string>> = jest.fn()
const mockEndTimestamp: jest.MockedFunction<(startTimestamp: number) => number> = jest.fn()
const mockGetBlock: jest.MockedFunction<
(
Expand All @@ -308,7 +308,7 @@ describe('Poll Validate', () => {
return super.pollValidate(params)
}

startTimestamp = mockStartTimestamp
cabinet = mockCabinet
endTimestamp = mockEndTimestamp
}
const twap = new MockTwap({ handler: TWAP_ADDRESS, data: TWAP_PARAMS_TEST })
Expand All @@ -319,15 +319,14 @@ describe('Poll Validate', () => {

test(`Open TWAP, passes the validations`, async () => {
// GIVEN: A TWAP that should be active (should start 1 second ago, should finish in 1 second)
mockStartTimestamp.mockReturnValue(Promise.resolve(blockTimestamp - 1))
mockCabinet.mockReturnValue(uint256Helper(blockTimestamp - 1))
mockEndTimestamp.mockReturnValue(blockTimestamp + 1)

// WHEN: We poll
const result = await twap.pollValidate({ ...pollParams, blockInfo: { blockNumber, blockTimestamp } })

// THEN: The start time and end time of the TWAP will be checked
expect(mockStartTimestamp).toBeCalledTimes(1)
expect(mockEndTimestamp).toBeCalledTimes(1)
expect(mockCabinet).toBeCalledTimes(1)

// THEN: Successful validation
expect(result).toEqual(undefined)
Expand All @@ -336,7 +335,7 @@ describe('Poll Validate', () => {
test(`[TRY_AT_EPOCH] TWAP has not started`, async () => {
// GIVEN: A TWAP that hasn't started (should start in 1 second, should finish in 2 second)
const startTime = blockTimestamp + 1
mockStartTimestamp.mockReturnValue(Promise.resolve(startTime))
mockCabinet.mockReturnValue(uint256Helper(startTime))
mockEndTimestamp.mockReturnValue(blockTimestamp + 2)

// WHEN: We poll
Expand All @@ -353,7 +352,7 @@ describe('Poll Validate', () => {
test(`[TRY_AT_EPOCH] TWAP has expired`, async () => {
// GIVEN: A TWAP that has already expired (started 2 seconds ago, finished 1 second ago)
const expireTime = blockTimestamp - 1
mockStartTimestamp.mockReturnValue(Promise.resolve(blockTimestamp - 2))
mockCabinet.mockReturnValue(uint256Helper(blockTimestamp - 2))
mockEndTimestamp.mockReturnValue(expireTime)

// WHEN: We poll
Expand All @@ -366,6 +365,20 @@ describe('Poll Validate', () => {
})
})

test(`[CABINET OVERFLOW] The cabinet stored value is greater than uint32`, async () => {
// GIVEN: The cabinet stored value is greater than uint32
mockCabinet.mockReturnValue(uint256Helper(2 ** 32))

// WHEN: We poll
const result = await twap.pollValidate({ ...pollParams, blockInfo: { blockNumber, blockTimestamp } })

// THEN: Then, it will return an error instructing to not try again (expired order is a final state, so there's no point to keep polling)
expect(result).toEqual({
result: PollResultCode.DONT_TRY_AGAIN,
reason: 'Cabinet epoch out of range: 4294967296',
})
})

test(`If there's no blockInfo, it will fetch the latest block`, async () => {
// GIVEN: We don't provide the blockInfo with the poll params
const blockInfo = undefined
Expand All @@ -378,7 +391,7 @@ describe('Poll Validate', () => {
} as providers.Block)
)
const startTime = blockTimestamp + 1
mockStartTimestamp.mockReturnValue(Promise.resolve(startTime))
mockCabinet.mockReturnValue(uint256Helper(startTime))
mockEndTimestamp.mockReturnValue(blockTimestamp + 2)

// WHEN: We poll
Expand Down Expand Up @@ -578,3 +591,5 @@ describe('Current TWAP part is in the Order Book', () => {
})
})
})

const uint256Helper = (n: number) => Promise.resolve(utils.defaultAbiCoder.encode(['uint256'], [n]))
64 changes: 47 additions & 17 deletions src/composable/orderTypes/Twap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,13 +273,21 @@ export class Twap extends ConditionalOrder<TwapData, TwapStruct> {
}

const cabinet = await this.cabinet(params)
const cabinetEpoch = utils.defaultAbiCoder.decode(['uint256'], cabinet)[0]
const rawCabinetEpoch = utils.defaultAbiCoder.decode(['uint256'], cabinet)[0] as BigNumber

// Guard against out-of-range cabinet epoch
if (rawCabinetEpoch.gt(MAX_UINT32)) {
throw new Error(`Cabinet epoch out of range: ${rawCabinetEpoch.toString()}`)
}

// Convert the cabinet epoch (bignumber) to a number.
const cabinetEpoch = rawCabinetEpoch.toNumber()

if (cabinetEpoch === 0) {
throw new Error('Cabinet is not set. Required for TWAP orders that start at mining time.')
}

return parseInt(cabinet, 16)
return cabinetEpoch
}

/**
Expand Down Expand Up @@ -313,27 +321,49 @@ export class Twap extends ConditionalOrder<TwapData, TwapStruct> {
const { blockInfo = await getBlockInfo(params.provider) } = params
const { blockTimestamp } = blockInfo

const startTimestamp = await this.startTimestamp(params)
try {
const startTimestamp = await this.startTimestamp(params)

if (startTimestamp > blockTimestamp) {
// The start time hasn't started
return {
result: PollResultCode.TRY_AT_EPOCH,
epoch: startTimestamp,
reason: `TWAP hasn't started yet. Starts at ${startTimestamp} (${formatEpoch(startTimestamp)})`,
if (startTimestamp > blockTimestamp) {
// The start time hasn't started
return {
result: PollResultCode.TRY_AT_EPOCH,
epoch: startTimestamp,
reason: `TWAP hasn't started yet. Starts at ${startTimestamp} (${formatEpoch(startTimestamp)})`,
}
}

const expirationTimestamp = this.endTimestamp(startTimestamp)
if (blockTimestamp >= expirationTimestamp) {
// The order has expired
return {
result: PollResultCode.DONT_TRY_AGAIN,
reason: `TWAP has expired. Expired at ${expirationTimestamp} (${formatEpoch(expirationTimestamp)})`,
}
}

return undefined
} catch (err: any) {
if (err?.message?.includes('Cabinet is not set')) {
// in this case we have a firm reason to not monitor this order as the cabinet is not set
return {
result: PollResultCode.DONT_TRY_AGAIN,
reason: `${err?.message}. User likely removed the order.`,
}
} else if (err?.message?.includes('Cabinet epoch out of range')) {
// in this case we have a firm reason to not monitor this order as the cabinet is not set correctly
return {
result: PollResultCode.DONT_TRY_AGAIN,
reason: `${err?.message}`,
}
}
}

const expirationTimestamp = this.endTimestamp(startTimestamp)
if (blockTimestamp >= expirationTimestamp) {
// The order has expired
return {
result: PollResultCode.DONT_TRY_AGAIN,
reason: `TWAP has expired. Expired at ${expirationTimestamp} (${formatEpoch(expirationTimestamp)})`,
result: PollResultCode.UNEXPECTED_ERROR,
reason: `Unexpected error: ${err.message}`,
error: err,
}
}

return undefined
}

/**
Expand Down

0 comments on commit dde6143

Please sign in to comment.