Conversation
Add a chain-scoped Settler action for the canonical Deepstate engine with direct-route validation, exact bounded input accounting, unmatched-input refunds, no-rest enforcement, generic-call isolation, and live-fork coverage. Co-Authored-By: OpenAI Codex <codex@openai.com>
`DEEPSTATE` now fills one Deepstate book from the Settler's current `sellToken` balance, selecting `token0`/`token1` and the bid/ask side on-chain from the two tokens. Asks are sized directly by the sell amount; bids are sized through a caller-supplied `inversePriceX128` because the engine only accepts `token0` quantities. Unmatched quantity is discarded via `noRest` so it remains in the Settler for later actions, and multi-hop routes are expressed as chained actions. The call is hand-encoded to `fill` rather than passing a `FillParams[]` to `fillRoute`. Refund handling, balance reconciliation, input validation, the restricted-target override, and the two custom errors are removed; none are needed because the engine debits only matched quantity from `msg.sender` and pays `msg.sender` directly. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
| // per-order rounding is absorbed by the caller's choice of `tick`. | ||
| uint256 quantity = sellAmount; | ||
| if (isBid) { | ||
| (uint256 hi, uint256 lo) = tmp().omul(sellAmount, inversePriceX128).into(); |
There was a problem hiding this comment.
Wait.... is inversePriceX128 the off-chain computed realized price? That's not a good design because then the filling amount depends on slippage.
There was a problem hiding this comment.
This value, is directly related to the tick, I don't see a way to go around it. It is provided to avoid doing the tick match in the contract
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Describe native ETH via ERC-7528, define `inversePriceX128` as the reciprocal of the limit price one tick past `tick`, and state that the limit sizing bounds the engine's debit by the sell amount. The mixin comment names the one-wei partial-fill rounding the extra tick absorbs. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
🛡️ Immunefi PR ReviewsWe noticed that your project isn't set up for automatic code reviews. If you'd like this PR reviewed by the Immunefi team, you can request it manually using the link below: Once submitted, we'll take care of assigning a reviewer and follow up here. |
| // Orders are sized in `token0`. A bid spends `token1`, so the sell amount is converted through the | ||
| // reciprocal of the limit price, which bounds the engine's debit by the sell amount. The one wei the | ||
| // engine can add when it partially consumes an ask is absorbed by the caller taking `inversePriceX128` | ||
| // one tick past the limit. | ||
| if (isBid) { | ||
| (, sellAmount) = tmp().omul(sellAmount, inversePriceX128).ishr(128).into(); | ||
| } |
There was a problem hiding this comment.
In contrast, #640 does:
sellToken.safeApprove(address(ROBINHOOD_DEEPSTATE), 0);
sellToken.safeApprove(address(ROBINHOOD_DEEPSTATE), maxSellAmount);
ROBINHOOD_DEEPSTATE.fastFill(0, token0, token1, epoch, tick, quantity, isBid);
sellToken.safeApprove(address(ROBINHOOD_DEEPSTATE), 0);I haven't actually read the Deepstate code. Does Deepstate introspect the allowance amount and only pull up to the limit?
There was a problem hiding this comment.
no, it doesn't introspect, it pull anything needed to pay the debt but, 640 was setting the maxSellAmount as a guard to make sure not more than that is pulled.
There was a problem hiding this comment.
this is gonna be a pain for the liquidity integrations guys, but OK
There was a problem hiding this comment.
can you document this thoroughly in the changelog?
There was a problem hiding this comment.
I get it, that it is going to be pain, but, either they put a price there or we walk the tree. Confirming with Deepstate that there is no other way to go about this
| // Orders are sized in `token0`. A bid spends `token1`, so the sell amount is converted through the | ||
| // reciprocal of the limit price, which bounds the engine's debit by the sell amount. The one wei the | ||
| // engine can add when it partially consumes an ask is absorbed by the caller taking `inversePriceX128` | ||
| // one tick past the limit. | ||
| if (isBid) { | ||
| (, sellAmount) = tmp().omul(sellAmount, inversePriceX128).ishr(128).into(); | ||
| } |
There was a problem hiding this comment.
this is gonna be a pain for the liquidity integrations guys, but OK
Summary
Integrates the Deepstate on-chain limit-order engine on Robinhood Chain as the
DEEPSTATEaction.sellTokenbalance viappm. Multi-hop routes are chained actions, each sized from what actually arrived.token0/token1ordering and the bid/ask side are derived on-chain from the two tokens, with the ETH sentinel mapped to Deepstate'saddress(0).inversePriceX128(Q128token0pertoken1), since the engine only acceptstoken0quantities. An adverse price move reverts on the engine'stransferFrom.noRestis always forced on so unmatched quantity stays in the Settler for later actions instead of resting as an order the Settler could never cancel.fillOrKillis off; the finalAllowedSlippagecheck protects the taker.fill. No refund logic, no balance reconciliation, no restricted-target override, no custom errors.Gas Optimization
fillRoutepath (route array allocation, three balance reads, temporary approve/reset, refund transfer) with a single hand-encodedfillcall, one balance read, and a standing max approval viasafeApproveIfBelow.RobinHoodSettlerruntime size drops from 22,928 to 20,427 bytes versus the previous Deepstate commit on this branch.settler_deepstate_robinhoodwhen run in CI; the stale snapshot from the previous implementation was removed.Testing
test/integration/RobinHood.t.solruns the productionRobinHoodSettleragainst the live engine on a Robinhood Chain fork. Not run locally (noROBINHOOD_MAINNET_RPC_URL), so CI is the first execution. It currently uses tick 0 and should gain a negative-tick and a bid case before merge.Open items
0x6cf19308C22FC82ea620Fa0B3E94948d20f27B96has not been bytecode-compared against the upstream repository.🤖 Generated with Claude Code