diff --git a/src/module/home/components/DescriptionSection.tsx b/src/module/home/components/DescriptionSection.tsx index dbd2be7..b77b02e 100644 --- a/src/module/home/components/DescriptionSection.tsx +++ b/src/module/home/components/DescriptionSection.tsx @@ -51,7 +51,7 @@ const DescriptionSection: FC = () => ( } @@ -80,7 +80,7 @@ const DescriptionSection: FC = () => ( -Do note that we can also sign the transactions in server using the `signTx` function defined in [`GeniusYield.Types.TxBody`](https://haddock.atlas-app.io/GeniusYield-Types-TxBody.html) module. +Do note that we can also sign the transactions in server using the `signGYTxBody` function defined in [`GeniusYield.Types.TxBody`](https://haddock.atlas-app.io/GeniusYield-Types-TxBody.html) module. ## Providing Data Provider @@ -137,9 +137,9 @@ We then see the use of an interesting function `withCfgProviders`. It's type is Entire code for it is available [here](https://github.com/geniusyield/atlas-examples/tree/main/bet-ref/server-lib/BetRef/Api/Context.hs) -Our endpoints would need an information for our provider, thus we have created the type for it, called `Ctx`. It's usage is made clear by function defined next, `runQuery` which in essence correspond to `ctxRunC` we saw in section on [Integration Tests](./integration-tests.mdx). Reasoning for `runTxI` & `runTxF` follows similarly. +Our endpoints would need an information for our provider, thus we have created the type for it, called `Ctx`. It's usage is made clear by function defined next, `runQuery` and `runTx`. -Note about our handling of collateral: Browser wallets usually have the option to set for collateral, in such a case wallets would create an UTxO specifically to be used as collateral and such an UTxO will be reserved, i.e., wallet won't be spending it. [CIP 40](https://cips.cardano.org/cips/cip40/) changed the properties related to collateral and therefore we can safely take even that UTxO as collateral which has large amounts of ada and it could also contain multiple assets. Therefore if there is no collateral set by browser wallet, framework is capable of choosing suitable UTxO as collateral (and also sets for return collateral & total collateral fields appropriately) and in that case it is also free to spend it, if required by transaction builder. But if however there is a 5-ada collateral set by wallet, then framework would use it as collateral and would also reserve it, i.e., it won't pick to spend it unless explicitly mentioned by transaction skeleton. Also note that, we'll use browser wallet's `getCollateral()` method to get for collateral. This method usually returns a list of ada-only UTxOs in wallet within a specific range (like in case of Nami, it is those with ada less than or equal to 50). We would send first element of this list (if exists) to backend and framework would check if the value contained in this UTxO is exactly 5 ada or not (like Nami's `getCollateral` method returns only a singleton list if collateral is set in wallet), if not, framework would ignore this (i.e., would not reserve for it) and would itself pick suitable UTxO as collateral. If however you want this to be reserved (& of course used as collateral) regardless of it's value, see the comment in call to `runGYTxMonadNodeF` in `runTxF` function. +Note about our handling of collateral: Browser wallets usually have the option to set for collateral, in such a case wallets would create an UTxO specifically to be used as collateral and such an UTxO will be reserved, i.e., wallet won't be spending it. [CIP 40](https://cips.cardano.org/cips/cip40/) changed the properties related to collateral and therefore we can safely take even that UTxO as collateral which has large amounts of ada and it could also contain multiple assets. Therefore if there is no collateral set by browser wallet, framework is capable of choosing suitable UTxO as collateral (and also sets for return collateral & total collateral fields appropriately) and in that case it is also free to spend it, if required by transaction builder. But if however there is a 5-ada collateral set by wallet, then framework would use it as collateral and would also reserve it, i.e., it won't pick to spend it unless explicitly mentioned by transaction skeleton. Also note that, we'll use browser wallet's `getCollateral()` method to get for collateral. This method usually returns a list of ada-only UTxOs in wallet within a specific range (like in case of Nami, it is those with ada less than or equal to 50). We would send first element of this list (if exists) to backend and framework would check if the value contained in this UTxO is exactly 5 ada or not (like Nami's `getCollateral` method returns only a singleton list if collateral is set in wallet), if not, framework would ignore this (i.e., would not reserve for it) and would itself pick suitable UTxO as collateral. If however you want this to be reserved (& of course used as collateral) regardless of it's value, see the comment in call to `runGYTxBuilderMonadIO` in `runTx` function. ```haskell -- | Our Context. @@ -149,39 +149,40 @@ data Ctx = Ctx } -- | To run for simple queries, the one which don't requiring building for transaction skeleton. -runQuery :: Ctx -> GYTxQueryMonadNode a -> IO a +runQuery :: Ctx -> GYTxQueryMonadIO a -> IO a runQuery ctx q = do - let nid = cfgNetworkId $ ctxCoreCfg ctx + let nid = cfgNetworkId $ ctxCoreCfg ctx providers = ctxProviders ctx - runGYTxQueryMonadNode nid providers q - --- | Wraps our skeleton under `Identity` and calls `runTxF`. -runTxI :: Ctx - -> [GYAddress] -- ^ User's used addresses. - -> GYAddress -- ^ User's change address. - -> Maybe GYTxOutRefCbor -- ^ Browser wallet's reserved collateral (if set). - -> GYTxMonadNode (GYTxSkeleton v) - -> IO GYTxBody -runTxI = coerce (runTxF @Identity) - --- | Tries to build for given skeletons wrapped under traversable structure. -runTxF :: Traversable t - => Ctx - -> [GYAddress] -- ^ User's used addresses. - -> GYAddress -- ^ User's change address. - -> Maybe GYTxOutRefCbor -- ^ Browser wallet's reserved collateral (if set). - -> GYTxMonadNode (t (GYTxSkeleton v)) - -> IO (t GYTxBody) -runTxF ctx addrs addr collateral skeleton = do - let nid = cfgNetworkId $ ctxCoreCfg ctx + runGYTxQueryMonadIO nid providers q + +-- | Tries to build for given skeleton. +runTx :: + Ctx -> + -- | User's used addresses. + [GYAddress] -> + -- | User's change address. + GYAddress -> + -- | Browser wallet's reserved collateral (if set). + Maybe GYTxOutRefCbor -> + GYTxBuilderMonadIO (GYTxSkeleton v) -> + IO GYTxBody +runTx ctx addrs addr collateral skeleton = do + let nid = cfgNetworkId $ ctxCoreCfg ctx providers = ctxProviders ctx - runGYTxMonadNodeF GYRandomImproveMultiAsset nid providers addrs addr - (collateral >>= - (\c -> Just (getTxOutRefHex c, - True -- Make this as `False` to not do 5-ada-only check for value in this given UTxO to be used as collateral. + runGYTxBuilderMonadIO + nid + providers + addrs + addr + ( collateral + >>= ( \c -> + Just + ( getTxOutRefHex c + , True -- Make this as `False` to not do 5-ada-only check for value in this given UTxO to be used as collateral. ) - ) - ) skeleton + ) + ) + (skeleton >>= buildTxBody) ``` ## Submit Endpoint diff --git a/src/pages/getting-started/operations.mdx b/src/pages/getting-started/operations.mdx index c82c343..035ad67 100644 --- a/src/pages/getting-started/operations.mdx +++ b/src/pages/getting-started/operations.mdx @@ -35,21 +35,10 @@ betRefValidator betRefParams = $$(PlutusTx.compile [|| mkBetRefValidator||]) `PlutusTx.unsafeApplyCode` PlutusTx.liftCode plcVersion100 betRefParams ``` -Note that since spending validator takes in a datum, redeemer and a script context and thus it's type signature, `PlutusTx.CompiledCode (PlutusTx.BuiltinData -> PlutusTx.BuiltinData -> ())` takes three `PlutusTx.BuiltinData`. We encapsulate it with `GYValidator` inside framework. +Inside Atlas, Plutus scripts are represented as [`GYScript (v :: PlutusVersion)`](https://haddock.atlas-app.io/GeniusYield-Types-Script.html#t:GYScript). +The mentioned [`GeniusYield.Types.Script`](https://haddock.atlas-app.io/GeniusYield-Types-Script.html) module contains a lot of helper utilities such as `scriptFromPlutus` which takes in Plutus's `PlutusTx.CompiledCode a` type to give out `GYScript v` where type variable `v` is of _kind_ `PlutusVersion` which is defined in [`GeniusYield.Types.PlutusVersion`](https://haddock.atlas-app.io/GeniusYield-Types-PlutusVersion.html) module and is used to tag plutus ledger version of our validator script[^1]. -Likewise minting policies and stake validators take in only redeemer and script context, thus having type `PlutusTx.CompiledCode (PlutusTx.BuiltinData -> PlutusTx.BuiltinData -> PlutusTx.BuiltinData -> ())` which is represented in Atlas as `GYMintingPolicy` and `GYStakeValidator` respectively. - -`GYValidator`, `GYMintingPolicy` and `GYStakeValidator` are nothing but `newtype` wrapper around `GYScript` where `GYScript` holds information of raw serialized Plutus script, [version of Plutus ledger language](https://plutus.readthedocs.io/en/latest/explanations/language-versions.html#what-are-plutus-language-versions) and script's hash. - -Thus encapsulating `GYScript` under say `GYValidator` gives us additional context regarding script's purpose. - - - Checkout [`GeniusYield.Types.Script`](https://haddock.atlas-app.io/GeniusYield-Types-Script.html) module for definitions of these types and corresponding helper utilities. - - -The mentioned `GeniusYield.Types.Script` module contains a lot of helper utilities such as `validatorFromPlutus` which takes in Plutus's `PlutusTx.CompiledCode (PlutusTx.BuiltinData -> PlutusTx.BuiltinData -> PlutusTx.BuiltinData -> ())` type to give out `GYValidator`. Though there has been slight abuse in mentioning type here as what is actually given out is `GYValidator v` where type variable `v` is of _kind_ `PlutusVersion` which is defined in [`GeniusYield.Types.PlutusVersion`](https://haddock.atlas-app.io/GeniusYield-Types-PlutusVersion.html) module and is used to tag plutus ledger version of our validator script[^1]. - -If we look at the type signature of `validatorFromPlutus`, we see: `validatorFromPlutus :: forall v. SingPlutusVersionI v => PlutusTx.CompiledCode (PlutusTx.BuiltinData -> PlutusTx.BuiltinData -> PlutusTx.BuiltinData -> ()) -> GYValidator v` where for the time being we can ignore the description of the typeclass `SingPlutusVersionI`[^1] besides noting the fact that only types (currently `'PlutusV1` & `'PlutusV2`) of kind `PlutusVersion` have an instance for it. So here, our function `validatorFromPlutus` works for all type variable `v` which have an instance of `SingPlutusVersionI` but there is no way to learn what this `v` is based solely on the input `PlutusTx.CompiledCode (PlutusTx.BuiltinData -> PlutusTx.BuiltinData -> PlutusTx.BuiltinData -> ())` and therefore, caller must specify it, either by providing type signature (of callee or caller due to type inference) or by using [visible type application](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/type_applications.html). Our first operation does make use of it but before looking at it, we need to understand about `GYTxQueryMonad`. +If we look at the type signature of `scriptFromPlutus`, we see: `scriptFromPlutus :: forall v a. SingPlutusVersionI v => PlutusTx.CompiledCode a -> GYScript v` where for the time being we can ignore the description of the typeclass `SingPlutusVersionI`[^1] besides noting the fact that only types (currently `'PlutusV1`, `'PlutusV2` & `'PlutusV3`) of kind `PlutusVersion` have an instance for it. So here, our function `scriptFromPlutus` works for all type variable `v` which have an instance of `SingPlutusVersionI` but there is no way to learn what this `v` is based solely on the input `PlutusTx.CompiledCode a` and therefore, caller must specify it, either by providing type signature (of callee or caller due to type inference) or by using [visible type application](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/type_applications.html). Our first operation does make use of it but before looking at it, we need to understand about `GYTxQueryMonad`. ### Interlude - `GYTxQueryMonad` @@ -66,7 +55,7 @@ So, if we are working inside a monad which happens to also provide an instance f In this operation, we only need to obtain network details with the help of this monad. Here is the code to obtain address (notice that we have provided multiple versions of the same code here): - Type of [`scriptAddress`](https://haddock.atlas-app.io/GeniusYield-TxBuilder-Class.html#v:scriptAddress) used below is `scriptAddress :: forall (m :: * -> *) (v :: PlutusVersion). GYTxQueryMonad m => GYValidator v -> m GYAddress`. Thus with respect to type application, the first parameter is for monad and second one is `PlutusVersion` kinded. + Type of [`scriptAddress`](https://haddock.atlas-app.io/GeniusYield-TxBuilder-Class.html#v:scriptAddress) used below is `scriptAddress :: forall (m :: * -> *) (v :: PlutusVersion). GYTxQueryMonad m => GYScript v -> m GYAddress`. Thus with respect to type application, the first parameter is for monad and second one is `PlutusVersion` kinded. Internally this function queries for network details. @@ -77,8 +66,8 @@ In this operation, we only need to obtain network details with the help of this -- A. Type is given by `scriptAddress`. -- | Validator in question, obtained after giving required parameters. - betRefValidator' :: SingPlutusVersionI v => BetRefParams -> GYValidator v - betRefValidator' brp = validatorFromPlutus $ betRefValidator brp + betRefValidator' :: SingPlutusVersionI v => BetRefParams -> GYScript v + betRefValidator' brp = scriptFromPlutus $ betRefValidator brp -- | Address of the validator, given params. betRefAddress :: (HasCallStack, GYTxQueryMonad m) => BetRefParams -> m GYAddress @@ -87,10 +76,10 @@ In this operation, we only need to obtain network details with the help of this ```haskell - -- B. Type is given by `validatorFromPlutus` using type application. + -- B. Type is given by `scriptFromPlutus` using type application. -- | Validator in question, obtained after giving required parameters. - betRefValidator' brp = validatorFromPlutus @'PlutusV2 $ betRefValidator brp + betRefValidator' brp = scriptFromPlutus @'PlutusV2 $ betRefValidator brp -- | Address of the validator, given params. betRefAddress :: (HasCallStack, GYTxQueryMonad m) => BetRefParams -> m GYAddress @@ -102,8 +91,8 @@ In this operation, we only need to obtain network details with the help of this -- C. Type is specified using signature. -- | Validator in question, obtained after giving required parameters. - betRefValidator' :: BetRefParams -> GYValidator 'PlutusV2 - betRefValidator' brp = validatorFromPlutus $ betRefValidator brp + betRefValidator' :: BetRefParams -> GYScript 'PlutusV2 + betRefValidator' brp = scriptFromPlutus $ betRefValidator brp -- | Address of the validator, given params. betRefAddress :: (HasCallStack, GYTxQueryMonad m) => BetRefParams -> m GYAddress @@ -190,7 +179,7 @@ Note that we have mentioned the value as empty for this UTxO and this is one of
Toggle Answer - Given the output address `addr :: GYAddress{:haskell}` and the Plutus V2 validator `script :: GYValidator 'PlutusV2{:haskell}`, we can write `mustHaveOutput $ GYTxOut addr mempty (Just (datumFromPlutusData (), GYTxOutDontUseInlineDatum)) (Just $ validatorToScript script){:haskell}` + Given the output address `addr :: GYAddress{:haskell}` and the Plutus V2 validator `script :: GYScript 'PlutusV2{:haskell}`, we can write `mustHaveOutput $ GYTxOut addr mempty (Just (datumFromPlutusData (), GYTxOutDontUseInlineDatum)) (Just script){:haskell}`
## Operation _3_: Placing a bet @@ -239,22 +228,24 @@ Thus, we have its definition as: ```haskell -- | Utility function to consume script UTxO. -input :: BetRefParams -> GYTxOutRef -> GYTxOutRef -> BetRefDatum -> BetRefAction -> GYTxSkeleton 'PlutusV2 +input :: BetRefParams -> GYTxOutRef -> GYTxOutRef -> BetRefDatum -> BetRefAction -> GYTxSkeleton 'PlutusV3 input brp refScript inputRef dat red = - mustHaveInput GYTxIn - { gyTxInTxOutRef = inputRef - , gyTxInWitness = GYTxInWitnessScript - (GYInReference refScript $ validatorToScript $ betRefValidator' brp) - (datumFromPlutusData dat) - (redeemerFromPlutusData red) - } + mustHaveInput + GYTxIn + { gyTxInTxOutRef = inputRef + , gyTxInWitness = + GYTxInWitnessScript + (GYInReference refScript $ betRefValidator' brp) + (datumFromPlutusData dat) + (redeemerFromPlutusData red) + } ``` In case we didn't want to use reference script, we would write `gyTxInWitness` as: ```haskell gyTxInWitness = GYTxInWitnessScript - (GYInScript (validatorToScript $ betRefValidator' brp)) + (GYInScript (betRefValidator' brp)) (datumFromPlutusData dat) (redeemerFromPlutusData red) ``` @@ -340,4 +331,24 @@ We weren't minting any tokens in our example here and thus didn't make use of `m We haven't made use of withdrawals, stake certificates and stake validators in our example. A sample illustration is provided in [this](https://github.com/geniusyield/atlas/blob/main/tests-privnet/GeniusYield/Test/Privnet/Stake.hs) privnet test. +## Monad hierarchy + +In Atlas we have hierarchy of monads with increasing level of capabilities. We already introduced `GYTxQueryMonad`. Besides it, we also have following monads. Don't worry if it appears overwhelming, they are mainly there for advanced usage and you would very rarely need to bother about their internals. + +### [`GYTxUserQueryMonad`](https://haddock.atlas-app.io/GeniusYield-TxBuilder-Class.html#t:GYTxUserQueryMonad) + +`GYTxQueryMonad` is a super-class of [`GYTxUserQueryMonad`](https://haddock.atlas-app.io/GeniusYield-TxBuilder-Class.html#t:GYTxUserQueryMonad). It is to be used for queries which have access to user's wallet details, such as change address, used addresses and so on. Please have a look at it's haddock entry [here](https://haddock.atlas-app.io/GeniusYield-TxBuilder-Class.html#t:GYTxUserQueryMonad) to see for available functions. + +### [`GYTxBuilderMonad`](https://haddock.atlas-app.io/GeniusYield-TxBuilder-Class.html#t:GYTxBuilderMonad) + +`GYTxUserQueryMonad` is a superclass of [`GYTxBuilderMonad`](https://haddock.atlas-app.io/GeniusYield-TxBuilder-Class.html#t:GYTxBuilderMonad). Purpose of `GYTxBuilderMonad` is to allow for functions related to transaction building. This however could have been part of `GYTxUserQueryMonad` but is separate only to allow for different custom transaction building strategy to be used as default. As we will soon see, in our code, we would use [`buildTxBody`](https://haddock.atlas-app.io/GeniusYield-TxBuilder-Class.html#v:buildTxBody) to build for transaction, and that would be possible only if we are working inside `GYTxBuilderMonad`. + +### [`GYTxMonad`](https://haddock.atlas-app.io/GeniusYield-TxBuilder-Class.html#t:GYTxMonad) + +`GYTxBuilderMonad` is a superclass of [`GYTxMonad`](https://haddock.atlas-app.io/GeniusYield-TxBuilder-Class.html#t:GYTxMonad). Different between `GYTxMonad` and `GYTxBuilderMonad` is that former allows for signing of transactions, thus it requires access to user's private key. The main function we would encounter from this class is [`signAndSubmitConfirmed`](https://haddock.atlas-app.io/GeniusYield-TxBuilder-Class.html#v:signAndSubmitConfirmed). + +### [`GYTxGameMonad`](https://haddock.atlas-app.io/GeniusYield-TxBuilder-Class.html#t:GYTxMonad) + +`GYTxMonad` is a superclass of [`GYTxGameMonad`](https://haddock.atlas-app.io/GeniusYield-TxBuilder-Class.html#t:GYTxMonad). This monad allows for simulating multiple users, and is mainly used for testing. We touch more on it in our [testing](./testing) section. + [^1]: This is making use of _"singletons"_ and one can read about it from the "Dependent Types" chapter (the last one) in [Thinking with Types](https://thinkingwithtypes.com/) book. diff --git a/src/pages/getting-started/smart-contract-intro.mdx b/src/pages/getting-started/smart-contract-intro.mdx index f688d33..2da9704 100644 --- a/src/pages/getting-started/smart-contract-intro.mdx +++ b/src/pages/getting-started/smart-contract-intro.mdx @@ -12,10 +12,18 @@ Let's now start by writing a smart contract that we will use to convey framework - Here we'll be writing our smart contract in Haskell but do note that we are not limited to it. You for instance could write your smart contract in any language of your choice and read the compiled CBOR using `scriptFromCBOR` function defined [here](https://github.com/geniusyield/atlas/tree/main/src/GeniusYield/Types/Script.hs) ([Operations over Contract](./operations.mdx) chapter explains about types such as `GYScript`, `PlutusVersion` which are used in this function). Similarly, there is `readScript` defined in the [same](https://github.com/geniusyield/atlas/tree/main/src/GeniusYield/Types/Script.hs) file to read from the compiled [text envelope](https://cardano-api.cardano.intersectmbo.org/cardano-api/Cardano-Api-SerialiseTextEnvelope.html#t:TextEnvelope) file. + Here we'll be writing our smart contract in Plutus-tx (aka Plinth) but do note that we are not limited to it. If you are using [Aiken](https://aiken-lang.org/), check out our [Blueprint](./../additional-features/blueprint) section to see how easily Aiken validators can be plugged into Atlas, also supporting creation of high level Haskell types corresponding to blueprint schema. + + And in general, one can read the compiled validator's CBOR using `scriptFromCBOR` function defined [here](https://github.com/geniusyield/atlas/tree/main/src/GeniusYield/Types/Script.hs) ([Operations over Contract](./operations.mdx) chapter explains about types such as `GYScript`, `PlutusVersion` which are used in this function). Similarly, there is `readScript` defined in the [same](https://github.com/geniusyield/atlas/tree/main/src/GeniusYield/Types/Script.hs) file to read from the compiled [text envelope](https://cardano-api.cardano.intersectmbo.org/cardano-api/Cardano-Api-SerialiseTextEnvelope.html#t:TextEnvelope) file. + + Note that if your validator is written in Plutarch, we recommend [Ply](https://github.com/geniusyield/ply) to read Plutarch compiled scripts. In particular, check out [this](https://github.com/geniusyield/dex-contracts-api/blob/main/geniusyield-dex-api/src/GeniusYield/Scripts/Common.hs) example where GeniusYield reads it's plutarch validators into Atlas. + +Besides Plutus scripts, Atlas also supports Simple scripts. Check out [Simple Scripts](../additional-features/simple-scripts) section to learn more! + + ## Contract Description A setting here is that we have a sport match happening and a group of friends want to bet on the number of goals scored by their favorite team in it. @@ -26,7 +34,7 @@ The smart contract code is available [here](https://github.com/geniusyield/atlas - Since the underlying version of `plutus` library we are using defaults to version 1.1.0 of plutus core, we need to explicitly set [`target-version`](https://plutus.readthedocs.io/en/latest/reference/writing-scripts/compiler-options.html) to 1.0.0, and that is why there is `ghc-options: -fplugin-opt PlutusTx.Plugin:target-version=1.0.0` in [our cabal file](https://github.com/geniusyield/atlas-examples/blob/main/bet-ref/betref.cabal). + Since the underlying version of `plutus` library we are using defaults to version 1.1.0 of plutus core, we need to explicitly set [`target-version`](https://plutus.readthedocs.io/en/latest/reference/writing-scripts/compiler-options.html) to 1.0.0 (as this example uses Plutus V2 ledger version, but Atlas also supports Plutus V3), and that is why there is `ghc-options: -fplugin-opt PlutusTx.Plugin:target-version=1.0.0` in [our cabal file](https://github.com/geniusyield/atlas-examples/blob/main/bet-ref/betref.cabal). @@ -47,7 +55,7 @@ data BetRefParams = BetRefParams , brpBetReveal :: POSIXTime -- ^ Time at which Oracle will reveal the correct match result. , brpBetStep :: Value -- ^ Each newly placed bet must be more than previous bet by `brpBetStep` amount. } -PlutusTx.makeLift ''BetRefParams +PlutusTx.unstableMakeIsData ''BetRefParams ``` ### Reference Input Datum diff --git a/src/pages/getting-started/testing.mdx b/src/pages/getting-started/testing.mdx index 4fd79bf..d439a4e 100644 --- a/src/pages/getting-started/testing.mdx +++ b/src/pages/getting-started/testing.mdx @@ -16,15 +16,14 @@ plenty of techniques and approaches. Let's focus on __levels__ at which we can perform testing: * **Testing of UPLC functions.** You may want to verify that individual functions - your validators or minting policies consist of, indeed hold some properties. This is + your validators consist of, indeed hold some properties. This is useful if your on-chain logic is convoluted and involves complex computations. This level is tightly coupled to the language you use to build your smart contracts - so you should consult the documentation. + so you should consult the respective documentation. * **Testing of individual contracts (script level).** You might want to verify that the on-chain contracts you developed behave as expected - in isolation just by calling them with hand-constructed arguments for datum, - redeemer, and script context (since they are just functions after all) + in isolation just by calling them with hand-constructed arguments (like script context) since they are just functions after all and checking the results. Here again, you can use language-specific tools. But in case your contracts are already compiled down to UPLC code, you will need a special testing framework to do that. @@ -32,16 +31,13 @@ perform testing: namely [liqwid-context-builder](https://github.com/Liqwid-Labs/liqwid-libs/tree/main/plutarch-context-builder) from Liqwid's Libs mono repo. It allows one to easily construct various transaction contexts and verify a result that a particular script evaluates. - Testing of individual contracts proved to be very fruitful for both simple - and complex applications since it discovers bugs at very early stages - even before operations are defined. * **Testing of operations (transactions).** At this level, one can execute whole operations (transactions) an application provides and verifies that they a) can run through and b) confirm that some conditions we are interested in are held. A nice thing to know about Atlas is that it allows you to reuse the code for operations - you created in the previous step [Operations over Contract](./operations.mdx). + you created in the previous step, "[Operations over Contract](./operations.mdx)". This is the level of testing we discuss in this section. You can also make a distinction between testing individual transactions and testing a flow of transactions, but in practice, it proves to be hard @@ -68,10 +64,8 @@ We will call them *ledger backends* throughout the rest of the section: It is a cluster of three `cardano-node` instances that potentially could support all Cardano features, including staking and governance. - The main downside of a privnet is that testing time is significantly bigger - (minutes not seconds even for simple tests). - So it's practically impossible to have a fresh network for every test case - but rather for the whole test suite run. + The main downside of a privnet is that testing time is significantly bigger and since spawning a testnet is a time-consuming operation, it's practically impossible to have a fresh network for every test case + but rather we prefer single testnet for the whole test suite run. @@ -82,8 +76,6 @@ We will call them *ledger backends* throughout the rest of the section: Fortunately, you can switch easily between two backends with unified testing. - TODO: add more information on how CLB/privnet differ. - Now that we know what the two backends available are, @@ -110,13 +102,15 @@ Let's run through an example test suite to get the idea and figure out its detai ## Testing placing a bet -You can find the entire code for this example [here](https://github.com/geniusyield/atlas/blob/main/tests-unified/GeniusYield/Test/Unified/BetRef/). +You can find the entire code for this example [here](https://github.com/geniusyield/atlas-examples/tree/main/bet-ref/tests). Mind you we are using [`tasty`](https://hackage.haskell.org/package/tasty) to write tests. Our objective here would be to write test cases for one of two main operations from the `bet-ref` example - namely for placing bet operation. +The test-suite given can be executed with `cabal run betref-tests` command. Do note that, in the initial setup, you would notice an initial test failure, this is due to a quirk in the workings of [`cardano-testnet`](https://github.com/IntersectMBO/cardano-node/tree/master/cardano-testnet) when spinning up private testnet and should be ignored. Note that before running this command, make sure that you have required version of `cardano-cli` & `cardano-node` installed and available inside your path, use `cabal install --package-env=$(pwd) --overwrite-policy=always cardano-cli cardano-node` from the root of the `atlas-examples` repository to achieve that. + ### Testing environment Unified testing hides implementation details specific to ledger backends under @@ -196,7 +190,7 @@ Let's start with the runner to test `placeBet` operation. We won't see anything new here. It just uses `asUser action` we just learned to run the operation. We need values of all arguments that our operation takes. We cannot know them, so we just take all of them as arguments to the runner itself, except the address as -it can be obtained using `ownAddresses` function. This function gives back all +it can be obtained using [`ownAddresses`](https://haddock.atlas-app.io/GeniusYield-TxBuilder-Class.html#v:ownAddresses) function. This function gives back all the addresses of the wallet (`User`) that we provide to `asUser`. Once we get the result of the operation, we can build, sign, and submit a transaction. Here, again, the wallet we specified to `asUser` action is used to sign it @@ -321,7 +315,7 @@ firstBetTest betUntil betReveal betStep dat bet (testWallets -> ws@Wallets{w1}) void $ runPlaceBet refScript brp dat bet Nothing w1 ``` -The code almost verbatim repeats what we just said using the function `withWalletBalancesCheckSimple`[^1]. +The code almost verbatim repeats what we just said using the function [`withWalletBalancesCheckSimple`](https://haddock.atlas-app.io/GeniusYield-Test-FeeTracker.html#v:withWalletBalancesCheckSimple)[^1]. It allows checking the change of wallet's balance with no caring about transaction and storage fees (the latter is also known as minimal ADA - the number of coins that should accompany Cardano native tokens). This convenience is possible because Atlas manages its own record of @@ -351,14 +345,6 @@ firstBetTest' = firstBetTest (valueFromLovelace 20_000_000) ``` -Use the following command to observe test's results (being granted you are inside a Nix -shell): - -```bash -$ cabal run atlas-unified-tests -- -p 'Emulator.Place bet.Placing first bet' -``` - - ### Multiple bets test Now let's write a slightly more involved test. This time we want to make sure @@ -480,13 +466,6 @@ mkMultipleBetsTest betUntil betReveal betStep bets ws = do (show vAfter) ``` -Use the following command to observe the test's results (being granted you are inside a Nix -shell): - -```bash -$ cabal run atlas-unified-tests -- -p 'Emulator.Place bet.Multiple bets' -``` - ### Writing negative tests But sometimes we want a test to fail! What happens if the newly placed bet is @@ -539,7 +518,7 @@ placeBetTests setup = testGroup "Place bet" This section concludes our journey to testing dApps with Atlas. -[^1]: If you were to have fine-grained control over balance change, use `withWalletBalancesCheck` instead. +[^1]: If you were to have fine-grained control over balance change, use [`withWalletBalancesCheck`](https://haddock.atlas-app.io/GeniusYield-Test-Utils.html#v:withWalletBalancesCheck) instead. [^2]: To convey the message better, we have a defined [`(:=)`](https://haddock.atlas-app.io/GeniusYield-Test-Utils.html#v::-61-) pattern synonym: diff --git a/src/pages/introduction.mdx b/src/pages/introduction.mdx index 6b5af24..a2553b4 100644 --- a/src/pages/introduction.mdx +++ b/src/pages/introduction.mdx @@ -10,23 +10,23 @@ Atlas is an all-in-one, Haskell-native application backend for writing off-chain Use an intuitive API to abstract away the complexity around building transactions, balancing UTxOs, and interfacing with Plutus smart contracts. ### Leverage first-class Haskell -Avoid code duplication between on-chain and off-chain code, interoperate with advanced functionalities offered by IOG's Cardano/Plutus libraries, and easily convert between Atlas and [Cardano API](https://github.com/IntersectMBO/cardano-api)/[Plutus Ledger API](https://github.com/IntersectMBO/plutus/tree/master/plutus-ledger-api) types. +Write type-safe abstractions, harnessing the power & security of Haskell. ### Utilize modular data providers Query ledger state information from remote provider such as [Maestro](https://www.gomaestro.org/dapp-platform), [Blockfrost](https://blockfrost.io/) or from your own node with the help of [Kupo](https://cardanosolutions.github.io/kupo/). You can also build and contribute your own data provider! ### Test extensively -Use Atlas' test harness to write realistic [tests](./getting-started/testing) -that can be run against an emulator or a private Cardano network. +Use Atlas' test harness to write realistic [test](./getting-started/testing) harness, +which can be run against either an emulator or a private Cardano network. Atlas offers a unified testing approach and the same test code can seamlessly execute on both an emulated mock ledger and an actual private Cardano test network. ### Stay up to date -Benefit from Cardano's latest innovations such as **Reference Inputs**, **Inline Datums** and **Reference Scripts**. Conway we are looking at you 👀. +Benefit from Cardano's latest innovations such as **Reference Inputs**, **Inline Datums**, **Reference Scripts**, governance actions and so on. ## Where to next? Work through an end-to-end example here: [Getting Started](./getting-started). - This guide tracks the latest commit of Atlas. We bring changes into Atlas, only when corresponding updates have been made into the documentation as well. + This guide tracks the Atlas version [0.6.2](https://github.com/geniusyield/atlas/releases/tag/v0.6.2).