Skip to content

Commit

Permalink
[consolidation] add MultiAsset
Browse files Browse the repository at this point in the history
  • Loading branch information
devfull committed Jul 5, 2022
1 parent ea08894 commit 1351b1d
Show file tree
Hide file tree
Showing 9 changed files with 324 additions and 1 deletion.
31 changes: 31 additions & 0 deletions src/Tokenomia/Common/Arbitrary/AssetClass.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{-# OPTIONS_GHC -Wno-orphans #-}

module Tokenomia.Common.Arbitrary.AssetClass
() where

import Plutus.V1.Ledger.Value
( AssetClass(..)
, CurrencySymbol (..)
, TokenName (..)
)

import Test.Tasty.QuickCheck
( Arbitrary
, arbitrary
, shrink
)

import Tokenomia.Common.Arbitrary.Builtins ()


instance Arbitrary CurrencySymbol where
arbitrary = CurrencySymbol <$> arbitrary
shrink x = CurrencySymbol <$> shrink (unCurrencySymbol x)

instance Arbitrary TokenName where
arbitrary = TokenName <$> arbitrary
shrink x = TokenName <$> shrink (unTokenName x)

instance Arbitrary AssetClass where
arbitrary = AssetClass <$> arbitrary
shrink x = AssetClass <$> shrink (unAssetClass x)
22 changes: 22 additions & 0 deletions src/Tokenomia/Common/Arbitrary/Builtins.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{-# OPTIONS_GHC -Wno-orphans #-}

module Tokenomia.Common.Arbitrary.Builtins
() where

import PlutusTx.Builtins.Internal
( BuiltinByteString(..) )

import Test.QuickCheck.Instances.ByteString ()
import Test.Tasty.QuickCheck
( Arbitrary
, arbitrary
, resize
, shrink
)


instance Arbitrary BuiltinByteString where
arbitrary = BuiltinByteString <$> resize 64 arbitrary
shrink x
| x == mempty = mempty
| otherwise = pure mempty
8 changes: 8 additions & 0 deletions src/Tokenomia/Common/Arbitrary/Modifiers.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module Tokenomia.Common.Arbitrary.Modifiers
( Restricted(..)
) where


newtype Restricted a
= Restricted { getRestricted :: a }
deriving (Show, Eq )
50 changes: 50 additions & 0 deletions src/Tokenomia/Common/Arbitrary/MultiAsset.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ImportQualifiedPost #-}
{-# OPTIONS_GHC -Wno-orphans #-}

module Tokenomia.Common.Arbitrary.MultiAsset
( Restricted(..)
) where

import Data.Functor.Syntax ( (<$$>) )
import Data.Set qualified as Set ( toList )
import Data.Map.Strict qualified as Map ( fromList )

import Ledger.Value
( AssetClass
)

import Test.Tasty.QuickCheck
( Arbitrary
, Gen
, arbitrary
, getPositive
, shrink
)

import Tokenomia.Common.MultiAsset
( MultiAsset(..)
, MultiAssetFormat(..)
)

import Tokenomia.Common.Arbitrary.Modifiers ( Restricted(..) )
import Tokenomia.Common.Arbitrary.AssetClass ()


instance Arbitrary MultiAsset where
arbitrary = MultiAsset <$> arbitrary
shrink x = MultiAsset <$> shrink (unMultiAsset x)

instance Arbitrary (Restricted MultiAsset) where
arbitrary = Restricted . MultiAsset . Map.fromList <$> gen
where
gen :: Gen [(AssetClass, Integer)]
gen =
zip
<$> (Set.toList <$> arbitrary)
<*> (getPositive <$$> arbitrary)
shrink x = Restricted <$> shrink (getRestricted x)

instance Arbitrary MultiAssetFormat where
arbitrary = MultiAssetFormat <$> arbitrary
shrink x = MultiAssetFormat <$> shrink (unMultiAssetFormat x)
65 changes: 65 additions & 0 deletions src/Tokenomia/Common/Arbitrary/Value.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -Wno-orphans #-}

module Tokenomia.Common.Arbitrary.Value
( Restricted(..)
) where

import Data.Functor.Syntax ( (<$$>) )
import Data.Set qualified as Set ( toList )

import Plutus.V1.Ledger.Value
( AssetClass(..)
, Value(..)
, assetClassValue
)

import PlutusTx.AssocMap qualified as AssocMap
( Map
, fromList
, toList
)

import Tokenomia.Common.MultiAsset
( MultiAsset(..)
, FromValue(..)
, ToValue(..)
)

import Test.QuickCheck.Instances.ByteString ()
import Test.Tasty.QuickCheck
( Arbitrary
, Gen
, arbitrary
, getPositive
, shrink
)

import Tokenomia.Common.Arbitrary.Modifiers ( Restricted(..) )
import Tokenomia.Common.Arbitrary.AssetClass ()
import Tokenomia.Common.Arbitrary.MultiAsset ()


instance (Arbitrary k, Arbitrary v) => Arbitrary (AssocMap.Map k v) where
arbitrary = AssocMap.fromList <$> arbitrary
shrink x = AssocMap.fromList <$> shrink (AssocMap.toList x)

instance Arbitrary Value where
arbitrary = Value <$> arbitrary
shrink x = Value <$> shrink (getValue x)

instance Arbitrary (Restricted Value) where
arbitrary =
Restricted . mconcat
<$> uncurry assetClassValue <$$> gen
where
gen :: Gen [(AssetClass, Integer)]
gen =
zip
<$> (Set.toList <$> arbitrary)
<*> (getPositive <$$> arbitrary)
shrink x =
Restricted . toValue
<$> shrink (fromValue @MultiAsset $ getRestricted x)
92 changes: 92 additions & 0 deletions src/Tokenomia/Common/MultiAsset.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeApplications #-}

module Tokenomia.Common.MultiAsset
( MultiAsset(..)
, MultiAssetFormat(..)
, FromValue(..)
, ToValue(..)
, adjustAda
, groupByAssetClass
) where

--
-- This wrapper module allows working on multiformat values
-- with a flat representation as a map indexed by asset classes
-- instead of the default nested maps representation
-- indexed in turns by the components of the asset class tuple.
--
-- Also, the underlying representation of plutus values
-- are not true maps but lists of (key, value) pairs.
-- This module provides a way to express multiformat values
-- that can be indexed by unique asset class keys.
--
-- Therefore, the type Value holds more inhabitants than MultiAsset.
-- When duplicate keys occur in a Value, since the map is built fromList
-- only the last value for the key is retained.
-- In other words, the conversion fromValue :: Value -> MultiAssetFormat
-- can suffer loss of data.
--
-- Otherwise, for any well-formed value, there is a bijection
-- between Value and MultiAsset. The following properies holds :
-- fromValue . toValue == id
-- toValue . fromValue == id
--

import Data.List.NonEmpty ( NonEmpty, groupAllWith )
import Data.Map ( Map, fromList, keysSet, adjust, foldrWithKey )
import Data.Set ( Set )

import Ledger.Value
( AssetClass(..)
, Value(..)
, flattenValue
, singleton
)

import Tokenomia.Common.AssetClass ( adaAssetClass )


newtype MultiAsset
= MultiAsset { unMultiAsset :: Map AssetClass Integer }
deriving (Show, Eq, Ord )

newtype MultiAssetFormat
= MultiAssetFormat { unMultiAssetFormat :: Set AssetClass }
deriving (Show, Eq, Ord)

class FromValue a where
fromValue :: Value -> a

instance FromValue MultiAsset where
fromValue :: Value -> MultiAsset
fromValue value =
MultiAsset . fromList $
(\(cs, tn, a) -> (AssetClass (cs, tn), a)) <$> flattenValue value

instance FromValue MultiAssetFormat where
fromValue :: Value -> MultiAssetFormat
fromValue =
MultiAssetFormat . keysSet . unMultiAsset . fromValue

class ToValue a where
toValue :: a -> Value

instance ToValue MultiAsset where
toValue :: MultiAsset -> Value
toValue (MultiAsset xs) =
foldrWithKey
(\(AssetClass (cs, tn)) a v -> v <> singleton cs tn a)
mempty
xs

groupByAssetClass :: (a -> Value) -> [a] -> [NonEmpty a]
groupByAssetClass value = groupAllWith $ fromValue @MultiAssetFormat . value

adjustAda :: Integer -> MultiAsset -> MultiAsset
adjustAda amount =
wrap $ adjust (const amount) adaAssetClass
where
wrap f = MultiAsset . f . unMultiAsset
2 changes: 2 additions & 0 deletions test/Spec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module Main(main) where

import qualified Spec.Tokenomia.Common.Data.Function.Memoize
import qualified Spec.Tokenomia.Common.Data.Sequence.IntegerPartitions
import qualified Spec.Tokenomia.Common.MultiAsset
import Test.Tasty ( TestTree, defaultMain, testGroup )

main :: IO ()
Expand All @@ -13,4 +14,5 @@ tests :: TestTree
tests = testGroup "use cases"
[ Spec.Tokenomia.Common.Data.Function.Memoize.tests
, Spec.Tokenomia.Common.Data.Sequence.IntegerPartitions.tests
, Spec.Tokenomia.Common.MultiAsset.tests
]
44 changes: 44 additions & 0 deletions test/Spec/Tokenomia/Common/MultiAsset.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{-# LANGUAGE TypeApplications #-}

module Spec.Tokenomia.Common.MultiAsset
( tests
) where

--
-- This module tests the `MultiAsset` wrapper.
--

import Test.Tasty ( TestTree, testGroup )
import Test.Tasty.QuickCheck ( testProperty )

import Tokenomia.Common.MultiAsset
( MultiAsset(..)
, FromValue(..)
, ToValue(..)
)

import Tokenomia.Common.Arbitrary.Modifiers ( Restricted(..) )
import Tokenomia.Common.Arbitrary.MultiAsset ()
import Tokenomia.Common.Arbitrary.Value ()


tests :: TestTree
tests = testGroup "Common.MultiAsset" [ properties ]

properties :: TestTree
properties = testGroup "Properties"
[ testGroup "MultiAsset"
[ testGroup "Bijection Laws"
[ testProperty "fromValue . toValue == id"
(\restricted ->
let x = getRestricted restricted
in (fromValue . toValue @MultiAsset $ x) == x
)
, testProperty "toValue . fromValue == id"
(\restricted ->
let x = getRestricted restricted
in (toValue @MultiAsset . fromValue $ x) == x
)
]
]
]
11 changes: 10 additions & 1 deletion tokenomia.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ library
Tokenomia.Vesting.Vest
Tokenomia.Vesting.Retrieve
Tokenomia.Tokenomic.CLAP.Simulation
Tokenomia.Common.Arbitrary.AssetClass
Tokenomia.Common.Arbitrary.Builtins
Tokenomia.Common.Arbitrary.Modifiers
Tokenomia.Common.Arbitrary.MultiAsset
Tokenomia.Common.Arbitrary.Value
Tokenomia.Common.AssetClass
Tokenomia.Common.Blockfrost
Tokenomia.Common.Value
Expand All @@ -81,6 +86,7 @@ library
Tokenomia.Common.Datum
Tokenomia.Common.Address
Tokenomia.Common.Asset
Tokenomia.Common.MultiAsset
Tokenomia.Common.Hash
Tokenomia.Common.Token
Tokenomia.Common.PageNumber
Expand Down Expand Up @@ -194,7 +200,9 @@ library
streamly,
deepseq,
hashable,
hex-text
hex-text,
quickcheck-instances,
tasty-quickcheck
hs-source-dirs: src

test-suite tokenomia-tests
Expand All @@ -215,6 +223,7 @@ test-suite tokenomia-tests
Spec.Tokenomia.ICO.Funds.Validation.CardanoCLI.Plan
Spec.Tokenomia.Common.Data.Function.Memoize
Spec.Tokenomia.Common.Data.Sequence.IntegerPartitions
Spec.Tokenomia.Common.MultiAsset
build-depends:
plutus-tx -any,
plutus-tx-plugin,
Expand Down

0 comments on commit 1351b1d

Please sign in to comment.