IntersectMBO / plutus

The Plutus language implementation and tools
Apache License 2.0
1.56k stars 477 forks source link

fundsAtAddressLeq -> Watch an address and return the outputs when the total is below or reached a value #3130

Closed LendaDeAfonselio closed 3 years ago

LendaDeAfonselio commented 3 years ago

Area

[] Plutus Foundation Related to the GHC plugin, Haskell-to-Plutus compiler, on-chain code [x] Plutus Application Framework Related to the Plutus application backend (PAB), emulator, Plutus libraries [] Marlowe Related to Marlowe [] Other Any other topic (Playgrounds, etc.)

Describe the feature you'd like

Hello, I was coding a smart contract where ADA would be deposited in the contract after a certain operation and I wanted to have trigger that activated when that money was withdrawn and then perform certain logic. Something like:

deposit = do
    (param1,param2) <- mapError (review _ContractError) $ endpoint @"deposit"
    -- Initialise the State Machine and deposit funds in the contract
    void $ mapError (review _ContractSMError) $  initialiseSM client (DepositedState param2) param1

   -- Wait until there are no funds in the contract...
   -- after the trigger activates the state machine changes the state of the game again 
    void $ mapError (review _ContractSMError) $ runContractStep client Stage2Input

Unfortunately there is no way that I can actively wait until there are no funds in the contract. As such I suggest that fundsAtAddressLeq and fundsAtAddressLt are added to the Plutus library.

I have seen the documentation for fundsAtAddressGt and fundsAtAddressGequnfortunately none of them are useful in the logic I want to implement

Describe alternatives you've considered

I have considered "negating" fundsAtAddressGt but I have not seen how I can achieve this. In this specific case I have also considered knowing the stored value using the state machine, once again without any results.

Additional context / screenshots

The code presented previously using this feature would look like this:

deposit = do
    (param1,param2) <- mapError (review _ContractError) $ endpoint @"deposit"
    -- Initialise the State Machine and deposit funds in the contract
    void $ mapError (review _ContractSMError) $  initialiseSM client (DepositedState param2) param1

   -- Wait until there are no funds in the contract...
   -- after the trigger activates the state machine changes the state of the game again 
   outcome <- mapError (review _ContractError) $ fundsAtAddressLeq contractAddress 0 
   void $ mapError (review _ContractSMError) $ runContractStep client Stage2Input

Any possible workaround this situation that are not storing the current value in DepositedState?

LendaDeAfonselio commented 3 years ago

Alternatively you can export fundsAtAddressCondition in the watchAddress module

ghost commented 3 years ago

Thanks for reporting!

Yeah, while the fix is not in master yet, you can copy fundsAtAddressCondition to your code. ghc will ask some language extensions and imports, but it should work.

LendaDeAfonselio commented 3 years ago

Thank you! I am currently using the playground, is there any way in which I can use fundsAtAddressCondition on the playground? Or is it impossible atm? While I am this topic, what is currently the best option for smart contracts?

  1. Playground
  2. Alpha Playground
  3. Install plutus locally
ghost commented 3 years ago
  1. Yeah, you can copy-paste fundsAtAddressCondition and use it in the local playground:
{-# LANGUAGE RankNTypes, FlexibleContexts, GADTs #-}

import           Ledger.AddressMap                 (UtxoMap)
import           Plutus.Contract.Util              (loopM)

fundsAtAddressCondition
    :: forall w s e.
       ( AsContractError e
       , HasAwaitSlot s
       , HasUtxoAt s
       )
    => (Value -> Bool)
    -> Address
    -> Contract w s e UtxoMap
fundsAtAddressCondition condition addr = loopM go () where
    go () = do
        cur <- utxoAt addr
        sl <- Contract.currentSlot
        let presentVal = foldMap (txOutValue . txOutTxOut) cur
        if condition presentVal
            then pure (Right cur)
            else awaitSlot (sl + 1) >> pure (Left ())

Maybe some language extensions or imports are missing but ghc will suggest them.

  1. It's better to use plutus or playground locally to be able to use the latest features as the code changes very often.