diff --git a/plutus-use-cases/src/Plutus/Contracts/Stablecoin.hs b/plutus-use-cases/src/Plutus/Contracts/Stablecoin.hs index ec07588806..7d405a25e4 100644 --- a/plutus-use-cases/src/Plutus/Contracts/Stablecoin.hs +++ b/plutus-use-cases/src/Plutus/Contracts/Stablecoin.hs @@ -101,11 +101,11 @@ import Plutus.Contract.StateMachine (AsSMContractError, OnChainState (..), SMCon import Plutus.Contract.StateMachine qualified as SM import PlutusTx qualified import PlutusTx.Prelude -import PlutusTx.Ratio as R +import PlutusTx.Ratio qualified as R import Prelude qualified as Haskell -- | Conversion rate from peg currency (eg. USD) to base currency (eg. Ada) -type ConversionRate = Ratio Integer +type ConversionRate = Rational -- Amounts of stablecoins and reservecoins (used for bookkeeping) -- SC, RC and BC are values that can be represented on-chain with the 'Value' @@ -164,7 +164,7 @@ initialState StateMachineClient{scInstance=SM.StateMachineInstance{SM.typedValid {-# INLINEABLE convert #-} -- | Convert peg currency units to base currency units using the -- observed conversion rate -convert :: ConversionRate -> PC (Ratio Integer) -> BC (Ratio Integer) +convert :: ConversionRate -> PC Rational -> BC Rational convert rate (PC pc) = BC $ rate * pc @@ -173,7 +173,7 @@ convert rate (PC pc) = liabilities :: BankState -> ConversionRate - -> BC (Ratio Integer) + -> BC Rational liabilities BankState{bsReserves=BC reserves,bsStablecoins=SC stablecoins} cr = let BC stableCoinLiabilities = convert cr (PC $ fromInteger stablecoins) in BC (min (fromInteger reserves) stableCoinLiabilities) @@ -184,7 +184,7 @@ liabilities BankState{bsReserves=BC reserves,bsStablecoins=SC stablecoins} cr = equity :: BankState -> ConversionRate - -> BC (Ratio Integer) + -> BC Rational equity r@BankState{bsReserves=BC reserves} cr = let BC l = liabilities r cr in BC (fromInteger reserves - l) @@ -193,9 +193,9 @@ equity r@BankState{bsReserves=BC reserves} cr = data Stablecoin = Stablecoin { scOracle :: PaymentPubKey -- ^ Public key of the oracle that provides exchange rates - , scFee :: Ratio Integer -- ^ Fee charged by bank for transactions. Calculated as a fraction of the total transaction volume in base currency. - , scMinReserveRatio :: Ratio Integer -- ^ The minimum ratio of reserves to liabilities - , scMaxReserveRatio :: Ratio Integer -- ^ The maximum ratio of reserves to liabilities + , scFee :: Rational -- ^ Fee charged by bank for transactions. Calculated as a fraction of the total transaction volume in base currency. + , scMinReserveRatio :: Rational -- ^ The minimum ratio of reserves to liabilities + , scMaxReserveRatio :: Rational -- ^ The maximum ratio of reserves to liabilities , scReservecoinDefaultPrice :: BC Integer -- ^ The price of a single reservecoin if no reservecoins have been issued , scBaseCurrency :: AssetClass -- ^ The asset class of the base currency. Value of this currency will be locked by the stablecoin state machine instance , scStablecoinTokenName :: TokenName -- ^ 'TokenName' of the stablecoin @@ -207,7 +207,7 @@ data Stablecoin = {-# INLINEABLE minReserve #-} -- | Minimum number of base currency coins held by the bank. -- Returns 'Nothing' if no stablecoins have been minted. -minReserve :: Stablecoin -> ConversionRate -> BankState -> Maybe (BC (Ratio Integer)) +minReserve :: Stablecoin -> ConversionRate -> BankState -> Maybe (BC Rational) minReserve Stablecoin{scMinReserveRatio} cr BankState{bsStablecoins=SC sc} | sc == zero = Nothing | otherwise = @@ -217,7 +217,7 @@ minReserve Stablecoin{scMinReserveRatio} cr BankState{bsStablecoins=SC sc} -- | Maximum number of base currency coins held by the bank. -- Returns 'Nothing' if no stablecoins have been minted. {-# INLINEABLE maxReserve #-} -maxReserve :: Stablecoin -> ConversionRate -> BankState -> Maybe (BC (Ratio Integer)) +maxReserve :: Stablecoin -> ConversionRate -> BankState -> Maybe (BC Rational) maxReserve Stablecoin{scMaxReserveRatio} cr BankState{bsStablecoins=SC sc} | sc == zero = Nothing | otherwise = @@ -226,7 +226,7 @@ maxReserve Stablecoin{scMaxReserveRatio} cr BankState{bsStablecoins=SC sc} {-# INLINEABLE reservecoinNominalPrice #-} -- | Price of a single reservecoin in base currency -reservecoinNominalPrice :: Stablecoin -> BankState -> ConversionRate -> BC (Ratio Integer) +reservecoinNominalPrice :: Stablecoin -> BankState -> ConversionRate -> BC Rational reservecoinNominalPrice Stablecoin{scReservecoinDefaultPrice} bankState@BankState{bsReservecoins=RC rc} cr | rc /= 0 = let BC e = equity bankState cr in BC (e * R.recip (fromInteger rc)) | otherwise = fmap fromInteger scReservecoinDefaultPrice @@ -234,7 +234,7 @@ reservecoinNominalPrice Stablecoin{scReservecoinDefaultPrice} bankState@BankStat {-# INLINEABLE stablecoinNominalPrice #-} -- | Price of a single stablecoin in base currency. If the banks' liabilities -- exceed its reserves then 'stablecoinNominalPrice' is zero. -stablecoinNominalPrice :: BankState -> ConversionRate -> BC (Ratio Integer) +stablecoinNominalPrice :: BankState -> ConversionRate -> BC Rational stablecoinNominalPrice bankState@BankState{bsStablecoins=SC sc} cr | sc == zero = BC p | otherwise = BC $ min p l @@ -252,7 +252,7 @@ data SCAction {-# INLINEABLE calcFees #-} -- | Calculate transaction fees (paid in base currency to the bank) as a -- fraction of the transaction's volume -calcFees :: Stablecoin -> BankState -> ConversionRate -> SCAction -> BC (Ratio Integer) +calcFees :: Stablecoin -> BankState -> ConversionRate -> SCAction -> BC Rational calcFees sc@Stablecoin{scFee} bs conversionRate = \case MintStablecoin (SC i) -> stablecoinNominalPrice bs conversionRate * BC scFee * (BC $ abs $ fromInteger i) @@ -352,8 +352,8 @@ data InvalidStateReason = NegativeReserveCoins | NegativeReserves | NegativeStablecoins - | MinReserves { allowed :: BC (Ratio Integer), actual :: BC (Ratio Integer) } - | MaxReserves { allowed :: BC (Ratio Integer), actual :: BC (Ratio Integer) } + | MinReserves { allowed :: BC Rational, actual :: BC Rational } + | MaxReserves { allowed :: BC Rational, actual :: BC Rational } | NegativeLiabilities | NegativeEquity deriving (Haskell.Show) diff --git a/plutus-use-cases/src/Plutus/Contracts/Uniswap/Pool.hs b/plutus-use-cases/src/Plutus/Contracts/Uniswap/Pool.hs index 069a5d887d..f384c7f852 100644 --- a/plutus-use-cases/src/Plutus/Contracts/Uniswap/Pool.hs +++ b/plutus-use-cases/src/Plutus/Contracts/Uniswap/Pool.hs @@ -12,7 +12,7 @@ module Plutus.Contracts.Uniswap.Pool import Ledger.Value (TokenName (..), unAssetClass, unCurrencySymbol) import Plutus.Contracts.Uniswap.Types -import PlutusTx.Prelude +import PlutusTx.Prelude hiding (ratio) import PlutusTx.Sqrt {-# INLINABLE calculateInitialLiquidity #-} @@ -34,7 +34,7 @@ calculateAdditionalLiquidity oldA' oldB' liquidity delA' delB' = Exactly x -> Amount x - liquidity Approximately x -> Amount x - liquidity where - ratio = (unAmount (liquidity * liquidity * newProd)) % unAmount oldProd + ratio = unsafeRatio (unAmount (liquidity * liquidity * newProd)) (unAmount oldProd) -- Unwrap, as we're combining terms oldA = unAmount oldA' diff --git a/plutus-use-cases/test/Spec/Stablecoin.hs b/plutus-use-cases/test/Spec/Stablecoin.hs index 2d93652311..6e96e0ad2c 100644 --- a/plutus-use-cases/test/Spec/Stablecoin.hs +++ b/plutus-use-cases/test/Spec/Stablecoin.hs @@ -15,7 +15,7 @@ module Spec.Stablecoin( import Control.Lens (preview) import Control.Monad (void) import Data.Maybe (listToMaybe, mapMaybe) -import Prelude hiding (negate) +import Prelude hiding (Rational, negate) import Ledger.Ada (adaSymbol, adaToken) import Ledger.Ada qualified as Ada @@ -36,7 +36,7 @@ import Plutus.Trace.Emulator (ContractHandle, EmulatorTrace) import Plutus.Trace.Emulator qualified as Trace import Plutus.Trace.Emulator.Types (_ContractLog, cilMessage) import PlutusTx.Numeric (negate, one, zero) -import PlutusTx.Ratio as Ratio +import PlutusTx.Ratio qualified as R import Wallet.Emulator.MultiAgent (eteEvent) import Test.Tasty @@ -47,15 +47,15 @@ user = w1 oraclePrivateKey :: PaymentPrivateKey oraclePrivateKey = CW.paymentPrivateKey $ CW.fromWalletNumber $ CW.WalletNumber 2 -onePercent :: Ratio Integer -onePercent = 1 % 100 +onePercent :: R.Rational +onePercent = R.unsafeRatio 1 100 coin :: Stablecoin coin = Stablecoin { scOracle = PaymentPubKey $ toPublicKey (unPaymentPrivateKey oraclePrivateKey) , scFee = onePercent , scMinReserveRatio = zero - , scMaxReserveRatio = 4 % 1 + , scMaxReserveRatio = R.unsafeRatio 4 1 , scReservecoinDefaultPrice = BC 1 , scBaseCurrency = Value.assetClass adaSymbol adaToken , scStablecoinTokenName = "stablecoin" @@ -101,7 +101,7 @@ tests = testGroup "Stablecoin" ) stablecoinTrace - , let expectedLogMsg = "New state is invalid: MaxReserves {allowed = BC {unBC = (20000000 % 1)}, actual = BC {unBC = (20173235 % 1)}}. The transition is not allowed." in + , let expectedLogMsg = "New state is invalid: MaxReserves {allowed = BC {unBC = Rational 20000000 1}, actual = BC {unBC = Rational 20173235 1}}. The transition is not allowed." in checkPredicate "Cannot exceed the maximum reserve ratio" (valueAtAddress stablecoinAddress (== (initialDeposit <> initialFee <> Ada.lovelaceValueOf 5_050_000)) .&&. assertNoFailedTransactions @@ -156,7 +156,7 @@ stablecoinTrace = do mintReserveCoins (RC 10_000_000) one hdl mintStableCoins (SC 5_000_000) one hdl -- redeem 2M stablecoins at an exchange rate of 2 Ada : 1 USD (so we get 4 Ada from the bank) - redeemStableCoins (SC 2_000_000) (Ratio.fromInteger 2) hdl + redeemStableCoins (SC 2_000_000) (R.fromInteger 2) hdl -- | Mint 100 reserve coins, mint 50 stablecoins, then attempt to mint -- another 49 reserve coins. This fails because the max. reserve ratio