diff --git a/Bulletproofs/ArithmeticCircuit/Internal.hs b/Bulletproofs/ArithmeticCircuit/Internal.hs
--- a/Bulletproofs/ArithmeticCircuit/Internal.hs
+++ b/Bulletproofs/ArithmeticCircuit/Internal.hs
@@ -7,6 +7,8 @@
 import Data.List (head)
 import qualified Data.List as List
 import qualified Data.Map as Map
+import Test.QuickCheck
+import PrimeField (PrimeField(..), toInt)
 
 import System.Random.Shuffle (shuffleM)
 import qualified Crypto.Random.Types as Crypto (MonadRandom(..))
@@ -103,10 +105,10 @@
     aRNew = padToNearestPowerOfTwo aR
     aONew = padToNearestPowerOfTwo aO
 
-delta :: (Eq f, Field f) => Integer -> f -> [f] -> [f] -> f
+delta :: (KnownNat p) => Integer -> PrimeField p -> [PrimeField p] -> [PrimeField p] -> PrimeField p
 delta n y zwL zwR= (powerVector (recip y) n `hadamardp` zwR) `dot` zwL
 
-commitBitVector :: (AsInteger f) => f -> [f] -> [f] -> Crypto.Point
+commitBitVector :: (KnownNat p) => PrimeField p -> [PrimeField p] -> [PrimeField p] -> Crypto.Point
 commitBitVector vBlinding vL vR = vLG `addP` vRH `addP` vBlindingH
   where
     vBlindingH = vBlinding `mulP` h
@@ -115,13 +117,13 @@
 
 shamirGxGxG :: (Show f, Num f) => Crypto.Point -> Crypto.Point -> Crypto.Point -> f
 shamirGxGxG p1 p2 p3
-  = fromInteger $ oracle $ show q <> pointToBS p1 <> pointToBS p2 <> pointToBS p3
+  = fromInteger $ oracle $ show _q <> pointToBS p1 <> pointToBS p2 <> pointToBS p3
 
 shamirGs :: (Show f, Num f) => [Crypto.Point] -> f
-shamirGs ps = fromInteger $ oracle $ show q <> foldMap pointToBS ps
+shamirGs ps = fromInteger $ oracle $ show _q <> foldMap pointToBS ps
 
 shamirZ :: (Show f, Num f) => f -> f
-shamirZ z = fromInteger $ oracle $ show q <> show z
+shamirZ z = fromInteger $ oracle $ show _q <> show z
 
 ---------------------------------------------
 -- Polynomials
@@ -180,30 +182,7 @@
 genZeroMatrix :: (Num f) => Integer -> Integer -> [[f]]
 genZeroMatrix (fromIntegral -> n) (fromIntegral -> m) = replicate n (replicate m 0)
 
-generateWv :: (Num f, MonadRandom m) => Integer -> Integer -> m [[f]]
-generateWv lConstraints m
-  | lConstraints < m = panic "Number of constraints must be bigger than m"
-  | otherwise = shuffleM (genIdenMatrix m ++ genZeroMatrix (lConstraints - m) m)
-
-generateGateWeights :: (Crypto.MonadRandom m, Num f) => Integer -> Integer -> m (GateWeights f)
-generateGateWeights lConstraints n = do
-  let genVec = ((\i -> insertAt (fromIntegral i) (oneVector n) (replicate (fromIntegral lConstraints - 1) (zeroVector n))) <$> generateMax (fromIntegral lConstraints))
-  wL <- genVec
-  wR <- genVec
-  wO <- genVec
-  pure $ GateWeights wL wR wO
-  where
-    zeroVector x = replicate (fromIntegral x) 0
-    oneVector x = replicate (fromIntegral x) 1
-
-generateRandomAssignment :: forall f m . (Num f, AsInteger f, Crypto.MonadRandom m) => Integer -> m (Assignment f)
-generateRandomAssignment n = do
-  aL <- replicateM (fromIntegral n) ((fromInteger :: Integer -> f) <$> generateMax (2^n))
-  aR <- replicateM (fromIntegral n) ((fromInteger :: Integer -> f) <$> generateMax (2^n))
-  let aO = aL `hadamardp` aR
-  pure $ Assignment aL aR aO
-
-computeInputValues :: (Field f, Eq f) => GateWeights f -> [[f]] -> Assignment f -> [f] -> [f]
+computeInputValues :: (KnownNat p) => GateWeights (PrimeField p) -> [[PrimeField p]] -> Assignment (PrimeField p) -> [PrimeField p] -> [PrimeField p]
 computeInputValues GateWeights{..} wV Assignment{..} cs
   = solveLinearSystem $ zipWith (\row s -> reverse $ s : row) wV solutions
   where
@@ -212,7 +191,7 @@
         ^+^ vectorMatrixProductT aO wO
         ^-^ cs
 
-gaussianReduce :: (Field f, Eq f) => [[f]] -> [[f]]
+gaussianReduce :: (KnownNat p) => [[PrimeField p]] -> [[PrimeField p]]
 gaussianReduce matrix = fixlastrow $ foldl reduceRow matrix [0..length matrix-1]
   where
     -- Swaps element at position a with element at position b.
@@ -247,7 +226,7 @@
         nz = List.last (List.init row)
 
 -- Solve a matrix (must already be in REF form) by back substitution.
-substituteMatrix :: (Field f, Eq f) => [[f]] -> [f]
+substituteMatrix :: (KnownNat p) => [[PrimeField p]] -> [PrimeField p]
 substituteMatrix matrix = foldr next [List.last (List.last matrix)] (List.init matrix)
   where
     next row found = let
@@ -255,5 +234,72 @@
       solution = List.last row - sum (zipWith (*) found subpart)
       in solution : found
 
-solveLinearSystem :: (Field f, Eq f) => [[f]] -> [f]
+solveLinearSystem :: (KnownNat p) => [[PrimeField p]] -> [PrimeField p]
 solveLinearSystem = reverse . substituteMatrix . gaussianReduce
+
+-------------------------
+-- Arbitrary instances --
+-------------------------
+
+instance (KnownNat p) => Arbitrary (ArithCircuit (PrimeField p)) where
+  arbitrary = do
+    n <- choose (1, 100)
+    m <- choose (1, n)
+    arithCircuitGen n m
+
+arithCircuitGen :: forall p. (KnownNat p) => Integer -> Integer -> Gen (ArithCircuit (PrimeField p))
+arithCircuitGen n m = do
+    -- TODO: Can lConstraints be a different value?
+    let lConstraints = m
+
+    cs <- vectorOf (fromIntegral m) arbitrary
+
+    weights@GateWeights{..} <- gateWeightsGen lConstraints n
+    let gateWeights = GateWeights wL wR wO
+
+    commitmentWeights <- wvGen lConstraints m
+    pure $ ArithCircuit gateWeights commitmentWeights cs
+      where
+        gateWeightsGen :: Integer -> Integer -> Gen (GateWeights (PrimeField p))
+        gateWeightsGen lConstraints n = do
+          let genVec = ((\i -> insertAt i (oneVector n) (replicate (fromIntegral lConstraints - 1) (zeroVector n))) <$> choose (0, fromIntegral lConstraints))
+          wL <- genVec
+          wR <- genVec
+          wO <- genVec
+          pure $ GateWeights wL wR wO
+
+        wvGen :: Integer -> Integer -> Gen [[PrimeField p]]
+        wvGen lConstraints m
+          | lConstraints < m = panic "Number of constraints must be bigger than m"
+          | otherwise = shuffle (genIdenMatrix m ++ genZeroMatrix (lConstraints - m) m)
+        zeroVector x = replicate (fromIntegral x) 0
+        oneVector x = replicate (fromIntegral x) 1
+
+
+instance (KnownNat p) => Arbitrary (Assignment (PrimeField p)) where
+  arbitrary = do
+    n <- (arbitrary :: Gen Integer)
+    arithAssignmentGen n
+
+arithAssignmentGen :: (KnownNat p) => Integer -> Gen (Assignment (PrimeField p))
+arithAssignmentGen n = do
+    aL <- vectorOf (fromIntegral n) (fromInteger <$> choose (0, 2^n))
+    aR <- vectorOf (fromIntegral n) (fromInteger <$> choose (0, 2^n))
+    let aO = aL `hadamardp` aR
+    pure $ Assignment aL aR aO
+
+instance (KnownNat p) => Arbitrary (ArithWitness (PrimeField p)) where
+  arbitrary = do
+    n <- choose (1, 100)
+    m <- choose (1, n)
+    arithCircuit <- arithCircuitGen n m
+    assignment <- arithAssignmentGen n
+    arithWitnessGen assignment arithCircuit m
+
+arithWitnessGen :: (KnownNat p) => Assignment (PrimeField p) -> ArithCircuit (PrimeField p) -> Integer -> Gen (ArithWitness (PrimeField p))
+arithWitnessGen assignment arith@ArithCircuit{..} m = do
+  commitBlinders <- vectorOf (fromIntegral m) arbitrary
+  let vs = computeInputValues weights commitmentWeights assignment cs
+      commitments = zipWith commit vs commitBlinders
+  pure $ ArithWitness assignment commitments commitBlinders
+
diff --git a/Bulletproofs/ArithmeticCircuit/Prover.hs b/Bulletproofs/ArithmeticCircuit/Prover.hs
--- a/Bulletproofs/ArithmeticCircuit/Prover.hs
+++ b/Bulletproofs/ArithmeticCircuit/Prover.hs
@@ -7,6 +7,7 @@
 import Crypto.Number.Generate (generateMax)
 import qualified Crypto.PubKey.ECC.Prim as Crypto
 import qualified Crypto.PubKey.ECC.Types as Crypto
+import PrimeField (PrimeField(..), toInt)
 
 import Bulletproofs.Curve
 import Bulletproofs.Utils hiding (shamirZ)
@@ -16,15 +17,15 @@
 -- | Generate a zero-knowledge proof of computation
 -- for an arithmetic circuit with a valid witness
 generateProof
-  :: forall f m
-   . (MonadRandom m, AsInteger f, Field f, Show f, Eq f)
-  => ArithCircuit f
-  -> ArithWitness f
-  -> m (ArithCircuitProof f)
+  :: forall p m
+   . (MonadRandom m, KnownNat p)
+  => ArithCircuit (PrimeField p)
+  -> ArithWitness (PrimeField p)
+  -> m (ArithCircuitProof (PrimeField p))
 generateProof (padCircuit -> ArithCircuit{..}) ArithWitness{..} = do
   let GateWeights{..} = weights
       Assignment{..} = padAssignment assignment
-      genBlinding = (fromInteger :: Integer -> f) <$> generateMax q
+      genBlinding = (fromInteger :: Integer -> PrimeField p) <$> generateMax _q
   aiBlinding <- genBlinding
   aoBlinding <- genBlinding
   sBlinding <- genBlinding
@@ -57,7 +58,7 @@
          + (zs `dot` w)
          + delta n y zwL zwR
 
-  tBlindings <- insertAt 2 0 . (:) 0 <$> replicateM 5 ((fromInteger :: Integer -> f) <$> generateMax q)
+  tBlindings <- insertAt 2 0 . (:) 0 <$> replicateM 5 ((fromInteger :: Integer -> PrimeField p) <$> generateMax _q)
   let tCommits = zipWith commit tPoly tBlindings
 
   let x = shamirGs tCommits
@@ -70,9 +71,9 @@
       commitTimesWeigths = commitBlinders `vectorMatrixProductT` commitmentWeights
       zGamma = zs `dot` commitTimesWeigths
       tBlinding = sum (zipWith (\i blinding -> blinding * (x ^ i)) [0..] tBlindings)
-                + (fSquare x * zGamma)
+                + ((x ^ 2) * zGamma)
 
-      mu = aiBlinding * x + aoBlinding * fSquare x + sBlinding * (x ^ 3)
+      mu = aiBlinding * x + aoBlinding * (x ^ 2) + sBlinding * (x ^ 3)
 
   let uChallenge = shamirU tBlinding mu t
       u = uChallenge `mulP` g
@@ -80,7 +81,7 @@
       gExp = (*) x <$> (powerVector (recip y) n `hadamardp` zwR)
       hExp = (((*) x <$> zwL) ^+^ zwO) ^-^ ys
       commitmentLR = (x `mulP` aiCommit)
-                   `addP` (fSquare x `mulP` aoCommit)
+                   `addP` ((x ^ 2) `mulP` aoCommit)
                    `addP` ((x ^ 3)`mulP` sCommit)
                    `addP` sumExps gExp gs
                    `addP` sumExps hExp hs'
diff --git a/Bulletproofs/ArithmeticCircuit/Verifier.hs b/Bulletproofs/ArithmeticCircuit/Verifier.hs
--- a/Bulletproofs/ArithmeticCircuit/Verifier.hs
+++ b/Bulletproofs/ArithmeticCircuit/Verifier.hs
@@ -6,6 +6,7 @@
 
 import qualified Crypto.PubKey.ECC.Prim as Crypto
 import qualified Crypto.PubKey.ECC.Types as Crypto
+import PrimeField (PrimeField(..), toInt)
 
 import Bulletproofs.Curve
 import Bulletproofs.Utils hiding (shamirZ)
@@ -17,10 +18,10 @@
 -- | Verify that a zero-knowledge proof holds
 -- for an arithmetic circuit given committed input values
 verifyProof
-  :: (AsInteger f, Field f, Eq f, Show f)
+  :: (KnownNat p)
   => [Crypto.Point]
-  -> ArithCircuitProof f
-  -> ArithCircuit f
+  -> ArithCircuitProof (PrimeField p)
+  -> ArithCircuit (PrimeField p)
   -> Bool
 verifyProof vCommits proof@ArithCircuitProof{..} (padCircuit -> ArithCircuit{..})
   = verifyLRCommitment && verifyTPoly
@@ -55,9 +56,9 @@
         rhs = (gExp `mulP` g)
             `addP` tCommitsExpSum
             `addP` sumExps vExp vCommits
-        gExp = fSquare x * (k + cQ)
+        gExp = (x ^ 2) * (k + cQ)
         cQ = zs `dot` cs
-        vExp = (*) (fSquare x) <$> (zs `vectorMatrixProduct` commitmentWeights)
+        vExp = (*) (x ^ 2) <$> (zs `vectorMatrixProduct` commitmentWeights)
         k = delta n y zwL zwR
         xs = 0 : x : 0 : (((^) x) <$> [3..6])
         tCommitsExpSum = sumExps xs tCommits
@@ -72,7 +73,7 @@
         gExp = (*) x <$> (powerVector (recip y) n `hadamardp` zwR)
         hExp = (((*) x <$> zwL) ^+^ zwO) ^-^ ys
         commitmentLR = (x `mulP` aiCommit)
-                     `addP` (fSquare x `mulP` aoCommit)
+                     `addP` ((x ^ 2) `mulP` aoCommit)
                      `addP` ((x ^ 3) `mulP` sCommit)
                      `addP` sumExps gExp gs
                      `addP` sumExps hExp hs'
diff --git a/Bulletproofs/Curve.hs b/Bulletproofs/Curve.hs
--- a/Bulletproofs/Curve.hs
+++ b/Bulletproofs/Curve.hs
@@ -1,5 +1,7 @@
 module Bulletproofs.Curve (
-  q,
+  _q,
+  _a,
+  _b,
   g,
   h,
   gs,
@@ -23,6 +25,19 @@
 import Numeric
 import qualified Data.List as L
 
+-- Implementation using the elliptic curve secp256k12
+-- which has 128 bit security.
+-- Parameters as in Cryptonite:
+-- SEC_p256k1 = CurveFP  $ CurvePrime
+--     0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f
+--     (CurveCommon
+--         { ecc_a = 0x0000000000000000000000000000000000000000000000000000000000000000
+--         , ecc_b = 0x0000000000000000000000000000000000000000000000000000000000000007
+--         , ecc_g = Point 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798
+--                         0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8
+--         , ecc_n = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
+--         , ecc_h = 1
+--         })
 curveName :: Crypto.CurveName
 curveName = Crypto.SEC_p256k1
 
@@ -30,9 +45,15 @@
 curve = Crypto.getCurveByName curveName
 
 -- | Order of the curve
-q :: Integer
-q = Crypto.ecc_n . Crypto.common_curve $ curve
+_q :: Integer
+_q = Crypto.ecc_n . Crypto.common_curve $ curve
 
+_b :: Integer
+_b = Crypto.ecc_b . Crypto.common_curve $ curve
+
+_a :: Integer
+_a = Crypto.ecc_a . Crypto.common_curve $ curve
+
 -- | Generator of the curve
 g :: Crypto.Point
 g = Crypto.ecc_g $ Crypto.common_curve curve
@@ -64,8 +85,8 @@
 pointToBS (Crypto.Point x y) = show x <> show y
 
 -- | Characteristic of the underlying finite field of the elliptic curve
-p :: Integer
-p = Crypto.ecc_p cp
+_p :: Integer
+_p = Crypto.ecc_p cp
   where
     cp = case curve of
       Crypto.CurveFP c -> c
@@ -82,6 +103,6 @@
       then Crypto.Point x y
       else generateH basePoint (toS $ '1':extra)
   where
-    x = oracle (pointToBS basePoint <> toS extra) `mod` p
-    yM = sqrtModP (x ^ 3 + 7) p
+    x = oracle (pointToBS basePoint <> toS extra) `mod` _p
+    yM = sqrtModP (x ^ 3 + 7) _p
 
diff --git a/Bulletproofs/Fq.hs b/Bulletproofs/Fq.hs
--- a/Bulletproofs/Fq.hs
+++ b/Bulletproofs/Fq.hs
@@ -1,108 +1,68 @@
-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE TypeFamilies #-}
+-- | Prime field with characteristic _q, over which the elliptic curve
+-- is defined and the other finite field extensions.
+--
+--   * Fq
+--   * Fq2 := Fq[u]/u^2 + 1
+--   * Fq6 := Fq2[v]/v^3 - (9 + u)
+--   * Fq12 := Fq6[w]/w^2 - v
 
-module Bulletproofs.Fq where
+module Bulletproofs.Fq
+  ( Fq
+  , PF
+  , fqRandom
+  , fqPow
+  , fqSqrt
+  , toInt
+  ) where
 
 import Protolude
 
 import Crypto.Random (MonadRandom)
 import Crypto.Number.Generate (generateMax)
-
+import Math.NumberTheory.Moduli.Class (powMod)
+import PrimeField (PrimeField(..), toInt)
+import Pairing.Modular
 import Bulletproofs.Curve
 
+
 -------------------------------------------------------------------------------
 -- Types
 -------------------------------------------------------------------------------
 
--- | Prime field with characteristic @_q@
-newtype Fq = Fq Integer -- ^ Use @new@ instead of this constructor
-  deriving (Show, Eq, Bits, Ord, Generic, NFData)
-
-instance Num Fq where
-  (+)           = fqAdd
-  (*)           = fqMul
-  abs           = panic "There is no absolute value in a finite field"
-  signum        = panic "This function doesn't make sense in a finite field"
-  negate        = fqNeg
-  fromInteger   = new
-
-instance Fractional Fq where
-  (/) = fqDiv
-  fromRational (a :% b) = Fq a / Fq b
-
--- | Turn an integer into an @Fq@ number, should be used instead of
--- the @Fq@ constructor.
-new :: Integer -> Fq
-new a = Fq (a `mod` q)
-
-{-# INLINE norm #-}
-norm :: Fq -> Fq
-norm (Fq a) = Fq (a `mod` q)
-
-{-# INLINE fqAdd #-}
-fqAdd :: Fq -> Fq -> Fq
-fqAdd (Fq a) (Fq b) = norm (Fq (a+b))
-
-{-# INLINE fqMul #-}
-fqMul :: Fq -> Fq -> Fq
-fqMul (Fq a) (Fq b) = norm (Fq (a*b))
-
-{-# INLINE fqNeg #-}
-fqNeg :: Fq -> Fq
-fqNeg (Fq a) = Fq ((-a) `mod` q)
-
-{-# INLINE fqDiv #-}
-fqDiv :: Fq -> Fq -> Fq
-fqDiv a b = fqMul a (inv b)
-
-{-# INLINE fqInv #-}
--- | Multiplicative inverse
-fqInv :: Fq -> Fq
-fqInv x = 1 / x
-
-{-# INLINE fqZero #-}
--- | Additive identity
-fqZero :: Fq
-fqZero = Fq 0
-
-{-# INLINE fqOne #-}
--- | Multiplicative identity
-fqOne :: Fq
-fqOne = Fq 1
+-- | Prime field @Fq@ with characteristic @_q@
+type Fq = PrimeField 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
 
-fqSquare :: Fq -> Fq
-fqSquare x = fqMul x x
+-- | Type family to extract the characteristic of the prime field
+type family PF a where
+  PF (PrimeField k) = k
 
-fqCube :: Fq -> Fq
-fqCube x = fqMul x (fqMul x x)
+-------------------------------------------------------------------------------
+-- Instances
+-------------------------------------------------------------------------------
 
-fqPower :: Fq -> Integer -> Fq
-fqPower base exp = fqPower' base exp (Fq 1)
+instance Ord Fq where
+  compare = on compare toInt
 
-fqPower' :: Fq  -> Integer -> Fq -> Fq
-fqPower' base 0 acc = acc
-fqPower' base exp acc = fqPower' base (exp - 1) (fqMul base acc)
+-------------------------------------------------------------------------------
+-- Random
+-------------------------------------------------------------------------------
 
-inv :: Fq -> Fq
-inv (Fq a) = Fq $ euclidean a q `mod` q
+fqRandom :: MonadRandom m => m Fq
+fqRandom = fromInteger <$> generateMax _q
 
-asInteger :: Fq -> Integer
-asInteger (Fq n) = n
+-------------------------------------------------------------------------------
+-- Y for X
+-------------------------------------------------------------------------------
 
--- | Euclidean algorithm to compute inverse in an integral domain @a@
-euclidean :: (Integral a) => a -> a -> a
-euclidean a b = fst (inv' a b)
+fqPow :: Integral e => Fq -> e -> Fq
+fqPow a b = fromInteger (withQ (modUnOp (toInt a) (flip powMod b)))
+{-# INLINE fqPow #-}
 
-{-# INLINEABLE inv' #-}
-{-# SPECIALISE inv' :: Integer -> Integer -> (Integer, Integer) #-}
-inv' :: (Integral a) => a -> a -> (a, a)
-inv' a b =
-  case b of
-   1 -> (0, 1)
-   _ -> let (e, f) = inv' b d
-        in (f, e - c*f)
-  where c = a `div` b
-        d = a `mod` b
+fqSqrt :: Bool -> Fq -> Maybe Fq
+fqSqrt largestY a = do
+  (y1, y2) <- withQM (modUnOpMTup (toInt a) bothSqrtOf)
+  return (fromInteger ((if largestY then max else min) y1 y2))
 
-random :: MonadRandom m => m Fq
-random = Fq <$> generateMax q
+fqYforX :: Fq -> Bool -> Maybe Fq
+fqYforX x largestY = fqSqrt largestY (x `fqPow` 3 + fromInteger _b)
diff --git a/Bulletproofs/InnerProductProof/Prover.hs b/Bulletproofs/InnerProductProof/Prover.hs
--- a/Bulletproofs/InnerProductProof/Prover.hs
+++ b/Bulletproofs/InnerProductProof/Prover.hs
@@ -11,6 +11,7 @@
 import qualified Data.Map as Map
 
 import qualified Crypto.PubKey.ECC.Types as Crypto
+import PrimeField (PrimeField(..), toInt)
 
 import Bulletproofs.Curve
 import Bulletproofs.Utils
@@ -20,25 +21,25 @@
 -- | Generate proof that a witness l, r satisfies the inner product relation
 -- on public input (Gs, Hs, h)
 generateProof
-  :: (AsInteger f, Eq f, Field f)
+  :: KnownNat p
   => InnerProductBase    -- ^ Generators Gs, Hs, h
   -> Crypto.Point
   -- ^ Commitment P = A + xS − zG + (z*y^n + z^2 * 2^n) * hs' of vectors l and r
   -- whose inner product is t
-  -> InnerProductWitness f
+  -> InnerProductWitness (PrimeField p)
   -- ^ Vectors l and r that hide bit vectors aL and aR, respectively
-  -> InnerProductProof f
+  -> InnerProductProof (PrimeField p)
 generateProof productBase commitmentLR witness
   = generateProof' productBase commitmentLR witness [] []
 
 generateProof'
-  :: (AsInteger f, Eq f, Field f)
+  :: KnownNat p
   => InnerProductBase
   -> Crypto.Point
-  -> InnerProductWitness f
+  -> InnerProductWitness (PrimeField p)
   -> [Crypto.Point]
   -> [Crypto.Point]
-  -> InnerProductProof f
+  -> InnerProductProof (PrimeField p)
 generateProof'
   InnerProductBase{ bGs, bHs, bH }
   commitmentLR
@@ -92,9 +93,9 @@
     rs' = ((*) xInv <$> rsLeft) ^+^ ((*) x <$> rsRight)
 
     commitmentLR'
-      = (fSquare x `mulP` lCommit)
+      = ((x ^ 2) `mulP` lCommit)
         `addP`
-        (fSquare xInv `mulP` rCommit)
+        ((xInv ^ 2) `mulP` rCommit)
         `addP`
         commitmentLR
 
@@ -122,23 +123,23 @@
         ==
         sumExps ls bGs
         `addP`
-        (fSquare x `mulP` aL')
+        ((x ^ 2) `mulP` aL')
         `addP`
-        (fSquare xInv `mulP` aR')
+        ((xInv ^ 2) `mulP` aR')
 
     checkRHs
       = rHs'
         ==
         sumExps rs bHs
         `addP`
-        (fSquare x `mulP` bR')
+        ((x ^ 2) `mulP` bR')
         `addP`
-        (fSquare xInv `mulP` bL')
+        ((xInv ^ 2) `mulP` bL')
 
     checkLBs
       = dot ls' rs'
         ==
-        dot ls rs + fSquare x * cL + fSquare xInv * cR
+        dot ls rs + (x ^ 2) * cL + (xInv ^ 2) * cR
 
     checkC
       = commitmentLR
diff --git a/Bulletproofs/InnerProductProof/Verifier.hs b/Bulletproofs/InnerProductProof/Verifier.hs
--- a/Bulletproofs/InnerProductProof/Verifier.hs
+++ b/Bulletproofs/InnerProductProof/Verifier.hs
@@ -10,6 +10,7 @@
 import qualified Data.Map as Map
 
 import qualified Crypto.PubKey.ECC.Types as Crypto
+import PrimeField (PrimeField(..), toInt)
 
 import Bulletproofs.Curve
 import Bulletproofs.Utils
@@ -18,11 +19,11 @@
 
 -- | Optimized non-interactive verifier using multi-exponentiation and batch verification
 verifyProof
-  :: (AsInteger f, Field f)
+  :: KnownNat p
   => Integer            -- ^ Range upper bound
   -> InnerProductBase   -- ^ Generators Gs, Hs, h
   -> Crypto.Point       -- ^ Commitment P
-  -> InnerProductProof f
+  -> InnerProductProof (PrimeField p)
   -- ^ Proof that a secret committed value lies in a certain interval
   -> Bool
 verifyProof n productBase@InnerProductBase{..} commitmentLR productProof@InnerProductProof{ l, r }
@@ -40,19 +41,23 @@
     gsCommit = sumExps otherExponents bGs
     hsCommit = sumExps (reverse otherExponents) bHs
 
-mkChallenges :: (AsInteger f, Field f) => InnerProductProof f -> Crypto.Point -> ([f], [f], Crypto.Point)
+mkChallenges
+  :: KnownNat p
+  => InnerProductProof (PrimeField p)
+  -> Crypto.Point
+  -> ([PrimeField p], [PrimeField p], Crypto.Point)
 mkChallenges InnerProductProof{ lCommits, rCommits } commitmentLR
   = foldl'
       (\(xs, xsInv, accC) (li, ri)
         -> let x = shamirX' accC li ri
                xInv = recip x
-               c = (fSquare x `mulP` li) `addP` (fSquare xInv `mulP` ri) `addP` accC
+               c = ((x ^ 2) `mulP` li) `addP` ((xInv ^ 2) `mulP` ri) `addP` accC
            in (x:xs, xInv:xsInv, c)
       )
       ([], [], commitmentLR)
       (zip lCommits rCommits)
 
-mkOtherExponents :: forall f . (AsInteger f, Field f) => Integer -> [f] -> [f]
+mkOtherExponents :: forall p . KnownNat p => Integer -> [PrimeField p] -> [PrimeField p]
 mkOtherExponents n challenges
   = Map.elems $ foldl'
       f
@@ -62,14 +67,14 @@
     n' = n `div` 2
     f acc i = foldl' (f' i) acc [0..logBase2 n-1]
 
-    f' :: Integer -> Map.Map Integer f -> Integer -> Map.Map Integer f
+    f' :: Integer -> Map.Map Integer (PrimeField p) -> Integer -> Map.Map Integer (PrimeField p)
     f' i acc' j
       = let i1 = (2^j) + i in
           if | i1 >= n -> acc'
              | Map.member i1 acc' -> acc'
              | otherwise -> Map.insert
                               i1
-                              (acc' Map.! i * fSquare (challenges L.!! fromIntegral j))
+                              (acc' Map.! i * ((challenges L.!! fromIntegral j) ^ 2))
                               acc'
 
 
diff --git a/Bulletproofs/MultiRangeProof/Prover.hs b/Bulletproofs/MultiRangeProof/Prover.hs
--- a/Bulletproofs/MultiRangeProof/Prover.hs
+++ b/Bulletproofs/MultiRangeProof/Prover.hs
@@ -12,6 +12,7 @@
 import qualified Crypto.PubKey.ECC.Generate as Crypto
 import qualified Crypto.PubKey.ECC.Prim as Crypto
 import qualified Crypto.PubKey.ECC.Types as Crypto
+import PrimeField (PrimeField(..), toInt)
 
 import Bulletproofs.Curve
 import Bulletproofs.Utils
@@ -22,13 +23,13 @@
 
 -- | Prove that a list of values lies in a specific range
 generateProof
-  :: (AsInteger f, Eq f, Field f, Show f, MonadRandom m)
+  :: (KnownNat p, MonadRandom m)
   => Integer                -- ^ Upper bound of the range we want to prove
-  -> [(Integer, Integer)]
+  -> [(PrimeField p, PrimeField p)]
   -- ^ Values we want to prove in range and their blinding factors
-  -> ExceptT RangeProofError m (RangeProof f)
+  -> ExceptT (RangeProofError (PrimeField p)) m (RangeProof (PrimeField p))
 generateProof upperBound vsAndvBlindings = do
-  unless (upperBound < q) $ throwE $ UpperBoundTooLarge upperBound
+  unless (upperBound < _q) $ throwE $ UpperBoundTooLarge upperBound
 
   case doubleLogM of
      Nothing -> throwE $ NNotPowerOf2 upperBound
@@ -52,29 +53,26 @@
 
 -- | Generate range proof from valid inputs
 generateProofUnsafe
-  :: forall f m
-   . (AsInteger f, Eq f, Field f, Show f, MonadRandom m)
+  :: forall p m
+   . (KnownNat p, MonadRandom m)
   => Integer    -- ^ Upper bound of the range we want to prove
-  -> [(Integer, Integer)]
+  -> [(PrimeField p, PrimeField p)]
   -- ^ Values we want to prove in range and their blinding factors
-  -> m (RangeProof f)
+  -> m (RangeProof (PrimeField p))
 generateProofUnsafe upperBound vsAndvBlindings = do
   let n = logBase2 upperBound
       m = fromIntegral $ length vsAndvBlindings
       nm = n * m
 
-      vsF :: [f]
-      vsF = (fromInteger . fst) <$> vsAndvBlindings
-
-      vBlindingsF :: [f]
-      vBlindingsF = (fromInteger . snd) <$> vsAndvBlindings
+      vsF = fst <$> vsAndvBlindings
+      vBlindingsF = snd <$> vsAndvBlindings
 
   let aL = reversedEncodeBitMulti n vsF
       aR = complementaryVector aL
 
   (sL, sR) <- chooseBlindingVectors nm
 
-  let genBlinding = (fromInteger :: Integer -> f) <$> generateMax q
+  let genBlinding = (fromInteger :: Integer -> (PrimeField p)) <$> generateMax _q
 
   aBlinding <- genBlinding
   sBlinding <- genBlinding
@@ -99,7 +97,7 @@
 
   let ls = l0 ^+^ ((*) x <$> l1)
       rs = r0 ^+^ ((*) x <$> r1)
-      t = t0 + (t1 * x) + (t2 * fSquare x)
+      t = t0 + (t1 * x) + (t2 * (x ^ 2))
 
   unless (t == dot ls rs) $
     panic "Error on: t = dot l r"
@@ -108,7 +106,7 @@
     panic "Error on: t1 = dot l1 r0 + dot l0 r1"
 
   let tBlinding = sum (zipWith (\vBlindingF j -> (z ^ (j + 1)) * vBlindingF) vBlindingsF [1..m])
-                + (t2Blinding * fSquare x)
+                + (t2Blinding * (x ^ 2))
                 + (t1Blinding * x)
       mu = aBlinding + (sBlinding * x)
 
@@ -141,16 +139,16 @@
 -- l(x) = (a L − z1 n ) + s L x
 -- r(x) = y^n ◦ (aR + z * 1^n + sR * x) + z^2 * 2^n
 computeLRPolys
-  :: (Eq f, Num f)
+  :: (KnownNat p)
   => Integer
   -> Integer
-  -> [f]
-  -> [f]
-  -> [f]
-  -> [f]
-  -> f
-  -> f
-  -> LRPolys f
+  -> [PrimeField p]
+  -> [PrimeField p]
+  -> [PrimeField p]
+  -> [PrimeField p]
+  -> PrimeField p
+  -> PrimeField p
+  -> LRPolys (PrimeField p)
 computeLRPolys n m aL aR sL sR y z
   = LRPolys
         { l0 = aL ^-^ ((*) z <$> powerVector 1 nm)
diff --git a/Bulletproofs/MultiRangeProof/Verifier.hs b/Bulletproofs/MultiRangeProof/Verifier.hs
--- a/Bulletproofs/MultiRangeProof/Verifier.hs
+++ b/Bulletproofs/MultiRangeProof/Verifier.hs
@@ -12,6 +12,7 @@
 import qualified Crypto.PubKey.ECC.Generate as Crypto
 import qualified Crypto.PubKey.ECC.Prim as Crypto
 import qualified Crypto.PubKey.ECC.Types as Crypto
+import PrimeField (PrimeField(..), toInt)
 
 import Bulletproofs.RangeProof.Internal
 import Bulletproofs.Curve
@@ -22,10 +23,10 @@
 
 -- | Verify that a commitment was computed from a value in a given range
 verifyProof
-  :: (AsInteger f, Eq f, Field f, Show f)
+  :: KnownNat p
   => Integer        -- ^ Range upper bound
   -> [Crypto.Point]   -- ^ Commitments of in-range values
-  -> RangeProof f
+  -> RangeProof (PrimeField p)
   -- ^ Proof that a secret committed value lies in a certain interval
   -> Bool
 verifyProof upperBound vCommits proof@RangeProof{..}
@@ -48,14 +49,14 @@
 -- t = t(x) = t0 + t1*x + t2*x^2
 -- This is what binds the proof to the actual original Pedersen commitment V to the actual value
 verifyTPoly
-  :: (AsInteger f, Eq f, Field f)
+  :: KnownNat p
   => Integer         -- ^ Dimension n of the vectors
   -> [Crypto.Point]   -- ^ Commitments of in-range values
-  -> RangeProof f
+  -> RangeProof (PrimeField p)
   -- ^ Proof that a secret committed value lies in a certain interval
-  -> f              -- ^ Challenge x
-  -> f              -- ^ Challenge y
-  -> f              -- ^ Challenge z
+  -> PrimeField p              -- ^ Challenge x
+  -> PrimeField p              -- ^ Challenge y
+  -> PrimeField p              -- ^ Challenge z
   -> Bool
 verifyTPoly n vCommits proof@RangeProof{..} x y z
   = lhs == rhs
@@ -63,24 +64,24 @@
     m = fromIntegral $ length vCommits
     lhs = commit t tBlinding
     rhs =
-          sumExps ((*) (fSquare z) <$> powerVector z m) vCommits
+          sumExps ((*) (z ^ 2) <$> powerVector z m) vCommits
           `addP`
           (delta n m y z `mulP` g)
           `addP`
           (x `mulP` t1Commit)
           `addP`
-          (fSquare x `mulP` t2Commit)
+          ((x ^ 2) `mulP` t2Commit)
 
 -- | Verify the inner product argument for the vectors l and r that form t
 verifyLRCommitment
-  :: (AsInteger f, Eq f, Field f, Show f)
+  :: KnownNat p
   => Integer         -- ^ Dimension n of the vectors
   -> Integer
-  -> RangeProof f
+  -> RangeProof (PrimeField p)
   -- ^ Proof that a secret committed value lies in a certain interval
-  -> f              -- ^ Challenge x
-  -> f              -- ^ Challenge y
-  -> f              -- ^ Challenge z
+  -> PrimeField p              -- ^ Challenge x
+  -> PrimeField p              -- ^ Challenge y
+  -> PrimeField p              -- ^ Challenge z
   -> Bool
 verifyLRCommitment n m proof@RangeProof{..} x y z
   = IPP.verifyProof
diff --git a/Bulletproofs/RangeProof/Internal.hs b/Bulletproofs/RangeProof/Internal.hs
--- a/Bulletproofs/RangeProof/Internal.hs
+++ b/Bulletproofs/RangeProof/Internal.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE DeriveGeneric, DeriveAnyClass #-}
+{-# LANGUAGE DeriveGeneric, DeriveAnyClass, ViewPatterns #-}
 module Bulletproofs.RangeProof.Internal where
 
 import Protolude
@@ -10,6 +10,7 @@
 import Crypto.Random.Types (MonadRandom(..))
 import qualified Crypto.PubKey.ECC.Prim as Crypto
 import qualified Crypto.PubKey.ECC.Types as Crypto
+import PrimeField (PrimeField(..), toInt)
 
 import Bulletproofs.Utils
 import Bulletproofs.Curve
@@ -40,10 +41,10 @@
     -- has vectors l, r ∈  Z^n for which P = l · G + r · H + ( l, r ) · U
     } deriving (Show, Eq, Generic, NFData)
 
-data RangeProofError
+data RangeProofError f
   = UpperBoundTooLarge Integer  -- ^ The upper bound of the range is too large
-  | ValueNotInRange Integer     -- ^ Value is not within the range required
-  | ValuesNotInRange [Integer]  -- ^ Values are not within the range required
+  | ValueNotInRange f     -- ^ Value is not within the range required
+  | ValuesNotInRange [f]  -- ^ Values are not within the range required
   | NNotPowerOf2 Integer        -- ^ Dimension n is required to be a power of 2
   deriving (Show, Eq, Generic, NFData)
 
@@ -73,16 +74,16 @@
 -- | Encode the value v into a bit representation. Let aL be a vector
 -- of bits such that <aL, 2^n> = v (put more simply, the components of a L are the
 -- binary digits of v).
-encodeBit :: (AsInteger f, Num f) => Integer -> f -> [f]
-encodeBit n v = fillWithZeros n $ fromIntegral . digitToInt <$> showIntAtBase 2 intToDigit (asInteger v) ""
+encodeBit :: KnownNat p => Integer -> PrimeField p -> [PrimeField p]
+encodeBit n v = fillWithZeros n $ fromIntegral . digitToInt <$> showIntAtBase 2 intToDigit (toInt v) ""
 
 -- | Bits of v reversed.
 -- v = <a, 2^n> = a_0 * 2^0 + ... + a_n-1 * 2^(n-1)
-reversedEncodeBit :: (AsInteger f, Num f) => Integer -> f -> [f]
+reversedEncodeBit :: KnownNat p => Integer -> PrimeField p -> [PrimeField p]
 reversedEncodeBit n = reverse . encodeBit n
 
 -- TODO: Test it
-reversedEncodeBitMulti :: (AsInteger f, Num f) => Integer -> [f] -> [f]
+reversedEncodeBitMulti :: KnownNat p => Integer -> [PrimeField p] -> [PrimeField p]
 reversedEncodeBitMulti n = foldl' (\acc v -> acc ++ reversedEncodeBit n v) []
 
 -- | In order to prove that v is in range, each element of aL is either 0 or 1.
@@ -102,9 +103,9 @@
 -- | Obfuscate encoded bits with challenges y and z.
 -- z^2 * <aL, 2^n> + z * <aL − 1^n − aR, y^n> + <aL, aR · y^n> = (z^2) * v
 -- The property holds because <aL − 1^n − aR, y^n> = 0 and <aL · aR,  y^n> = 0
-obfuscateEncodedBits :: (Eq f, Field f) => Integer -> [f] -> [f] -> f -> f -> f
+obfuscateEncodedBits :: KnownNat p => Integer -> [PrimeField p] -> [PrimeField p] -> PrimeField p -> PrimeField p -> PrimeField p
 obfuscateEncodedBits n aL aR y z
-  = (fSquare z * dot aL (powerVector 2 n))
+  = ((z ^ 2) * dot aL (powerVector 2 n))
     + (z * dot ((aL ^-^ powerVector 1 n) ^-^ aR) yN)
     + dot (hadamardp aL aR) yN
   where
@@ -115,11 +116,11 @@
 -- what’s important is that the aL , aR terms be kept inside
 -- (since they can’t be shared with the Verifier):
 -- <aL − z * 1^n , y^n ◦ (aR + z * 1^n) + z^2 * 2^n> = z 2 v + δ(y, z)
-obfuscateEncodedBitsSingle :: (Eq f, Field f) => Integer -> [f] -> [f] -> f -> f -> f
+obfuscateEncodedBitsSingle :: KnownNat p => Integer -> [PrimeField p] -> [PrimeField p] -> PrimeField p -> PrimeField p -> PrimeField p
 obfuscateEncodedBitsSingle n aL aR y z
   = dot
       (aL ^-^ z1n)
-      (hadamardp (powerVector y n) (aR ^+^ z1n) ^+^ ((*) (fSquare z) <$> powerVector 2 n))
+      (hadamardp (powerVector y n) (aR ^+^ z1n) ^+^ ((*) (z ^ 2) <$> powerVector 2 n))
   where
     z1n = (*) z <$> powerVector 1 n
 
@@ -128,13 +129,13 @@
 -- Prover can send commitments to these vectors;
 -- these are properly blinded vector Pedersen commitments:
 commitBitVectors
-  :: (MonadRandom m, AsInteger f)
-  => f
-  -> f
-  -> [f]
-  -> [f]
-  -> [f]
-  -> [f]
+  :: (MonadRandom m)
+  => PrimeField p
+  -> PrimeField p
+  -> [PrimeField p]
+  -> [PrimeField p]
+  -> [PrimeField p]
+  -> [PrimeField p]
   -> m (Crypto.Point, Crypto.Point)
 commitBitVectors aBlinding sBlinding aL aR sL sR = do
     let aLG = sumExps aL gs
@@ -153,35 +154,35 @@
     pure (aCommit, sCommit)
 
 -- | (z − z^2) * <1^n, y^n> − z^3 * <1^n, 2^n>
-delta :: (Eq f, Field f) => Integer -> Integer -> f -> f -> f
+delta :: KnownNat p => Integer -> Integer -> PrimeField p -> PrimeField p -> PrimeField p
 delta n m y z
-  = ((z - fSquare z) * dot (powerVector 1 nm) (powerVector y nm))
+  = ((z - (z ^ 2)) * dot (powerVector 1 nm) (powerVector y nm))
   - foldl' (\acc j -> acc + ((z ^ (j + 2)) * dot (powerVector 1 n) (powerVector 2 n))) 0 [1..m]
   where
     nm = n * m
 
 -- | Check that a value is in a specific range
-checkRange :: Integer -> Integer -> Bool
-checkRange n v = v >= 0 && v < 2 ^ n
+checkRange :: Integer -> PrimeField p -> Bool
+checkRange n (toInt -> v) = v >= 0 && v < 2 ^ n
 
 -- | Check that a value is in a specific range
-checkRanges :: Integer -> [Integer] -> Bool
-checkRanges n vs = and $ fmap (\v -> v >= 0 && v < 2 ^ n) vs
+checkRanges :: Integer -> [PrimeField p] -> Bool
+checkRanges n vs = and $ fmap (\(toInt -> v) -> v >= 0 && v < 2 ^ n) vs
 
 -- | Compute commitment of linear vector polynomials l and r
 -- P = A + xS − zG + (z*y^n + z^2 * 2^n) * hs'
 computeLRCommitment
-  :: (AsInteger f, Eq f, Num f, Show f)
+  :: KnownNat p
   => Integer
   -> Integer
   -> Crypto.Point
   -> Crypto.Point
-  -> f
-  -> f
-  -> f
-  -> f
-  -> f
-  -> f
+  -> PrimeField p
+  -> PrimeField p
+  -> PrimeField p
+  -> PrimeField p
+  -> PrimeField p
+  -> PrimeField p
   -> [Crypto.Point]
   -> Crypto.Point
 computeLRCommitment n m aCommit sCommit t tBlinding mu x y z hs'
diff --git a/Bulletproofs/RangeProof/Prover.hs b/Bulletproofs/RangeProof/Prover.hs
--- a/Bulletproofs/RangeProof/Prover.hs
+++ b/Bulletproofs/RangeProof/Prover.hs
@@ -6,28 +6,28 @@
 import Protolude
 
 import Crypto.Random.Types (MonadRandom(..))
+import PrimeField (PrimeField(..), toInt)
 
-import Bulletproofs.Utils (AsInteger, Field)
 import Bulletproofs.RangeProof.Internal
 import qualified Bulletproofs.MultiRangeProof.Prover as MRP
 
 -- | Prove that a value lies in a specific range
 generateProof
-  :: (AsInteger f, Eq f, Field f, Show f, MonadRandom m)
+  :: (KnownNat p, MonadRandom m)
   => Integer                -- ^ Upper bound of the range we want to prove
-  -> (Integer, Integer)
+  -> (PrimeField p, PrimeField p)
   -- ^ Values we want to prove in range and their blinding factors
-  -> ExceptT RangeProofError m (RangeProof f)
+  -> ExceptT (RangeProofError (PrimeField p)) m (RangeProof (PrimeField p))
 generateProof upperBound (v, vBlinding) =
   MRP.generateProof upperBound [(v, vBlinding)]
 
 -- | Generate range proof from valid inputs
 generateProofUnsafe
-  :: (AsInteger f, Eq f, Field f, Show f, MonadRandom m)
+  :: (KnownNat p, MonadRandom m)
   => Integer    -- ^ Upper bound of the range we want to prove
-  -> (Integer, Integer)
+  -> (PrimeField p, PrimeField p)
   -- ^ Values we want to prove in range and their blinding factors
-  -> m (RangeProof f)
+  -> m (RangeProof (PrimeField p))
 generateProofUnsafe upperBound (v, vBlinding) =
   MRP.generateProofUnsafe upperBound [(v, vBlinding)]
 
diff --git a/Bulletproofs/RangeProof/Verifier.hs b/Bulletproofs/RangeProof/Verifier.hs
--- a/Bulletproofs/RangeProof/Verifier.hs
+++ b/Bulletproofs/RangeProof/Verifier.hs
@@ -9,6 +9,7 @@
 import Protolude
 
 import qualified Crypto.PubKey.ECC.Types as Crypto
+import PrimeField (PrimeField(..), toInt)
 
 import Bulletproofs.RangeProof.Internal
 import Bulletproofs.Curve
@@ -18,10 +19,10 @@
 
 -- | Verify that a commitment was computed from a value in a given range
 verifyProof
-  :: (AsInteger f, Eq f, Field f, Show f)
+  :: KnownNat p
   => Integer        -- ^ Range upper bound
   -> Crypto.Point   -- ^ Commitments of in-range values
-  -> RangeProof f
+  -> RangeProof (PrimeField p)
   -- ^ Proof that a secret committed value lies in a certain interval
   -> Bool
 verifyProof upperBound vCommit proof@RangeProof{..}
@@ -31,27 +32,27 @@
 -- t = t(x) = t0 + t1*x + t2*x^2
 -- This is what binds the proof to the actual original Pedersen commitment V to the actual value
 verifyTPoly
-  :: (AsInteger f, Eq f, Field f, Show f)
+  :: KnownNat p
   => Integer         -- ^ Dimension n of the vectors
   -> Crypto.Point    -- ^ Commitment of in-range value
-  -> RangeProof f
+  -> RangeProof (PrimeField p)
   -- ^ Proof that a secret committed value lies in a certain interval
-  -> f              -- ^ Challenge x
-  -> f              -- ^ Challenge y
-  -> f              -- ^ Challenge z
+  -> PrimeField p              -- ^ Challenge x
+  -> PrimeField p              -- ^ Challenge y
+  -> PrimeField p              -- ^ Challenge z
   -> Bool
 verifyTPoly n vCommit
   = MRP.verifyTPoly n [vCommit]
 
 -- | Verify the inner product argument for the vectors l and r that form t
 verifyLRCommitment
-  :: (AsInteger f, Eq f, Field f, Show f)
+  :: KnownNat p
   => Integer         -- ^ Dimension n of the vectors
-  -> RangeProof f
+  -> RangeProof (PrimeField p)
   -- ^ Proof that a secret committed value lies in a certain interval
-  -> f              -- ^ Challenge x
-  -> f              -- ^ Challenge y
-  -> f              -- ^ Challenge z
+  -> PrimeField p              -- ^ Challenge x
+  -> PrimeField p              -- ^ Challenge y
+  -> PrimeField p              -- ^ Challenge z
   -> Bool
 verifyLRCommitment n
   = MRP.verifyLRCommitment n 1
diff --git a/Bulletproofs/Utils.hs b/Bulletproofs/Utils.hs
--- a/Bulletproofs/Utils.hs
+++ b/Bulletproofs/Utils.hs
@@ -6,27 +6,11 @@
 import qualified Crypto.PubKey.ECC.Types as Crypto
 import Crypto.Random (MonadRandom)
 import Crypto.Number.Generate (generateMax)
+import PrimeField (PrimeField, toInt)
 
 import Bulletproofs.Fq as Fq hiding (asInteger)
 import Bulletproofs.Curve
 
-class AsInteger a where
-  asInteger :: a -> Integer
-
-instance AsInteger Fq where
-  asInteger (Fq x) = x
-
-instance AsInteger Integer where
-  asInteger x = x
-
--- Class for specialisations of field operations that may have
--- optimised implementations.
-class (Num f, Fractional f) => Field f where
-  fSquare :: f -> f
-
-instance Field Fq where
-  fSquare = Fq.fqSquare
-
 -- | Return a vector containing the first n powers of a
 powerVector :: (Eq f, Num f) => f -> Integer -> [f]
 powerVector a x
@@ -55,15 +39,15 @@
 subP x y = Crypto.pointAdd curve x (Crypto.pointNegate curve y)
 
 -- | Multiply a scalar and a point in an elliptic curve
-mulP :: AsInteger f => f -> Crypto.Point -> Crypto.Point
-mulP x = Crypto.pointMul curve (asInteger x)
+mulP :: PrimeField p -> Crypto.Point -> Crypto.Point
+mulP x = Crypto.pointMul curve (toInt x)
 
 -- | Double exponentiation (Shamir's trick): g0^x0 + g1^x1
-addTwoMulP :: AsInteger f => f -> Crypto.Point -> f -> Crypto.Point -> Crypto.Point
-addTwoMulP exp0 pt0 exp1 pt1 = Crypto.pointAddTwoMuls curve (asInteger exp0) pt0 (asInteger exp1) pt1
+addTwoMulP :: PrimeField p -> Crypto.Point -> PrimeField p -> Crypto.Point -> Crypto.Point
+addTwoMulP exp0 pt0 exp1 pt1 = Crypto.pointAddTwoMuls curve (toInt exp0) pt0 (toInt exp1) pt1
 
 -- | Raise every point to the corresponding exponent, sum up results
-sumExps :: AsInteger f => [f] -> [Crypto.Point] -> Crypto.Point
+sumExps :: [PrimeField p] -> [Crypto.Point] -> Crypto.Point
 sumExps (exp0:exp1:exps) (pt0:pt1:pts)
   = addTwoMulP exp0 pt0 exp1 pt1 `addP` sumExps exps pts
 sumExps (exp:_) (pt:_) = mulP exp pt -- this also catches cases where either list is longer than the other
@@ -71,7 +55,7 @@
 
 -- | Create a Pedersen commitment to a value given
 -- a value and a blinding factor
-commit :: AsInteger f => f -> f -> Crypto.Point
+commit :: PrimeField p -> PrimeField p -> Crypto.Point
 commit x r = addTwoMulP x g r h
 
 isLogBase2 :: Integer -> Bool
@@ -135,12 +119,12 @@
 shamirY :: Num f => Crypto.Point -> Crypto.Point -> f
 shamirY aCommit sCommit
   = fromInteger $ oracle $
-      show q <> pointToBS aCommit <> pointToBS sCommit
+      show _q <> pointToBS aCommit <> pointToBS sCommit
 
 shamirZ :: (Show f, Num f) => Crypto.Point -> Crypto.Point -> f -> f
 shamirZ aCommit sCommit y
   = fromInteger $ oracle $
-      show q <> pointToBS aCommit <> pointToBS sCommit <> show y
+      show _q <> pointToBS aCommit <> pointToBS sCommit <> show y
 
 shamirX
   :: (Show f, Num f)
@@ -153,7 +137,7 @@
   -> f
 shamirX aCommit sCommit t1Commit t2Commit y z
   = fromInteger $ oracle $
-      show q <> pointToBS aCommit <> pointToBS sCommit <> pointToBS t1Commit <> pointToBS t2Commit <> show y <> show z
+      show _q <> pointToBS aCommit <> pointToBS sCommit <> pointToBS t1Commit <> pointToBS t2Commit <> show y <> show z
 
 shamirX'
   :: Num f
@@ -163,9 +147,9 @@
   -> f
 shamirX' commitmentLR l' r'
   = fromInteger $ oracle $
-      show q <> pointToBS l' <> pointToBS r' <> pointToBS commitmentLR
+      show _q <> pointToBS l' <> pointToBS r' <> pointToBS commitmentLR
 
 shamirU :: (Show f, Num f) => f -> f -> f -> f
 shamirU tBlinding mu t
   = fromInteger $ oracle $
-      show q <> show tBlinding <> show mu <> show t
+      show _q <> show tBlinding <> show mu <> show t
diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,12 +1,23 @@
 # Changelog for bulletproofs
 
-## 0.1
+## 1.0
 
-* Initial release.
-* Implementation of the Bulletproofs protocol for range proofs
-* Use of the improved inner-product argument to reduce the communication complexity
-* Support for SECp256k1 curve
+* Use galois-field library as dependency
+* Remove custom definition of Fq
+* Remove Fractional constraints and use PrimeField instead
+* Update interface of rangeproofs to guarantee the use of prime fields
 
+## 0.4
+
+* Use double exponentiation to improve performance.
+* Use Control.Exception.assert to make sure debugging assertions are not checked
+  when compiled with optimisations.
+* Add benchmarks for rangeproofs.
+
+## 0.3
+
+* Update dependencies
+
 ## 0.2
 
 * Prove and verify computations of arithmetic circuits using Bulletproofs
@@ -16,13 +27,10 @@
 * Provide examples for using aggregated range proofs.
 * Add multi-range proofs documentation.
 
-## 0.3
-
-* Update dependencies
+## 0.1
 
-## 0.4
+* Initial release.
+* Implementation of the Bulletproofs protocol for range proofs
+* Use of the improved inner-product argument to reduce the communication complexity
+* Support for SECp256k1 curve
 
-* Use double exponentiation to improve performance.
-* Use Control.Exception.assert to make sure debugging assertions are not checked
-  when compiled with optimisations.
-* Add benchmarks for rangeproofs.
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright Adjoint Inc. (c) 2018
+Copyright Adjoint Inc. (c) 2018-2019
 
 All rights reserved.
 
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -100,7 +100,7 @@
 ```haskell
 import qualified Bulletproofs.RangeProof as RP
 
-testSingleRangeProof :: (Integer, Integer) -> IO Bool
+testSingleRangeProof :: (Fq, Fq) -> IO Bool
 testSingleRangeProof (v, vBlinding) = do
   let vCommit = commit v vBlinding
       -- n needs to be a power of 2
@@ -113,7 +113,7 @@
   -- Verifier
   case proofE of
     Left err -> panic $ show err
-    Right (proof@RangeProof{..})
+    Right (proof@RP.RangeProof{..})
       -> pure $ RP.verifyProof upperBound vCommit proof
 ```
 
@@ -123,7 +123,7 @@
 ```haskell
 import qualified Bulletproofs.MultiRangeProof as MRP
 
-testMultiRangeProof :: [(Integer, Integer)] -> IO Bool
+testMultiRangeProof :: [(Fq, Fq)] -> IO Bool
 testMultiRangeProof vsAndvBlindings = do
   let vCommits = fmap (uncurry commit) vsAndvBlindings
       -- n needs to be a power of 2
@@ -136,7 +136,7 @@
   -- Verifier
   case proofE of
     Left err -> panic $ show err
-    Right (proof@RangeProof{..})
+    Right (proof@RP.RangeProof{..})
       -> pure $ MRP.verifyProof upperBound vCommits proof
 ```
 
@@ -205,7 +205,7 @@
       v0 = sum aL
       v1 = sum aR
 
-  commitBlinders <- replicateM m Fq.random
+  commitBlinders <- replicateM m fqRandom
   let commitments = zipWith commit [v0, v1] commitBlinders
 
   let arithWitness = ArithWitness
diff --git a/bulletproofs.cabal b/bulletproofs.cabal
--- a/bulletproofs.cabal
+++ b/bulletproofs.cabal
@@ -2,10 +2,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: 45ba76c7c1825f4c36913fb81f9c8f6c9691748248208ec2ff4d1bbb54c9a836
+-- hash: c3039f817828e381dba02b51b3dd24480614dfa32254ebae4b12992e2d2a126e
 
 name:           bulletproofs
-version:        0.4.0
+version:        1.0.0
 description:    Please see the README on GitHub at <https://github.com/adjoint-io/bulletproofs#readme>
 category:       Cryptography
 homepage:       https://github.com/adjoint-io/bulletproofs#readme
@@ -47,14 +47,17 @@
       Paths_bulletproofs
   hs-source-dirs:
       ./.
-  default-extensions: OverloadedStrings NoImplicitPrelude
+  default-extensions: LambdaCase RecordWildCards OverloadedStrings NoImplicitPrelude FlexibleInstances ExplicitForAll RankNTypes DataKinds KindSignatures GeneralizedNewtypeDeriving TypeApplications ExistentialQuantification ScopedTypeVariables DeriveGeneric BangPatterns FlexibleContexts
   build-depends:
       MonadRandom
+    , QuickCheck
     , arithmoi
     , base >=4.7 && <5
     , containers
     , cryptonite
+    , galois-field
     , memory
+    , pairing
     , protolude >=0.2
     , random-shuffle
     , text
@@ -80,7 +83,9 @@
     , bulletproofs
     , containers
     , cryptonite
+    , galois-field
     , memory
+    , pairing
     , protolude >=0.2
     , random-shuffle
     , tasty
@@ -106,7 +111,9 @@
     , containers
     , criterion >=1.5.1.0
     , cryptonite
+    , galois-field
     , memory
+    , pairing
     , protolude >=0.2
     , random-shuffle
     , tasty
diff --git a/tests/TestArithCircuitProtocol.hs b/tests/TestArithCircuitProtocol.hs
--- a/tests/TestArithCircuitProtocol.hs
+++ b/tests/TestArithCircuitProtocol.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE ViewPatterns, RecordWildCards  #-}
+{-# LANGUAGE ViewPatterns, RecordWildCards, TypeApplications  #-}
 
 module TestArithCircuitProtocol where
 
@@ -34,28 +34,17 @@
 --    wL * aL + wR * aR + wO * aO - c = wV * v
 test_arithCircuitProof_arbitrary :: TestTree
 test_arithCircuitProof_arbitrary = localOption (QuickCheckTests 10) $
-  testProperty "Arbitrary arithmetic circuit proof" $ QCM.monadicIO $ do
-    n <- QCM.run $ generateBetween 1 100
-    m <- QCM.run $ generateBetween 1 n
-    let lConstraints = m
-
-    weights@GateWeights{..} <- QCM.run $ generateGateWeights lConstraints n
-    commitmentWeights <- QCM.run $ generateWv lConstraints m
-    Assignment{..} <- QCM.run $ generateRandomAssignment n
-
-    cs <- QCM.run $ replicateM (fromIntegral m) Fq.random
-    commitBlinders <- QCM.run $ replicateM (fromIntegral m) Fq.random
-
-    let gateWeights = GateWeights wL wR wO
-        gateInputs = Assignment aL aR aO
-        vs = computeInputValues weights commitmentWeights gateInputs cs
-        commitments = zipWith commit vs commitBlinders
-        arithCircuit = ArithCircuit gateWeights commitmentWeights cs
-        arithWitness = ArithWitness gateInputs commitments commitBlinders
-
-    proof <- QCM.run $ generateProof arithCircuit arithWitness
-
-    QCM.assert $ verifyProof commitments proof arithCircuit
+  testProperty "Arbitrary arithmetic circuit proof" $ go
+  where
+    go :: Property
+    go = forAll (arbitrary `suchThat` ((<) 100))
+         $ \n -> forAll (arbitrary `suchThat` (\m -> m > 0 && m < n))
+         $ \m -> forAll (arithCircuitGen @(PF Fq) n m)
+         $ \arithCircuit@ArithCircuit{..} -> forAll (arithAssignmentGen n)
+         $ \assignment@Assignment{..} -> forAll (arithWitnessGen assignment arithCircuit m)
+         $ \arithWitness@ArithWitness{..} -> QCM.monadicIO $ do
+      proof <- QCM.run $ generateProof arithCircuit arithWitness
+      QCM.assert $ verifyProof commitments proof arithCircuit
 
 -- | Test hadamard product relation
 --  2 linear constraints (q = 2):
@@ -67,40 +56,40 @@
 --  2 input values (m = 2)
 test_arithCircuitProof_hadamardp :: TestTree
 test_arithCircuitProof_hadamardp = localOption (QuickCheckTests 20) $
-  testProperty "Arithmetic circuit proof. Hadamard product relation" $ QCM.monadicIO $ do
-
-    let n = 16
-    aL <- QCM.run $ replicateM (fromIntegral n) Fq.random
-    aR <- QCM.run $ replicateM (fromIntegral n) Fq.random
-    let aO = aL `hadamardp` aR
+  testProperty "Arithmetic circuit proof. Hadamard product relation" go
+  where
+    n = 16
+    go :: Fq -> Fq -> Property
+    go r s = forAll (vectorOf n (arbitrary @Fq))
+        $ \aL -> forAll (vectorOf n arbitrary)
+        $ \aR -> QCM.monadicIO $ do
+      let aO = aL `hadamardp` aR
 
-    r <- QCM.run Fq.random
-    s <- QCM.run Fq.random
-    let v0 = sum aL
-        v1 = sum aR
+      let v0 = sum aL
+          v1 = sum aR
 
-    let v0Commit = commit v0 r
-        v1Commit = commit v1 s
+      let v0Commit = commit v0 r
+          v1Commit = commit v1 s
 
-    let zeroVector = replicate (fromIntegral n) 0
-        oneVector = replicate (fromIntegral n) 1
+      let zeroVector = replicate (fromIntegral n) 0
+          oneVector = replicate (fromIntegral n) 1
 
-    let wL = [oneVector, zeroVector]
-        wR = [zeroVector, oneVector]
-        wO = [zeroVector, zeroVector]
+      let wL = [oneVector, zeroVector]
+          wR = [zeroVector, oneVector]
+          wO = [zeroVector, zeroVector]
 
-        commitmentWeights = [[1, 0], [0, 1]]
-        cs = [0, 0]
-        commitments = [v0Commit, v1Commit]
-        commitBlinders = [r, s]
-        gateWeights = GateWeights wL wR wO
-        gateInputs = Assignment aL aR aO
-        arithCircuit = ArithCircuit gateWeights commitmentWeights cs
-        arithWitness = ArithWitness gateInputs commitments commitBlinders
+          commitmentWeights = [[1, 0], [0, 1]]
+          cs = [0, 0]
+          commitments = [v0Commit, v1Commit]
+          commitBlinders = [r, s]
+          gateWeights = GateWeights wL wR wO
+          gateInputs = Assignment aL aR aO
+          arithCircuit = ArithCircuit gateWeights commitmentWeights cs
+          arithWitness = ArithWitness gateInputs commitments commitBlinders
 
-    proof <- QCM.run $ generateProof arithCircuit arithWitness
+      proof <- QCM.run $ generateProof arithCircuit arithWitness
 
-    QCM.assert $ verifyProof commitments proof arithCircuit
+      QCM.assert $ verifyProof commitments proof arithCircuit
 
 -- | Test that an addition circuit without multiplication gates succeeds
 --  1 linear constraints (q = 1):
@@ -111,30 +100,31 @@
 --  3 input values (m = 3)
 test_arithCircuitProof_no_mult_gates :: TestTree
 test_arithCircuitProof_no_mult_gates = localOption (QuickCheckTests 20) $
-  testProperty "Arithmetic circuit proof. n = 0, m = 3, q = 1"
-    $ QCM.monadicIO $ do
-    let n = 0
-        m = 3
-
-    commitBlinders <- QCM.run $ replicateM m Fq.random
-    let wL = [[]]
-        wR = [[]]
-        wO = [[]]
-        cs = [0]
-        aL = []
-        aR = []
-        aO = []
-        commitmentWeights = [[1, 1, -1]]
-        vs = [2, 5, 7]
-        commitments = zipWith commit vs commitBlinders
-        gateWeights = GateWeights wL wR wO
-        gateInputs = Assignment aL aR aO
-        arithCircuit = ArithCircuit gateWeights commitmentWeights cs
-        arithWitness = ArithWitness gateInputs commitments commitBlinders
+  testProperty "Arithmetic circuit proof. n = 0, m = 3, q = 1" go
+  where
+    m = 3
+    go :: Property
+    go = forAll (vectorOf (fromIntegral m) (arbitrary @Fq))
+         $ \commitBlinders -> QCM.monadicIO $ do
+      let n = 0
+      let wL = [[]]
+          wR = [[]]
+          wO = [[]]
+          cs = [0]
+          aL = []
+          aR = []
+          aO = []
+          commitmentWeights = [[1, 1, -1]]
+          vs = [2, 5, 7]
+          commitments = zipWith commit vs commitBlinders
+          gateWeights = GateWeights wL wR wO
+          gateInputs = Assignment aL aR aO
+          arithCircuit = ArithCircuit gateWeights commitmentWeights cs
+          arithWitness = ArithWitness gateInputs commitments commitBlinders
 
-    proof <- QCM.run $ generateProof arithCircuit arithWitness
+      proof <- QCM.run $ generateProof arithCircuit arithWitness
 
-    QCM.assert $ verifyProof commitments proof arithCircuit
+      QCM.assert $ verifyProof commitments proof arithCircuit
 
 --  | Test that a circuit with a single multiplication gate
 --  with linear contraints and not committed values succeeds
@@ -149,31 +139,30 @@
 --  0 input values (m = 0)
 test_arithCircuitProof_no_input_values :: TestTree
 test_arithCircuitProof_no_input_values = localOption (QuickCheckTests 20) $
-  testProperty "Arithmetic circuit proof. n = 1, m = 0, q = 3"
-    $ QCM.monadicIO $ do
-    let n = 1
-        m = 0
-
-    commitBlinders <- QCM.run $ replicateM m Fq.random
-    let wL = [[0], [0], [1]]
-        wR = [[0], [1], [0]]
-        wO = [[1], [0], [0]]
-        cs = [35, 5, 7]
-        aL = [7]
-        aR = [5]
-        aO = [35]
-        commitmentWeights = [[], [], []]
-        vs = []
-        commitments = zipWith commit vs commitBlinders
-        gateWeights = GateWeights wL wR wO
-        gateInputs = Assignment aL aR aO
-        arithCircuit = ArithCircuit gateWeights commitmentWeights cs
-        arithWitness = ArithWitness gateInputs commitments commitBlinders
-
-    proof <- QCM.run $ generateProof arithCircuit arithWitness
-
-    QCM.assert $ verifyProof commitments proof arithCircuit
+  testProperty "Arithmetic circuit proof. n = 1, m = 0, q = 3" go
+  where
+    m = 0
+    go :: Property
+    go = forAll (vectorOf (fromIntegral m) (arbitrary @Fq))
+         $ \commitBlinders -> QCM.monadicIO $ do
+      let n = 1
 
+      let wL = [[0], [0], [1]]
+          wR = [[0], [1], [0]]
+          wO = [[1], [0], [0]]
+          cs = [35, 5, 7]
+          aL = [7]
+          aR = [5]
+          aO = [35]
+          commitmentWeights = [[], [], []]
+          vs = []
+          commitments = zipWith commit vs commitBlinders
+          gateWeights = GateWeights wL wR wO
+          gateInputs = Assignment aL aR aO
+          arithCircuit = ArithCircuit gateWeights commitmentWeights cs
+          arithWitness = ArithWitness gateInputs commitments commitBlinders
+      proof <- QCM.run $ generateProof arithCircuit arithWitness
+      QCM.assert $ verifyProof commitments proof arithCircuit
 
 --  5 linear constraints (q = 5):
 --  aO[0] = aO[1]
@@ -189,42 +178,43 @@
 --  4 input values (m = 4)
 test_arithCircuitProof_shuffle_circuit :: TestTree
 test_arithCircuitProof_shuffle_circuit = localOption (QuickCheckTests 20) $
-  testProperty "Arithmetic circuit proof. n = 2, m = 4, q = 5" $ QCM.monadicIO $ do
-    z <- QCM.run Fq.random
-    commitBlinders <- QCM.run $ replicateM 4 Fq.random
-
-    let wL = [[0, 0]
-             ,[1, 0]
-             ,[0, 1]
-             ,[0, 0]
-             ,[0, 0]]
-        wR = [[0, 0]
-             ,[0, 0]
-             ,[0, 0]
-             ,[1, 0]
-             ,[0, 1]]
-        wO = [[1, -1]
-             ,[0, 0]
-             ,[0, 0]
-             ,[0, 0]
-             ,[0, 0]]
-        wV = [[0, 0, 0, 0]
-             ,[1, 0, 0, 0]
-             ,[0, 0, 1, 0]
-             ,[0, 1, 0 ,0]
-             ,[0, 0, 0, 1]]
-        cs = [0, -z, -z, -z, -z]
-        aL = [4 - z, 9 - z]
-        aR = [9 - z, 4 - z]
-        aO = aL `hadamardp` aR
-        vs = [4, 9, 9, 4]
-        commitments = zipWith commit vs commitBlinders
-        gateWeights = GateWeights wL wR wO
-        gateInputs = Assignment aL aR aO
-        arithCircuit = ArithCircuit gateWeights wV cs
-        arithWitness = ArithWitness gateInputs commitments commitBlinders
+  testProperty "Arithmetic circuit proof. n = 2, m = 4, q = 5" $ go
+  where
+    go :: Fq -> Property
+    go z = forAll (vectorOf 4 (arbitrary @Fq))
+        $ \commitBlinders -> QCM.monadicIO $ do
 
-    proof <- QCM.run $ generateProof arithCircuit arithWitness
+      let wL = [[0, 0]
+               ,[1, 0]
+               ,[0, 1]
+               ,[0, 0]
+               ,[0, 0]]
+          wR = [[0, 0]
+               ,[0, 0]
+               ,[0, 0]
+               ,[1, 0]
+               ,[0, 1]]
+          wO = [[1, -1]
+               ,[0, 0]
+               ,[0, 0]
+               ,[0, 0]
+               ,[0, 0]]
+          wV = [[0, 0, 0, 0]
+               ,[1, 0, 0, 0]
+               ,[0, 0, 1, 0]
+               ,[0, 1, 0 ,0]
+               ,[0, 0, 0, 1]]
+          cs = [0, -z, -z, -z, -z]
+          aL = [4 - z, 9 - z]
+          aR = [9 - z, 4 - z]
+          aO = aL `hadamardp` aR
+          vs = [4, 9, 9, 4]
+          commitments = zipWith commit vs commitBlinders
+          gateWeights = GateWeights wL wR wO
+          gateInputs = Assignment aL aR aO
+          arithCircuit = ArithCircuit gateWeights wV cs
+          arithWitness = ArithWitness gateInputs commitments commitBlinders
 
-    QCM.assert $ verifyProof commitments proof arithCircuit
+      proof <- QCM.run $ generateProof arithCircuit arithWitness
+      QCM.assert $ verifyProof commitments proof arithCircuit
 
diff --git a/tests/TestField.hs b/tests/TestField.hs
--- a/tests/TestField.hs
+++ b/tests/TestField.hs
@@ -16,9 +16,6 @@
 
 import TestCommon
 
-instance Arbitrary Fq where
-  arbitrary = Fq.new <$> arbitrary
-
 prop_addMod :: Fq -> Fq -> Property
 prop_addMod x y
   = (x + y) `mulP` g === (x `mulP` g) `addP` (y `mulP` g)
@@ -26,7 +23,6 @@
 prop_subMod :: Fq -> Fq -> Property
 prop_subMod x y
   = (x - y) `mulP` g === (x `mulP` g) `addP` Crypto.pointNegate curve (y `mulP` g)
-
 
 -------------------------------------------------------------------------------
 -- Laws of field operations
diff --git a/tests/TestProtocol.hs b/tests/TestProtocol.hs
--- a/tests/TestProtocol.hs
+++ b/tests/TestProtocol.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE ViewPatterns, RecordWildCards, TypeApplications  #-}
+{-# LANGUAGE ViewPatterns, RecordWildCards, TypeApplications, ScopedTypeVariables  #-}
 
 module TestProtocol where
 
@@ -14,6 +14,7 @@
 import qualified Crypto.PubKey.ECC.Generate as Crypto
 import qualified Crypto.PubKey.ECC.Prim as Crypto
 import qualified Crypto.PubKey.ECC.Types as Crypto
+import GaloisField (GaloisField(..))
 
 import Bulletproofs.Curve
 import qualified Bulletproofs.RangeProof as RP
@@ -47,16 +48,16 @@
 prop_dot_aL2n :: Property
 prop_dot_aL2n = QCM.monadicIO $ do
   n <- QCM.run $ (2 ^) <$> generateMax 8
-  v <- QCM.run $ randomN n
-  QCM.assert $ RP.reversedEncodeBit n v `dot` powerVector 2 n == v
+  v <- QCM.run $ fromInteger <$> randomN n
+  QCM.assert $ RP.reversedEncodeBit @(PF Fq) n v `dot` powerVector 2 n == v
 
 prop_challengeComplementaryVector :: Property
 prop_challengeComplementaryVector = QCM.monadicIO $ do
   n <- QCM.run $ (2 ^) <$> generateMax 8
-  v <- QCM.run $ randomN n
-  let aL = RP.reversedEncodeBit n v
+  v <- QCM.run $ fromInteger <$> randomN n
+  let aL = RP.reversedEncodeBit @(PF Fq) n v
       aR = RP.complementaryVector aL
-  y <- QCM.run $ randomN n
+  y <- QCM.run $ fromInteger <$> randomN n
   QCM.assert
     $ dot
       ((aL ^-^ powerVector 1 n) ^-^ aR)
@@ -67,19 +68,19 @@
 prop_reversedEncodeBitAggr :: Int -> Property
 prop_reversedEncodeBitAggr x = QCM.monadicIO $ do
   n <- QCM.run $ (2 ^) <$> generateMax 8
-  vs <- QCM.run $ replicateM x $ randomN n
+  vs <- QCM.run $ ((<$>) fromInteger) <$> replicateM x (randomN n)
   let m = fromIntegral $ length vs
-      reversed = RP.reversedEncodeBitMulti n vs
+      reversed = RP.reversedEncodeBitMulti @(PF Fq) n vs
   QCM.assert $ vs == fmap (\j -> dot (slice n j reversed) (powerVector 2 n)) [1..m]
 
 prop_challengeComplementaryVectorAggr :: Int -> Property
 prop_challengeComplementaryVectorAggr x = QCM.monadicIO $ do
   n <- QCM.run $ (2 ^) <$> generateMax 8
-  vs <- QCM.run $ replicateM 3 $ randomN n
-  let aL = RP.reversedEncodeBitMulti n vs
+  vs <- QCM.run $ ((<$>) fromInteger) <$> replicateM 3 (randomN n)
+  let aL = RP.reversedEncodeBitMulti @(PF Fq) n vs
       aR = RP.complementaryVector aL
       m = length vs
-  y <- QCM.run $ randomN n
+  y <- QCM.run $ fromInteger <$> randomN n
   QCM.assert $
     replicate m 0
     ==
@@ -92,11 +93,11 @@
 prop_obfuscateEncodedBits y z
   = QCM.monadicIO $ do
   n <- QCM.run $ (2 ^) <$> generateMax 8
-  v <- QCM.run $ Fq.new <$> randomN n
+  v <- QCM.run $ fromInteger <$> randomN n
   let aL = RP.reversedEncodeBit n v
       aR = RP.complementaryVector aL
 
-  QCM.assert $ RP.obfuscateEncodedBits n aL aR y z == fSquare z * v
+  QCM.assert $ RP.obfuscateEncodedBits n aL aR y z == (z ^ 2) * v
 
 prop_singleInnerProduct
   :: Fq
@@ -105,18 +106,18 @@
 prop_singleInnerProduct y z
   = QCM.monadicIO $ do
   n <- QCM.run $ (2 ^) <$> generateMax 8
-  v <- QCM.run $ Fq.new <$> randomN n
+  v <- QCM.run $ fromInteger <$> randomN n
 
   let aL = RP.reversedEncodeBit n v
       aR = RP.complementaryVector aL
 
-  QCM.assert $ RP.obfuscateEncodedBitsSingle n aL aR y z == (fSquare z * v) + RP.delta n 1 y z
+  QCM.assert $ RP.obfuscateEncodedBitsSingle n aL aR y z == ((z ^ 2) * v) + RP.delta n 1 y z
 
-setupV :: MonadRandom m => Integer -> m ((Integer, Integer), Crypto.Point)
+setupV :: MonadRandom m => Integer -> m ((Fq, Fq), Crypto.Point)
 setupV n = do
-  v <- generateMax (2^n)
-  vBlinding <- Crypto.scalarGenerate curve
-  let vCommit = commit (Fq.new v) (Fq.new vBlinding)
+  v <- fromInteger <$> generateMax (2^n)
+  vBlinding <- fromInteger <$> Crypto.scalarGenerate curve
+  let vCommit = commit v vBlinding
   pure ((v, vBlinding), vCommit)
 
 test_verifyTPolynomial :: TestTree
@@ -159,9 +160,9 @@
   n <- QCM.run $ (2 ^) <$> generateMax 8
   ((v, vBlinding), vCommit) <- QCM.run $ setupV n
   let upperBound = getUpperBound n
-      vNotInRange = v + upperBound
+      vNotInRange = fromInteger (toInt v + upperBound)
 
-  proofE <- QCM.run $ runExceptT $ MRP.generateProof @Fq upperBound [(vNotInRange, vBlinding)]
+  proofE <- QCM.run $ runExceptT $ MRP.generateProof upperBound [(vNotInRange, vBlinding)]
   case proofE of
     Left err ->
       QCM.assert $ RP.ValuesNotInRange [vNotInRange] == err
@@ -172,8 +173,8 @@
 prop_invalidUpperBound = QCM.monadicIO $ do
   n <- QCM.run $ (2 ^) <$> generateMax 8
   ((v, vBlinding), vCommit) <- QCM.run $ setupV n
-  let invalidUpperBound = q + 1
-  proofE <- QCM.run $ runExceptT $ MRP.generateProof @Fq invalidUpperBound [(v, vBlinding)]
+  let invalidUpperBound = _q + 1
+  proofE <- QCM.run $ runExceptT $ MRP.generateProof invalidUpperBound [(v, vBlinding)]
   case proofE of
     Left err ->
       QCM.assert $ RP.UpperBoundTooLarge invalidUpperBound == err
@@ -184,7 +185,7 @@
 prop_differentUpperBound (Positive upperBound') = expectFailure . QCM.monadicIO $ do
   n <- QCM.run $ (2 ^) <$> generateMax 8
   ((v, vBlinding), vCommit) <- QCM.run $ setupV n
-  proofE <- QCM.run $ runExceptT $ MRP.generateProof @Fq (getUpperBound n) [(v, vBlinding)]
+  proofE <- QCM.run $ runExceptT $ MRP.generateProof @(PF Fq) (getUpperBound n) [(v, vBlinding)]
   case proofE of
     Left err -> panic $ show err
     Right (proof@RP.RangeProof{..}) ->
@@ -195,12 +196,12 @@
   testProperty "Check invalid commitment" $ QCM.monadicIO $ do
   n <- QCM.run $ (2 ^) <$> generateMax 8
   ((v, vBlinding), vCommit) <- QCM.run $ setupV n
-  let invalidVCommit = commit (Fq.new $ v + 1) (Fq.new vBlinding)
+  let invalidVCommit = commit (v + 1) vBlinding
       upperBound = getUpperBound n
-  proofE <- QCM.run $ runExceptT $ MRP.generateProof @Fq upperBound [(v, vBlinding)]
+  proofE <- QCM.run $ runExceptT $ MRP.generateProof @(PF Fq) upperBound [(v, vBlinding)]
   case proofE of
     Left err -> panic $ show err
-    Right (proof@RP.RangeProof{..}) ->
+    Right (proof@(RP.RangeProof{..})) ->
       QCM.assert $ not $ MRP.verifyProof upperBound [invalidVCommit] proof
 
 test_multiRangeProof_completeness :: TestTree
@@ -211,7 +212,7 @@
     ctx <- QCM.run $ replicateM (fromIntegral m) (setupV n)
     let upperBound = getUpperBound n
 
-    proofE <- QCM.run $ runExceptT $ MRP.generateProof @Fq (getUpperBound n) (fst <$> ctx)
+    proofE <- QCM.run $ runExceptT $ MRP.generateProof @(PF Fq) (getUpperBound n) (fst <$> ctx)
     case proofE of
       Left err -> panic $ show err
       Right (proof@RP.RangeProof{..}) ->
@@ -224,7 +225,7 @@
     ((v, vBlinding), vCommit) <- QCM.run $ setupV n
     let upperBound = getUpperBound n
 
-    proofE <- QCM.run $ runExceptT $ RP.generateProof @Fq (getUpperBound n) (v, vBlinding)
+    proofE <- QCM.run $ runExceptT $ RP.generateProof @(PF Fq) (getUpperBound n) (v, vBlinding)
     case proofE of
       Left err -> panic $ show err
       Right (proof@RP.RangeProof{..}) ->
