Skip to content

feat: add Blue callback limit-order maker/taker flow - #5

Open
Oliverpt-1 wants to merge 21 commits into
mainfrom
feat/blue-callback-limit-order
Open

feat: add Blue callback limit-order maker/taker flow#5
Oliverpt-1 wants to merge 21 commits into
mainfrom
feat/blue-callback-limit-order

Conversation

@Oliverpt-1

@Oliverpt-1 Oliverpt-1 commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds production-shaped TypeScript maker and taker functions for a callback-backed Midnight lend offer, plus a deterministic Base-fork integration test. A lender can keep USDC supplied in Morpho Blue while a signed fixed-rate Midnight offer rests offchain.

Reusable functions

  • prepareBlueCallbackPosition() creates or resumes an empty callback, rejects funded callback reuse, approves and supplies Blue, and authorizes the ratifier
  • signBlueCallbackOffer() constructs and signs the callback-backed offer without changing onchain state
  • simulateTake() calculates units, reads the settlement fee, simulates Midnight.take, and adds a gas buffer
  • takeOffer() submits the simulated request

Anvil impersonation, fixture funding, collateral setup, and assertions remain isolated in callback-flow.test.ts.

Proven flow

  • supplies 100 USDC to the callback-owned Base cbBTC/USDC Blue position
  • signs a roughly 6% APR December 2026 Midnight offer
  • executes explicit 25 USDC and 10 USDC partial fills
  • verifies the actual Blue position falls from about 100 to 75 to 65 USDC
  • verifies lender credit, borrower debt, and borrower USDC receipt
  • tracks buyerAssetsBound separately as immediately executable callback capacity
  • rejects repeat preparation of an already-funded callback
  • validates the pinned Midnight market before setup writes
  • removes most remaining Blue liquidity and proves a stale oversized fill reverts

API boundary

The example creates and signs offers with the Midnight SDK and settles them directly through Midnight.take. It intentionally excludes live API publication and discovery because a public order book cannot index state that exists only on a private Anvil fork.

Verification

  • TypeScript compiler passed
  • Base-fork flow passed at block 50524275

@Oliverpt-1 Oliverpt-1 changed the title feat: add Blue callback limit-order fork test feat: add Blue callback limit-order maker/taker flow Aug 26, 2026
Comment thread blue-callback-limit-order/src/maker.ts Outdated
Comment thread blue-callback-limit-order/src/maker.ts Outdated
Comment thread blue-callback-limit-order/src/maker.ts
@tomrpl

tomrpl commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

@codex review the work, knowing we found this:

• Medium — test reports callback capacity as Blue balance: callback-flow.test.ts:201-206,219-229,270-274 uses buyerAssetsBound() for “Blue before/after”. That function returns min(callback supply, market liquidity, Blue token balance), not the callback’s actual supplied balance. Global Blue liquidity can make these numbers differ from the callback position. The test only checks that shares decreased; it does not prove 25/10 USDC withdrawals or the documented 100→75→65 balances. Assert actual Blue position assets/shares converted with current market totals, and assert the fill amounts explicitly.

• Medium — repeated maker setup silently adds funds: maker.ts:173-184 reuses an existing callback but always calls supplyBlue(). Calling prepareBlueCallbackPosition() twice with the same salt tops up the existing callback instead of failing or detecting already-funded state. The app-side implementation checks for an existing position and fails closed. Add the same guard, or document/parameterize top-up behavior.

• Test hygiene — market-ID validation happens after writes: callback-flow.test.ts:144-180 creates the callback, approves/supplies Blue, authorizes the ratifier, and signs the offer before validating MIDNIGHT_MARKET_ID. Move the touchMarket check before maker-side setup so stale constants fail without leaving partial state, even on the local fork.


async function blueSupplyAssets(callback: Address) {
// Position assets prove what the callback owns; buyerAssetsBound only measures immediately executable capacity.
return (await fetchAccrualPosition(callback, BLUE_MARKET_ID as MarketId, publicClient)).supplyAssets

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: the test now reads the callback’s accrued Blue position for the 100→75→65 assertions and keeps buyerAssetsBound only as executable capacity.

args: [new MarketParams(blueMarket).id, callback],
})
// An empty deployment can resume after failure, but a funded per-offer callback must not be silently topped up.
if (position[0] !== 0n) throw new Error('callback already has a Blue supply position')

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: preparation now rejects a callback with existing supply shares while still allowing an empty deployment to resume.

assert.notEqual(await publicClient.getCode({ address: CALLBACK_FACTORY }), '0x', 'callback factory is not deployed')

// Validate pinned market constants before any test funding or maker-side transactions.
const { result: computedMarketId } = await publicClient.simulateContract({

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: the pinned Midnight market is now validated before funding or maker-side transactions.

@antoncoding antoncoding left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic here looks good and very clean and helpful!

Comment thread blue-callback-limit-order/src/maker.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants