mirror of
https://github.com/luxfi/precompile.git
synced 2026-09-24 00:26:21 +00:00
precompile/dex: EVM-path 0x9010 fixes — StateDB-authoritative pool reads + modifyLiquidity idempotency
getPool/getPoolState now read the StateDB sqrtPrice slot FIRST (revert-safe authority) and drop any in-memory cache entry StateDB contradicts — fixes a speculative-exec cache poisoning where a reverted exec left a stale 'pool already initialized' entry that reverted re-execs (the EVM runs a tx ~5x; only canonical exec commits). modifyLiquidity now replay-idempotent via modifyBindKey (mirrors swapBindKey). cmd/evme2e = the V4-calldata EVM e2e driver. Proven live: real C-Chain tx → 0x9010 → ZAP → D-Chain → fills → native LUX settle.
This commit is contained in:
@@ -0,0 +1,257 @@
|
||||
// Command evme2e is the PATH-A (EVM 0x9010) e2e driver: it sends REAL signed
|
||||
// C-Chain transactions to the V4 PoolManager precompile at
|
||||
// 0x0000000000000000000000000000000000009010 and proves the call ROUTES through
|
||||
// NewZAPEngine -> the venue D-Chain -> consensus fills -> a V4 BalanceDelta +
|
||||
// native settlement.
|
||||
//
|
||||
// It encodes the three V4 calls with the precompile's own ABI encoders
|
||||
// (dex.EncodePoolKeyABI etc. — byte-identical to what the on-chain decoder
|
||||
// expects):
|
||||
//
|
||||
// initialize((c0,c1,fee,tickSpacing,hooks), sqrtPriceX96) sel 0x6276CBBE
|
||||
// modifyLiquidity(key,(tickLower,tickUpper,liqDelta,salt),hookData) sel 0x5A6BCFDA
|
||||
// swap(key,(zeroForOne,amountSpecified,sqrtPriceLimitX96),hookData) sel 0xF3CD914C
|
||||
//
|
||||
// It also opens a direct ZAP connection to the same venue to OBSERVE the resting
|
||||
// book (clob_depth) before/after each EVM call — the routing proof: liquidity
|
||||
// the EVM precompile placed/consumed shows up on the venue under the exact
|
||||
// keccak256(PoolKey) poolId.
|
||||
//
|
||||
// Funding is a localnet genesis account derived from the well-known LightMnemonic
|
||||
// dev seed (m/44'/60'/0'/0/0) — NOT an EWOQ key.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"flag"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
dex "github.com/luxfi/precompile/dex"
|
||||
|
||||
"github.com/luxfi/crypto"
|
||||
"github.com/luxfi/geth/common"
|
||||
"github.com/luxfi/geth/core/types"
|
||||
"github.com/luxfi/geth/ethclient"
|
||||
"github.com/luxfi/rpc"
|
||||
)
|
||||
|
||||
var lxpool = common.HexToAddress("0x0000000000000000000000000000000000009010")
|
||||
|
||||
const (
|
||||
selInitialize = 0x6276CBBE
|
||||
selModifyLiquidity = 0x5A6BCFDA
|
||||
selSwap = 0xF3CD914C
|
||||
)
|
||||
|
||||
func sel(s uint32) []byte { b := make([]byte, 4); binary.BigEndian.PutUint32(b, s); return b }
|
||||
|
||||
func i256(v *big.Int) []byte {
|
||||
out := make([]byte, 32)
|
||||
if v.Sign() >= 0 {
|
||||
v.FillBytes(out)
|
||||
return out
|
||||
}
|
||||
new(big.Int).Add(new(big.Int).Lsh(big.NewInt(1), 256), v).FillBytes(out)
|
||||
return out
|
||||
}
|
||||
func u256(v *big.Int) []byte { out := make([]byte, 32); v.FillBytes(out); return out }
|
||||
func boolWord(b bool) []byte {
|
||||
out := make([]byte, 32)
|
||||
if b {
|
||||
out[31] = 1
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// sqrtPriceX96 = floor(sqrt(price) * 2^96).
|
||||
func sqrtPriceX96(price float64) *big.Int {
|
||||
q96 := new(big.Float).SetPrec(256)
|
||||
q96.SetInt(new(big.Int).Lsh(big.NewInt(1), 96))
|
||||
sp := new(big.Float).SetPrec(256).Sqrt(big.NewFloat(price))
|
||||
sp.Mul(sp, q96)
|
||||
r, _ := sp.Int(nil)
|
||||
return r
|
||||
}
|
||||
|
||||
func key(c0, c1 common.Address, fee uint32, ts int32) dex.PoolKey {
|
||||
return dex.PoolKey{
|
||||
Currency0: dex.Currency{Address: c0}, Currency1: dex.Currency{Address: c1},
|
||||
Fee: fee, TickSpacing: ts, Hooks: common.Address{},
|
||||
}
|
||||
}
|
||||
|
||||
func encInitialize(k dex.PoolKey, sqrtP *big.Int) []byte {
|
||||
return append(append(sel(selInitialize), dex.EncodePoolKeyABI(k)...), u256(sqrtP)...)
|
||||
}
|
||||
func encModifyLiquidity(k dex.PoolKey, tickLower, tickUpper int32, liqDelta *big.Int, salt [32]byte) []byte {
|
||||
out := append(sel(selModifyLiquidity), dex.EncodePoolKeyABI(k)...)
|
||||
out = append(out, i256(big.NewInt(int64(tickLower)))...)
|
||||
out = append(out, i256(big.NewInt(int64(tickUpper)))...)
|
||||
out = append(out, i256(liqDelta)...)
|
||||
out = append(out, salt[:]...)
|
||||
return out
|
||||
}
|
||||
func encSwap(k dex.PoolKey, zeroForOne bool, amt, sqrtLimit *big.Int) []byte {
|
||||
out := append(sel(selSwap), dex.EncodePoolKeyABI(k)...)
|
||||
out = append(out, boolWord(zeroForOne)...)
|
||||
out = append(out, i256(amt)...)
|
||||
out = append(out, u256(sqrtLimit)...)
|
||||
return out
|
||||
}
|
||||
|
||||
func main() {
|
||||
rpcURL := flag.String("rpc", "http://127.0.0.1:9650/ext/bc/C/rpc", "C-Chain RPC")
|
||||
venueAddr := flag.String("venue", "127.0.0.1:9099", "venue ZAP addr (for book observation)")
|
||||
pkHex := flag.String("pk", "ed0c0416e953639c0ae02e313c2c73a84dd509937e64330b303342d16af7394e", "funded genesis privkey hex (LightMnemonic idx0)")
|
||||
flag.Parse()
|
||||
|
||||
ctx := context.Background()
|
||||
cl, err := ethclient.Dial(*rpcURL)
|
||||
must("dial", err)
|
||||
chainID, err := cl.ChainID(ctx)
|
||||
must("chainID", err)
|
||||
pk, err := crypto.HexToECDSA(*pkHex)
|
||||
must("key", err)
|
||||
from := common.BytesToAddress(crypto.PubkeyToAddress(pk.PublicKey).Bytes())
|
||||
fmt.Printf("PATH-A driver rpc=%s chainID=%s from=%s\n", *rpcURL, chainID, from.Hex())
|
||||
|
||||
// Venue observation channel (read-only clob_depth on the same poolId).
|
||||
vctx, vcancel := context.WithTimeout(ctx, 90*time.Second)
|
||||
defer vcancel()
|
||||
vconn, verr := rpc.ZAPDial(vctx, *venueAddr)
|
||||
if verr != nil {
|
||||
fmt.Printf("WARN venue dial (%s): %v — book observation disabled\n", *venueAddr, verr)
|
||||
}
|
||||
|
||||
c0 := common.Address{} // native LUX
|
||||
c1 := common.HexToAddress("0x0000000000000000000000000000000000000042") // currency1
|
||||
const fee = uint32(3000)
|
||||
const ts = int32(60)
|
||||
k := key(c0, c1, fee, ts)
|
||||
poolID := k.ID()
|
||||
fmt.Printf("pool c0=%s c1=%s fee=%d tickSpacing=%d\npoolID(keccak256(PoolKey))=0x%s\n",
|
||||
c0.Hex(), c1.Hex(), fee, ts, hex.EncodeToString(poolID[:]))
|
||||
|
||||
depth := func(tag string) {
|
||||
if vconn == nil {
|
||||
return
|
||||
}
|
||||
r, e := vconn.Call(vctx, "clob_depth", poolID[:])
|
||||
if e != nil || len(r) < 29 {
|
||||
fmt.Printf(" VENUE %-12s depth err: %v\n", tag, e)
|
||||
return
|
||||
}
|
||||
fmt.Printf(" VENUE %-12s orders=%d remaining=%.3f bestBid=%.4f bestAsk=%.4f found=%v\n",
|
||||
tag, binary.BigEndian.Uint32(r[0:4]), math.Float64frombits(binary.BigEndian.Uint64(r[4:12])),
|
||||
math.Float64frombits(binary.BigEndian.Uint64(r[12:20])), math.Float64frombits(binary.BigEndian.Uint64(r[20:28])), r[28] == 1)
|
||||
}
|
||||
|
||||
send := func(label string, data []byte) *types.Receipt {
|
||||
nonce, e := cl.PendingNonceAt(ctx, from)
|
||||
must(label+" nonce", e)
|
||||
gp, e := cl.SuggestGasPrice(ctx)
|
||||
must(label+" gasprice", e)
|
||||
tx := types.NewTx(&types.LegacyTx{Nonce: nonce, GasPrice: gp, Gas: 5_000_000, To: &lxpool, Value: big.NewInt(0), Data: data})
|
||||
signed, e := types.SignTx(tx, types.LatestSignerForChainID(chainID), pk)
|
||||
must(label+" sign", e)
|
||||
if e := cl.SendTransaction(ctx, signed); e != nil {
|
||||
fmt.Printf(" %-16s SEND ERROR: %v\n", label, e)
|
||||
return nil
|
||||
}
|
||||
h := signed.Hash()
|
||||
r := waitReceipt(ctx, cl, h)
|
||||
if r == nil {
|
||||
fmt.Printf(" %-16s tx=%s NO RECEIPT\n", label, h.Hex())
|
||||
return nil
|
||||
}
|
||||
st := "SUCCESS"
|
||||
if r.Status == 0 {
|
||||
st = "REVERT"
|
||||
}
|
||||
fmt.Printf(" %-16s tx=%s status=%d(%s) block=%d gasUsed=%d\n", label, h.Hex(), r.Status, st, r.BlockNumber.Uint64(), r.GasUsed)
|
||||
return r
|
||||
}
|
||||
|
||||
bal := func(tag string) {
|
||||
b, _ := cl.BalanceAt(ctx, from, nil)
|
||||
pm, _ := cl.BalanceAt(ctx, lxpool, nil)
|
||||
fmt.Printf(" BAL %-12s from=%s poolMgr=%s\n", tag, b.String(), pm.String())
|
||||
}
|
||||
|
||||
// ---- STEP 1: initialize price=100 -> clob_ensure_market on venue ----
|
||||
fmt.Println("\n--- STEP 1: initialize(price=100) -> clob_ensure_market ---")
|
||||
depth("pre-init")
|
||||
send("initialize", encInitialize(k, sqrtPriceX96(100)))
|
||||
depth("post-init")
|
||||
|
||||
// ---- STEP 2: modifyLiquidity (+) -> clob_place a resting ASK ----
|
||||
// init price=100 (tick 46054). Place an ASK just above: tick 46554 (~price 105).
|
||||
// An ask funds base=currency0=native LUX (clean settlement).
|
||||
fmt.Println("\n--- STEP 2: modifyLiquidity(+10 @ ask~105) -> clob_place ---")
|
||||
bal("pre-place")
|
||||
var salt [32]byte
|
||||
salt[31] = 1
|
||||
send("modifyLiq-ask", encModifyLiquidity(k, 46500, 46560, big.NewInt(10), salt))
|
||||
bal("post-place")
|
||||
depth("post-place")
|
||||
|
||||
// ---- STEP 3: swap marketable SELL crossing... need a BID to cross a sell ----
|
||||
// Place a resting BID just below init: tick 45600 (~price 96), funds quote=c1.
|
||||
// (LP needs c1 for the bid; if c1 settlement is unfunded this place may revert —
|
||||
// reported honestly. The ASK above is the native-clean leg.)
|
||||
fmt.Println("\n--- STEP 3: modifyLiquidity(+8 @ bid~96) -> clob_place ---")
|
||||
var salt2 [32]byte
|
||||
salt2[31] = 2
|
||||
send("modifyLiq-bid", encModifyLiquidity(k, 45540, 45600, big.NewInt(8), salt2))
|
||||
depth("post-place-bid")
|
||||
|
||||
// ---- STEP 4: swap marketable BUY (cross the ASK) -> clob_submit -> fills ----
|
||||
// !zeroForOne buy: taker RECEIVES base=native (credit, clean), OWES quote=c1.
|
||||
fmt.Println("\n--- STEP 4: swap BUY 5 (cross ask) -> clob_submit -> fills+BalanceDelta ---")
|
||||
bal("pre-swap-buy")
|
||||
depth("pre-swap-buy")
|
||||
maxSqrt := new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 160), big.NewInt(1))
|
||||
send("swap-buy", encSwap(k, false, big.NewInt(-5), maxSqrt))
|
||||
bal("post-swap-buy")
|
||||
depth("post-swap-buy")
|
||||
|
||||
// ---- STEP 5: swap marketable SELL (cross the BID) -> owed leg = native ----
|
||||
// zeroForOne sell: taker OWES base=native (clean SubBalance), RECEIVES quote=c1.
|
||||
fmt.Println("\n--- STEP 5: swap SELL 4 (cross bid) -> clob_submit -> fills (native owed) ---")
|
||||
bal("pre-swap-sell")
|
||||
depth("pre-swap-sell")
|
||||
minSqrt := big.NewInt(1)
|
||||
send("swap-sell", encSwap(k, true, big.NewInt(-4), minSqrt))
|
||||
bal("post-swap-sell")
|
||||
depth("post-swap-sell")
|
||||
|
||||
fmt.Println("\nPATH-A complete. VENUE depth deltas under poolID = routing proof;")
|
||||
fmt.Println("BAL deltas (native currency0) = on-chain settlement proof.")
|
||||
if vconn != nil {
|
||||
_ = vconn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func waitReceipt(ctx context.Context, cl *ethclient.Client, h common.Hash) *types.Receipt {
|
||||
dl := time.Now().Add(20 * time.Second)
|
||||
for time.Now().Before(dl) {
|
||||
r, err := cl.TransactionReceipt(ctx, h)
|
||||
if err == nil && r != nil {
|
||||
return r
|
||||
}
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func must(stage string, err error) {
|
||||
if err != nil {
|
||||
fmt.Printf("FATAL %s: %v\n", stage, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
+117
-5
@@ -43,6 +43,7 @@ var (
|
||||
pauseStatePrefix = []byte("paus")
|
||||
freezeStatePrefix = []byte("frzn")
|
||||
swapBindPrefix = []byte("swpb")
|
||||
modBindPrefix = []byte("modb")
|
||||
)
|
||||
|
||||
// PoolState extends the basic Pool with V4 tick-level state for concentrated
|
||||
@@ -219,6 +220,78 @@ func slotToSigned(h common.Hash) *big.Int {
|
||||
return v
|
||||
}
|
||||
|
||||
// modifyBindKey is the ModifyLiquidity analog of swapBindKey (RED H1, same
|
||||
// reasoning): a place/cancel is an IRREVERSIBLE clob_place/clob_cancel on the
|
||||
// d-chain book, so the EVM's repeated executions of ONE modifyLiquidity tx
|
||||
// (gas-estimate, mempool validate, block build, block verify) must issue the
|
||||
// venue op EXACTLY ONCE. Without this, exec #1 rests the order on the venue and
|
||||
// every re-exec gets "order rejected" off the now-occupied book, reverting the
|
||||
// tx while the venue keeps the order — the C↔D split the swap path already
|
||||
// closes. The key is the consensus-deterministic tuple (txHash, poolId,
|
||||
// modifyParams): same on every validator, durable across restart, revert-safe
|
||||
// (StateDB snapshot). ok=false for a non-EVM caller (no tx identity) — that
|
||||
// caller skips the guard rather than colliding on the zero slot.
|
||||
func modifyBindKey(stateDB StateDB, poolId [32]byte, params ModifyLiquidityParams) (common.Hash, bool) {
|
||||
idr, ok := stateDB.(txIdentified)
|
||||
if !ok {
|
||||
return common.Hash{}, false
|
||||
}
|
||||
txHash := idr.TxHash()
|
||||
if txHash == (common.Hash{}) {
|
||||
return common.Hash{}, false
|
||||
}
|
||||
h := blake3.New()
|
||||
h.Write(txHash[:])
|
||||
h.Write(poolId[:])
|
||||
h.Write([]byte(modifyParamsDigest(params)))
|
||||
var id [32]byte
|
||||
h.Digest().Read(id[:])
|
||||
return makeStorageKey(modBindPrefix, id[:]), true
|
||||
}
|
||||
|
||||
// modifyParamsDigest serializes the position parameters that distinguish two
|
||||
// modifyLiquidity calls issued by the SAME tx against the SAME pool (tick range,
|
||||
// signed liquidity delta, salt) so each gets its own durable slot.
|
||||
func modifyParamsDigest(p ModifyLiquidityParams) string {
|
||||
var b [4 + 4 + 1 + 32 + 32]byte
|
||||
binary.BigEndian.PutUint32(b[0:4], uint32(p.TickLower))
|
||||
binary.BigEndian.PutUint32(b[4:8], uint32(p.TickUpper))
|
||||
if p.LiquidityDelta != nil && p.LiquidityDelta.Sign() < 0 {
|
||||
b[8] = 1
|
||||
}
|
||||
if p.LiquidityDelta != nil {
|
||||
new(big.Int).Abs(p.LiquidityDelta).FillBytes(b[9:41])
|
||||
}
|
||||
copy(b[41:73], p.Salt[:])
|
||||
return string(b[:])
|
||||
}
|
||||
|
||||
// loadModifyBinding / storeModifyBinding mirror loadSwapBinding / storeSwapBinding
|
||||
// (same three-slot flag+amount0+amount1 layout, reusing deriveSlot's suffix
|
||||
// scheme under modBindPrefix). A bound modifyLiquidity returns its recorded
|
||||
// callerDelta verbatim on re-execution WITHOUT a second clob_place/clob_cancel.
|
||||
func loadModifyBinding(stateDB StateDB, bindKey common.Hash) (delta BalanceDelta, settled bool) {
|
||||
flag := stateDB.GetState(poolManagerAddr, deriveModSlot(bindKey, swapBindFlagSuffix))
|
||||
if flag[31] != 1 {
|
||||
return ZeroBalanceDelta(), false
|
||||
}
|
||||
a0 := stateDB.GetState(poolManagerAddr, deriveModSlot(bindKey, swapBindAmt0Suffix))
|
||||
a1 := stateDB.GetState(poolManagerAddr, deriveModSlot(bindKey, swapBindAmt1Suffix))
|
||||
return NewBalanceDelta(slotToSigned(a0), slotToSigned(a1)), true
|
||||
}
|
||||
|
||||
func storeModifyBinding(stateDB StateDB, bindKey common.Hash, delta BalanceDelta) {
|
||||
stateDB.SetState(poolManagerAddr, deriveModSlot(bindKey, swapBindAmt0Suffix), signedToSlot(delta.Amount0))
|
||||
stateDB.SetState(poolManagerAddr, deriveModSlot(bindKey, swapBindAmt1Suffix), signedToSlot(delta.Amount1))
|
||||
var flag common.Hash
|
||||
flag[31] = 1
|
||||
stateDB.SetState(poolManagerAddr, deriveModSlot(bindKey, swapBindFlagSuffix), flag)
|
||||
}
|
||||
|
||||
func deriveModSlot(base common.Hash, suffix []byte) common.Hash {
|
||||
return makeStorageKey(modBindPrefix, append(base[:], suffix...))
|
||||
}
|
||||
|
||||
// getOrCreateTick returns the TickInfo for a tick, creating it if absent.
|
||||
func (ps *PoolState) getOrCreateTick(tick int32) *TickInfo {
|
||||
ti, ok := ps.Ticks[tick]
|
||||
@@ -275,8 +348,18 @@ func NewPoolManager(engine ...Engine) *PoolManager {
|
||||
// getPoolState returns the extended V4 pool state, creating one from the
|
||||
// base pool if it doesn't exist yet.
|
||||
func (pm *PoolManager) getPoolState(stateDB StateDB, poolId [32]byte, tickSpacing int32, lpFee uint32) *PoolState {
|
||||
// Same revert-safety rule as getPool: the poolStates cache survives the EVM's
|
||||
// repeated executions of one tx, so a cached PoolState from a speculative
|
||||
// (later reverted) Initialize must NOT mask the StateDB truth. Gate the cache
|
||||
// on the pool's StateDB existence; a cached state for a pool StateDB no longer
|
||||
// has is stale and is rebuilt from the (StateDB-authoritative) getPool.
|
||||
sqrtPriceKey := makeStorageKey(poolStatePrefix, append(poolId[:], []byte("sqrtPrice")...))
|
||||
committed := stateDB.GetState(poolManagerAddr, sqrtPriceKey) != (common.Hash{})
|
||||
if ps, ok := pm.poolStates[poolId]; ok {
|
||||
return ps
|
||||
if committed {
|
||||
return ps
|
||||
}
|
||||
delete(pm.poolStates, poolId)
|
||||
}
|
||||
pool := pm.getPool(stateDB, poolId)
|
||||
ps := NewPoolState(pool, tickSpacing, lpFee)
|
||||
@@ -678,11 +761,25 @@ func (pm *PoolManager) ModifyLiquidity(
|
||||
}
|
||||
}
|
||||
|
||||
// Replay-idempotency (RED H1), mirroring Swap: a place/cancel is an
|
||||
// irreversible venue op, so the EVM's repeated executions of one tx must issue
|
||||
// it exactly once. A bound modify returns its recorded delta without a second
|
||||
// clob_place/clob_cancel.
|
||||
bindKey, bound := modifyBindKey(stateDB, poolId, params)
|
||||
if bound {
|
||||
if prior, settled := loadModifyBinding(stateDB, bindKey); settled {
|
||||
return prior, ZeroBalanceDelta(), nil
|
||||
}
|
||||
}
|
||||
|
||||
pm.routePool(poolId, ps)
|
||||
callerDelta, feesAccrued, err := pm.engine.ModifyLiquidity(ps, caller, params)
|
||||
if err != nil {
|
||||
return ZeroBalanceDelta(), ZeroBalanceDelta(), err
|
||||
}
|
||||
if bound {
|
||||
storeModifyBinding(stateDB, bindKey, callerDelta)
|
||||
}
|
||||
|
||||
posKey := PositionKey(caller, params.TickLower, params.TickUpper, params.Salt)
|
||||
if pos, ok := ps.Positions[posKey]; ok {
|
||||
@@ -802,14 +899,29 @@ func (pm *PoolManager) Flash(
|
||||
// =========================================================================
|
||||
|
||||
func (pm *PoolManager) getPool(stateDB StateDB, poolId [32]byte) *Pool {
|
||||
// StateDB is the revert-safe authority for pool EXISTENCE: a pool's sqrtPrice
|
||||
// slot is zero unless an Initialize committed it, and it reverts to zero with
|
||||
// any tx that rolls back. The process-memory cache (pm.pools) does NOT roll
|
||||
// back, so trusting it first leaks state across the EVM's multiple
|
||||
// (estimate/build/verify) executions of one tx — a speculative Initialize
|
||||
// would populate the cache, then every re-execution sees "already
|
||||
// initialized" off the stale cache and reverts. So consult StateDB FIRST;
|
||||
// only serve the cached *Pool when StateDB confirms the pool still exists
|
||||
// (matches the StateDB-authoritative discipline already used for
|
||||
// pause/freeze — see isDEXPaused, RED V6). A cache entry contradicted by
|
||||
// StateDB is stale and dropped.
|
||||
sqrtPriceKey := makeStorageKey(poolStatePrefix, append(poolId[:], []byte("sqrtPrice")...))
|
||||
sqrtPriceHash := stateDB.GetState(poolManagerAddr, sqrtPriceKey)
|
||||
committed := sqrtPriceHash != (common.Hash{})
|
||||
if pool, ok := pm.pools[poolId]; ok {
|
||||
return pool
|
||||
if committed {
|
||||
return pool
|
||||
}
|
||||
delete(pm.pools, poolId)
|
||||
}
|
||||
pool := NewPool()
|
||||
|
||||
sqrtPriceKey := makeStorageKey(poolStatePrefix, append(poolId[:], []byte("sqrtPrice")...))
|
||||
sqrtPriceHash := stateDB.GetState(poolManagerAddr, sqrtPriceKey)
|
||||
if sqrtPriceHash != (common.Hash{}) {
|
||||
if committed {
|
||||
pool.SqrtPriceX96 = new(big.Int).SetBytes(sqrtPriceHash[:])
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user