wip: drop order/slippage code the funding rail no longer reaches

Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
2026-08-07 17:25:28 -07:00
co-authored by Hanzo Dev
parent f8ca4a37be
commit 42d2759b52
3 changed files with 10 additions and 170 deletions
+2 -94
View File
@@ -5,7 +5,6 @@ package dex
import (
"errors"
"math"
"math/big"
"github.com/luxfi/geth/common"
@@ -101,26 +100,7 @@ const (
GasNativeSettlement uint64 = 50_000 // decode + SM get/bind + replay slot + credit + SM remove + DEXFill log (Phase B).
)
// maxOrderTTL is the FINITE reclaim horizon applied to any value-path swap order that
// did NOT name an explicit deadline (a plain swap(), or a DI01 body with deadline 0). It
// bounds how long a taker's locked principal can sit unsettled before reclaimIntent can
// refund it. Seven days (in seconds) is generous enough for any honest cross-domain
// settlement to land yet guarantees the principal is never permanently stranded — the
// structural fix for the deadline-0 fund-lock. A taker who wants a tighter window passes an
// explicit DI01 deadline; this is only the floor for those who pass none.
const maxOrderTTL uint64 = 7 * 24 * 60 * 60 // 604800s = 7 days
// defaultedReclaimDeadline returns the finite deadline to stamp on an order that supplied
// none: blockTimestamp + maxOrderTTL, saturating at the uint64 max so the addition can
// never wrap to a SMALL (already-past) value (which would let reclaim fire immediately, or —
// worse — a wrapped tiny deadline could let a Phase-B settlement be rejected as "late" right
// away). Saturation keeps the horizon in the future for every realistic block time.
func defaultedReclaimDeadline(blockTimestamp uint64) uint64 {
if blockTimestamp > math.MaxUint64-maxOrderTTL {
return math.MaxUint64
}
return blockTimestamp + maxOrderTTL
}
// isNativeAsset reports whether an injective AssetID is native LUX (all-zero).
func isNativeAsset(id [32]byte) bool { return id == ([32]byte{}) }
@@ -290,73 +270,10 @@ func buildTransfer(key PoolKey, params SwapParams, caller common.Address) (Trans
}, nil
}
// priceScale is the fixed-point scale of the CLOB price wire (price × 1e8) — the
// matcher's PriceInt grid, byte-identical with dex/pkg/zapwire.PriceScale (==
// lx.PriceMultiplier) and the chains/dexvm proxy's PriceScale. Quote-per-base limits
// and Fill prices cross the C<->D seam as EXACT integers on this grid; there is NO
// float64 on the value path (its out-of-range int conversion is architecture-
// dependent and forks — the cross-arch consensus bug the integer wire closes).
const priceScale = 100000000
// priceLimitToCLOB converts the V4 SqrtPriceLimitX96 into the CLOB price domain the
// dexvm settles in — quote-per-base as a uint64 FIXED-POINT ×priceScale value (the
// PriceInt grid), computed as an EXACT integer so no float→int conversion ever
// touches the value the settle path compares against. It is the slippage floor the
// matcher / settle path enforces: a fill WORSE than this is refused (bounded MEV).
//
// price (quote/base) = (sqrtPriceX96 / 2^96)^2 = currency1 per currency0,
// grid value = round(price × priceScale) = round(s^2 × priceScale / 2^192).
//
// Round-to-NEAREST (not floor): the client's sqrtPriceLimitX96 is a floored sqrt, so its
// exact square sits an epsilon below the intended price; flooring the grid value would
// quantize a "50" limit to 49.99999999 and fail to cross a maker resting AT 50. Rounding
// to the nearest PriceInt quantizes to the price the taker meant, deterministically.
//
// DIRECTION. limitIsUpper reports which side the limit bounds, matching the dexvm's
// fill-side convention:
// - zeroForOne (sell currency0/base for currency1/quote => CLOB SELL): the V4 limit is
// the MINIMUM sqrt price, i.e. a price FLOOR — reject fills BELOW it (limitIsUpper=false).
// - !zeroForOne (buy currency0/base with currency1/quote => CLOB BUY): the V4 limit is
// the MAXIMUM sqrt price, i.e. a price CEILING — reject fills ABOVE it (limitIsUpper=true).
//
// A SqrtPriceLimitX96 at/beyond the V4 sentinel bounds (MinSqrtRatio for zeroForOne,
// MaxSqrtRatio for !zeroForOne) means "no limit" — returns 0 so the settle path imposes
// no floor (the pre-existing unbounded behavior, preserved).
func priceLimitToCLOB(params SwapParams) (priceLimitUnits uint64, limitIsUpper bool) {
limitIsUpper = !params.ZeroForOne
s := params.SqrtPriceLimitX96
if s == nil || s.Sign() <= 0 {
return 0, limitIsUpper // unset => no limit
}
// Treat the V4 "no limit" sentinels as unbounded: a zeroForOne floor at/below
// MinSqrtRatio, or a !zeroForOne ceiling at/above MaxSqrtRatio, imposes nothing.
if params.ZeroForOne {
if s.Cmp(MinSqrtRatio) <= 0 {
return 0, limitIsUpper
}
} else {
if s.Cmp(MaxSqrtRatio) >= 0 {
return 0, limitIsUpper
}
}
// grid = round(s^2 × priceScale / 2^192), EXACT big.Int arithmetic (add 2^191 before
// the >>192 floor-divide = round-half-up). The matcher's crossing loop may project this
// back to a float for display, but the CONSENSUS value the settle floor compares against
// is this exact PriceInt grid integer.
units := new(big.Int).Mul(s, s)
units.Mul(units, big.NewInt(priceScale))
units.Add(units, new(big.Int).Lsh(big.NewInt(1), 191)) // + 2^191 for round-to-nearest
units.Rsh(units, 192)
if units.Sign() <= 0 || !units.IsUint64() {
return 0, limitIsUpper // degenerate / out of range => no limit (unbounded)
}
return units.Uint64(), limitIsUpper
}
// balanceDeltaForOutput maps a credited output amount to the V4 BalanceDelta
// convention (output paid out to taker = negative on the output side; the input
// side already moved at Phase A so it is zero on the settlement leg).
// convention (output paid out to caller = negative on the output side; the input
// side already moved when the value crossed, so it is zero on the import leg).
func balanceDeltaForOutput(params SwapParams, amountOut *big.Int) BalanceDelta {
out := new(big.Int).Neg(amountOut)
if params.ZeroForOne {
@@ -366,15 +283,6 @@ func balanceDeltaForOutput(params SwapParams, amountOut *big.Int) BalanceDelta {
return BalanceDelta{Amount0: out, Amount1: big.NewInt(0)}
}
// minAmountOut derives the swap's slippage floor from V4 SwapParams (exact-output
// names the floor; exact-input leaves it to the D price limit). Routing only.
func minAmountOut(params SwapParams) *big.Int {
if params.AmountSpecified != nil && params.AmountSpecified.Sign() > 0 {
return new(big.Int).Set(params.AmountSpecified)
}
return big.NewInt(0)
}
// swapAssetDirection returns the (tokenIn, tokenOut) injective AssetIDs implied by
// the pool key and swap direction. ZeroForOne true => in=currency0, out=currency1.
func swapAssetDirection(key PoolKey, params SwapParams) (in, out [32]byte) {
-66
View File
@@ -5,9 +5,7 @@ package dex
import (
"bytes"
"encoding/binary"
"errors"
"math/big"
"github.com/luxfi/geth/common"
"github.com/luxfi/ids"
@@ -180,68 +178,4 @@ func EncodeSettlementHookData(claimID ids.ID, object []byte) []byte {
return out
}
// orderBodyLen is the Phase-A body width with a deadline only: deadline(32).
// orderBodyLenWithNonce adds the nonce word: deadline(32) | nonce(32). An empty body
// (the common plain-swap case) means deadline 0, nonce 0.
const (
orderBodyLen = 32
orderBodyLenWithNonce = 64
)
// decodeOrderBody parses the OPTIONAL Phase-A order body into (deadline, nonce). An
// empty body => (0, 0) — the plain-swap default. A 32-byte body carries only a deadline
// (nonce 0). A 64-byte body carries deadline + nonce. Any other width, or a value that
// does not fit uint64, is malformed (a swap that carries garbage where a deadline/nonce
// should be reverts rather than silently dropping it and deriving a mismatched id /
// stranding funds with no reclaim horizon).
func decodeOrderBody(body []byte) (deadline, nonce uint64, err error) {
switch len(body) {
case 0:
return 0, 0, nil
case orderBodyLen:
d := new(big.Int).SetBytes(body[0:32])
if !d.IsUint64() {
return 0, 0, ErrOrderBadDeadline
}
return d.Uint64(), 0, nil
case orderBodyLenWithNonce:
d := new(big.Int).SetBytes(body[0:32])
n := new(big.Int).SetBytes(body[32:64])
if !d.IsUint64() || !n.IsUint64() {
return 0, 0, ErrOrderBadDeadline
}
return d.Uint64(), n.Uint64(), nil
default:
return 0, 0, ErrOrderBodyMalformed
}
}
// EncodeOrderHookData builds an explicit Phase-A hookData: the tag, plus deadline and
// nonce words. A plain empty hookData also selects Phase A with deadline 0, nonce 0.
// The encoding is MINIMAL-WIDTH so the off-chain (zap/dexsession) and on-chain encoders
// produce byte-identical calldata for the same (deadline, nonce):
// - deadline==0 && nonce==0 -> tag only (4 bytes)
// - nonce==0 -> tag | deadline[32]
// - else -> tag | deadline[32] | nonce[32]
func EncodeOrderHookData(deadline, nonce uint64) []byte {
if deadline == 0 && nonce == 0 {
out := make([]byte, 4)
copy(out, orderPhaseTag[:])
return out
}
var d [32]byte
binary.BigEndian.PutUint64(d[24:32], deadline)
if nonce == 0 {
out := make([]byte, 0, 4+orderBodyLen)
out = append(out, orderPhaseTag[:]...)
out = append(out, d[:]...)
return out
}
var n [32]byte
binary.BigEndian.PutUint64(n[24:32], nonce)
out := make([]byte, 0, 4+orderBodyLenWithNonce)
out = append(out, orderPhaseTag[:]...)
out = append(out, d[:]...)
out = append(out, n[:]...)
return out
}
+8 -10
View File
@@ -95,7 +95,7 @@ func (s *SettleContract) runSeedSeamReserve(
defer exitCustodyKV(stateDB)
aid := assetID(asset)
// PHASE A (record) credits seamReserve BEFORE the terminal ERC-20 pull, so the seed's
// PHASE A (record) credits custody BEFORE the terminal ERC-20 pull, so the seed's
// accounting is never dropped by the geth nested-call host bug.
delivered, derr := receiveOperatorValue(stateDB, caller, asset, aid, amount, func(amt *big.Int) error {
storeCustody(stateDB, aid, new(big.Int).Add(loadCustody(stateDB, aid), amt))
@@ -110,8 +110,8 @@ func (s *SettleContract) runSeedSeamReserve(
}
// runCreditPositionFee credits an LP position's earned fees: it receives operator
// value into the vault, adds it to committedPositions[asset] (the LP-rail pot backing
// the collect), raises the NAMED position record's LockedAmt and the owner's per-asset
// value into the vault, adds it to custody[asset] (the pot backing the collect),
// raises the NAMED position record's LockedAmt and the owner's per-asset
// committed reserve by the same amount. So a maker's withdrawable rises with the fees
// the D-Chain credited them, the per-owner committed bound stays exact (a collect can
// still only pull up to THIS owner's record), and conservation holds (the fee credit
@@ -160,8 +160,7 @@ func (s *SettleContract) runCreditPositionFee(
return nil, gasLeft, ErrFeeNoPosition // fees credit the position's own locked asset.
}
// PHASE A (record): back the collect BEFORE the terminal ERC-20 pull — the pot, the
// record's withdrawable, and the owner reserve all rise by the credited fee, keeping
// committedPositions == Σ live records' LockedAmt and the per-owner bound exact. Recording
// record's withdrawable, and the owner reserve all rise by the credited fee. Recording
// before the transfer is the geth nested-call fund-loss fix (the prior order dropped these
// writes for an ERC-20 fee credit).
delivered, derr := receiveOperatorValue(stateDB, caller, asset, aid, amount, func(amt *big.Int) error {
@@ -184,7 +183,7 @@ func (s *SettleContract) runCreditPositionFee(
// the target pot via `record` using the NATIVE-ZAP two-phase discipline (the geth nested-call
// fund-loss fix): the pot write (Phase A, via `record`) happens BEFORE the ERC-20 transferFrom
// (Phase B, terminal), so the host bug cannot drop the seed's accounting (which it did — the
// ERC-20 seed moved the token but lost the seamReserve/committedPositions credit => a stranded,
// ERC-20 seed moved the token but lost the custody credit => a stranded,
// liveness-ineffective seed).
//
// - NATIVE: observed-delta read (the host frame already moved msg.value into 0x9999; delivered
@@ -200,13 +199,12 @@ func receiveOperatorValue(stateDB StateDB, caller common.Address, asset Currency
if _, of := uint256.FromBig(amount); of {
return nil, ErrSeedBadAmount
}
// delivered = realBal − (settleVault + seamReserve + committedPositions): the native
// this call carried, since every native-moving path keeps the three pots in lock-step
// with the vault's real native balance.
// delivered = realBal − (settleVault + custody): the native this call carried,
// since every native-moving path keeps the pots in lock-step with the vault's
// real native balance.
realBal := stateDB.GetBalance(poolManagerAddr9999).ToBig()
tracked := new(big.Int).Set(loadSettleVault(stateDB, aid))
tracked.Add(tracked, loadCustody(stateDB, aid))
tracked.Add(tracked, loadCustody(stateDB, aid))
delivered := new(big.Int).Sub(realBal, tracked)
if delivered.Sign() < 0 || delivered.Cmp(amount) < 0 {
return nil, ErrSeedUndelivered