diff --git a/CHANGELOG b/CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,12 @@
 # Changelog
 
+- 0.5.10 (2026-11-07)
+  * Bumps the ppad-fixed dependency to 0.2.2, which fixes its parity
+    check on Montgomery values. 'parse_point' used that check when
+    lifting a 32-byte BIP340 public key, and so returned the point of
+    odd y, rather than even, about half the time. Schnorr verification
+    was unaffected, as it chooses the even-y point itself.
+
 - 0.5.9 (2026-10-31)
   * Bumps the ppad-fixed dependency to 0.2.1, which under the llvm
     flag on x86_64 compiles ppad-fixed with LLVM's x86 cmov converter
diff --git a/ppad-secp256k1.cabal b/ppad-secp256k1.cabal
--- a/ppad-secp256k1.cabal
+++ b/ppad-secp256k1.cabal
@@ -1,6 +1,6 @@
 cabal-version:      3.0
 name:               ppad-secp256k1
-version:            0.5.9
+version:            0.5.10
 synopsis:           Schnorr signatures, ECDSA, and ECDH on the elliptic curve
                     secp256k1
 license:            MIT
@@ -44,7 +44,7 @@
     , bytestring >= 0.9 && < 0.13
     , ppad-hmac-drbg >= 0.3.1 && < 0.4
     , ppad-sha256 >= 0.3.2 && < 0.4
-    , ppad-fixed >= 0.2.1 && < 0.3
+    , ppad-fixed >= 0.2.2 && < 0.3
     , primitive >= 0.8 && < 0.10
 
 test-suite secp256k1-tests
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -142,7 +142,32 @@
   , parse_point_test_q
   , parse_point_test_r
   , noncanonical_tests
+  , xonly_tests
   ]
+
+-- BIP340's lift_x gives the point of even y. Its parity is that of y,
+-- not of y's Montgomery form, which differs about half the time, as it
+-- does for xo_hex.
+xonly_tests :: TestTree
+xonly_tests = testGroup "32-byte x-only keys have even y" [
+    testCase (render xo_hex) $
+      assertEqual mempty (Just ("02" <> xo_hex))
+        (fmap (B16.encode . serialize_point)
+          (parse_point (decodeLenient xo_hex)))
+  , testCase "x(G) to x(64G)" $
+      mapM_ even_y [1 .. 64 :: Int]
+  ]
+  where
+    even_y k = case parse_int256 (BS.replicate 31 0 <> BS.singleton (fi k))
+                      >>= derive_pub of
+      Nothing  -> assertFailure "bad key"
+      Just pub ->
+        let x = BS.drop 1 (serialize_point pub)
+        in  assertEqual (show k) (Just (BS.cons 0x02 x))
+              (fmap serialize_point (parse_point x))
+
+xo_hex :: BS.ByteString
+xo_hex = "989c0b76cb563971fdc9bef31ec06c3560f3249d6ee9e5d83c57625596e05f6f"
 
 -- The field prime is within 2 ^ 32 of 2 ^ 256, so a coordinate in
 -- [p, 2 ^ 256) reduces to a small one, giving a second encoding of a
