Skip to content

Commit

Permalink
Merge branch 'amir/api-endpoints' of github.com:input-output-hk/wsc-p…
Browse files Browse the repository at this point in the history
…oc into amir/api-endpoints
  • Loading branch information
j-mueller committed Dec 19, 2024
2 parents 633f52f + 6ccace1 commit 53e4668
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 7 deletions.
21 changes: 20 additions & 1 deletion src/lib/SmartTokens/Contracts/ExampleTransferLogic.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE QualifiedDo #-}
{-# LANGUAGE UndecidableInstances #-}

module SmartTokens.Contracts.ExampleTransferLogic (
mkPermissionedTransfer,
mkFreezeAndSeizeTransfer,
BlacklistProof (..),
) where

import Plutarch.LedgerApi.V3
Expand All @@ -26,6 +29,19 @@ import Plutarch.Core.Utils
pvalidateConditions )
import Plutarch.Unsafe ( punsafeCoerce )
import SmartTokens.Types.PTokenDirectory ( PBlacklistNode, pletFieldsBlacklistNode)
import qualified PlutusTx
import Plutarch.DataRepr (DerivePConstantViaData (..))
import Plutarch.Lift (PConstantDecl, PUnsafeLiftDecl (..))

data BlacklistProof
= NonmembershipProof Integer
deriving stock (Show, Eq, Generic)
deriving anyclass (PlutusTx.ToData, PlutusTx.FromData, PlutusTx.UnsafeFromData)

deriving via
(DerivePConstantViaData BlacklistProof PBlacklistProof)
instance
(PConstantDecl BlacklistProof)

data PBlacklistProof (s :: S)
= PNonmembershipProof
Expand All @@ -42,6 +58,9 @@ data PBlacklistProof (s :: S)
instance DerivePlutusType PBlacklistProof where
type DPTStrat _ = PlutusTypeData

instance PUnsafeLiftDecl PBlacklistProof where
type PLifted PBlacklistProof = BlacklistProof

{-|
The 'mkPermissionedTransfer' is a transfer logic script that enforces that all transactions which spend the
associated programmable tokens must be signed by the specified permissioned credential.
Expand Down Expand Up @@ -162,4 +181,4 @@ mkFreezeAndSeizeTransfer = plam $ \blacklistNodeCS ctx -> P.do
pvalidateConditions
[ pisRewarding ctxF.scriptInfo
, pvalidateWitnesses # blacklistNodeCS # red # infoF.referenceInputs # txWitnesses
]
]
3 changes: 3 additions & 0 deletions src/lib/SmartTokens/LinkedList/MintDirectory.hs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ import Plutarch.Prelude (ClosedTerm, DerivePlutusType (..), Generic, PAsData,
pto, type (:-->), (#))
import Plutarch.Unsafe (punsafeCoerce)
import PlutusCore.Data qualified as PLC
import Plutarch.Lift (PConstantDecl, PUnsafeLiftDecl (..))
import Plutarch.DataRepr (DerivePConstantViaData (..), PDataFields)
import qualified PlutusTx
import PlutusLedgerApi.V3 (CurrencySymbol)
import PlutusTx qualified
import PlutusTx.Builtins.Internal qualified as BI
Expand Down
4 changes: 2 additions & 2 deletions src/lib/SmartTokens/Types/PTokenDirectory.hs
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ import SmartTokens.CodeLens (_printTerm)

data BlacklistNode =
BlacklistNode {
blnKey :: BuiltinByteString,
blnNext :: BuiltinByteString
blnKey :: Credential,
blnNext :: Credential
}
deriving stock (Show, Eq, Generic)
deriving anyclass (SOP.Generic)
Expand Down
6 changes: 3 additions & 3 deletions src/lib/Wst/Offchain/BuildTx/ProgrammableLogic.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
module Wst.Offchain.BuildTx.ProgrammableLogic
( issueProgrammableToken,
transferProgrammableToken,
seizePragrammableToken,
seizeProgrammableToken,
)
where

Expand Down Expand Up @@ -130,8 +130,8 @@ transferProgrammableToken (paramsTxIn, paramsPolId) tokenTxIn programmableTokenS
ensure that the specific issuer logic stake script witness is included in the
final transaction.
-}
seizePragrammableToken :: forall era m. (C.IsBabbageBasedEra era, MonadBlockchain era m, C.HasScriptLanguageInEra C.PlutusScriptV3 era, MonadBuildTx era m) => (C.TxIn, C.PolicyId) -> (C.TxIn, C.TxOut C.CtxTx era) -> (C.TxIn, C.TxOut C.CtxTx era) -> CurrencySymbol -> [(C.TxIn, C.InAnyCardanoEra (C.TxOut C.CtxTx))] -> m ()
seizePragrammableToken (paramsTxIn, paramsPolId) (seizingTxIn, seizingOutput) (issuerTxIn, issuerTxOut) seizingTokenSymbol directoryList = Utils.inBabbage @era $ do
seizeProgrammableToken :: forall era m. (C.IsBabbageBasedEra era, MonadBlockchain era m, C.HasScriptLanguageInEra C.PlutusScriptV3 era, MonadBuildTx era m) => (C.TxIn, C.PolicyId) -> (C.TxIn, C.TxOut C.CtxTx era) -> (C.TxIn, C.TxOut C.CtxTx era) -> CurrencySymbol -> [(C.TxIn, C.InAnyCardanoEra (C.TxOut C.CtxTx))] -> m ()
seizeProgrammableToken (paramsTxIn, paramsPolId) (seizingTxIn, seizingOutput) (issuerTxIn, issuerTxOut) seizingTokenSymbol directoryList = Utils.inBabbage @era $ do
nid <- queryNetworkId

let globalStakeScript = programmableLogicGlobalScript paramsPolId
Expand Down
113 changes: 113 additions & 0 deletions src/lib/Wst/Offchain/BuildTx/TransferLogic.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ViewPatterns #-}

module Wst.Offchain.BuildTx.TransferLogic (
transferStablecoins,
issueStablecoins,
) where

import Cardano.Api qualified as C
import Cardano.Api.Shelley qualified as C
import Convex.BuildTx (MonadBuildTx, addBtx, addReference, addScriptWithdrawal,
addStakeWitness, addWithdrawalWithTxBody,
buildScriptWitness, findIndexReference,
findIndexSpending, mintPlutus, payToAddress,
spendPlutusInlineDatum)
import Convex.CardanoApi.Lenses as L
import Convex.Class (MonadBlockchain (queryNetworkId))
import Convex.PlutusLedger.V1 (transPolicyId, unTransCredential,
unTransPolicyId)
import Convex.Scripts (fromHashableScriptData)
import Convex.Utils qualified as Utils
import Data.Either (fromRight)
import Data.Foldable (find, maximumBy)
import Data.Function (on)
import Data.Maybe (fromJust)
import PlutusLedgerApi.V3 (CurrencySymbol (..))
import PlutusTx qualified
import SmartTokens.Contracts.ExampleTransferLogic (BlacklistProof (..))
import SmartTokens.Contracts.Issuance (SmartTokenMintingAction (MintPToken, RegisterPToken))
import SmartTokens.Contracts.ProgrammableLogicBase (ProgrammableLogicGlobalRedeemer (..),
TokenProof (..))
import SmartTokens.Types.ProtocolParams
import SmartTokens.Types.PTokenDirectory (BlacklistNode (..),
DirectorySetNode (..))
import Wst.Offchain.BuildTx.DirectorySet (insertDirectoryNode)
import Wst.Offchain.BuildTx.ProgrammableLogic (issueProgrammableToken)
import Wst.Offchain.BuildTx.ProtocolParams (getProtocolParamsGlobalInline)
import Wst.Offchain.Scripts (freezeAndSezieTransferScript,
permissionedTransferScript,
programmableLogicBaseScript,
programmableLogicGlobalScript,
programmableLogicMintingScript)

issueStablecoins :: forall era m. (C.IsBabbageBasedEra era, MonadBlockchain era m, C.HasScriptLanguageInEra C.PlutusScriptV3 era, MonadBuildTx era m) => C.PaymentCredential -> C.Value -> m ()
issueStablecoins issuerLogicCred amount = Utils.inBabbage @era $ do
symbol <- issueProgrammableToken undefined undefined undefined undefined undefined
addIssueStablecoinsWitness undefined

-- TODO: create the value to be minted and the special address to send it to
let value = undefined
addr = undefined --
payToAddress value addr

addIssueStablecoinsWitness :: forall era m. (C.IsBabbageBasedEra era, MonadBlockchain era m, C.HasScriptLanguageInEra C.PlutusScriptV3 era, MonadBuildTx era m) => C.Hash C.PaymentKey -> m ()
addIssueStablecoinsWitness issuerPubKeyHash = Utils.inBabbage @era $ do
let mintingScript = permissionedTransferScript issuerPubKeyHash
sh = C.hashScript $ C.PlutusScript C.PlutusScriptV3 mintingScript
addScriptWithdrawal sh 0 $ buildScriptWitness mintingScript C.NoScriptDatumForStake ()


transferStablecoins :: forall era m. (C.IsBabbageBasedEra era, MonadBlockchain era m, C.HasScriptLanguageInEra C.PlutusScriptV3 era, MonadBuildTx era m) => C.PaymentCredential -> C.PolicyId -> [(C.TxIn, C.TxOut C.CtxTx era)] -> [(C.TxIn, C.TxOut C.CtxTx era)] -> C.Value -> C.PaymentCredential -> m ()
transferStablecoins transferLogicCred blacklistPolicyId blacklistOutputs userOutputs amount destinationCred = pure ()

addTransferWitness :: forall era m. (C.IsBabbageBasedEra era, MonadBlockchain era m, C.HasScriptLanguageInEra C.PlutusScriptV3 era, MonadBuildTx era m) => C.PolicyId -> [(C.TxIn, C.TxOut C.CtxTx era)] -> C.PaymentCredential -> m ()
addTransferWitness blacklistPolicyId blacklistNodes clientCred = Utils.inBabbage @era $ do
nid <- queryNetworkId
let transferScript = freezeAndSezieTransferScript blacklistPolicyId
transferStakeCred = C.StakeCredentialByScript $ C.hashScript $ C.PlutusScript C.PlutusScriptV3 transferScript

(blnNodeRef, blnNodeOut) =
maximumBy (compare `on` (fmap blnKey . getDatumInline @BlacklistNode . C.inAnyCardanoEra (C.cardanoEra @era) . snd)) $
filter (maybe False ((<= clientCred) . fromRight (error "could not unTrans credential") . unTransCredential . blnKey) . getDatumInline @BlacklistNode . C.inAnyCardanoEra (C.cardanoEra @era) . snd) blacklistNodes

-- Finds the index of the blacklist node reference in the transaction ref
-- inputs
blacklistNodeReferenceIndex txBody =
fromIntegral @Int @Integer $ findIndexReference blnNodeRef txBody

-- The redeemer for the transfer script based on whether a blacklist node
-- exists with the client credential
transferRedeemer txBody =
if fmap
(fromRight (error "could not unTrans credential") . unTransCredential . blnKey)
(getDatumInline @BlacklistNode $ C.inAnyCardanoEra (C.cardanoEra @era) blnNodeOut)
== Just clientCred
then error "Credential is blacklisted" -- TODO: handle this and other error cases properly
else NonmembershipProof $ blacklistNodeReferenceIndex txBody

-- TODO: extend this to handle multiple proofs (i.e. transfers) per tx, onchain allows it
transferWitness txBody = buildScriptWitness transferScript C.NoScriptDatumForStake [transferRedeemer txBody]

addReference blnNodeRef -- Add the blacklist node reference to the transaction
addWithdrawalWithTxBody -- Add the global script witness to the transaction
(C.makeStakeAddress nid transferStakeCred)
(C.Quantity 0)
$ C.ScriptWitness C.ScriptWitnessForStakeAddr . transferWitness

addSeizeWitness :: forall era m. (C.IsBabbageBasedEra era, MonadBlockchain era m, C.HasScriptLanguageInEra C.PlutusScriptV3 era, MonadBuildTx era m) => C.Hash C.PaymentKey -> m ()
addSeizeWitness issuerPubKeyHash = Utils.inBabbage @era $ do
let seizeScript = permissionedTransferScript issuerPubKeyHash
sh = C.hashScript $ C.PlutusScript C.PlutusScriptV3 seizeScript
addScriptWithdrawal sh 0 $ buildScriptWitness seizeScript C.NoScriptDatumForStake ()


-- TODO: move to separate utils module
getDatumInline :: forall a. (PlutusTx.FromData a) => C.InAnyCardanoEra (C.TxOut C.CtxTx) -> Maybe a
getDatumInline (C.InAnyCardanoEra C.ConwayEra (C.TxOut _ _ dat _)) =
case dat of
C.TxOutDatumInline C.BabbageEraOnwardsConway (fromHashableScriptData -> Just d) -> Just d
_ -> Nothing
getDatumInline _ = Nothing
22 changes: 21 additions & 1 deletion src/lib/Wst/Offchain/Scripts.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,28 @@ module Wst.Offchain.Scripts (
programmableLogicMintingScript,
programmableLogicBaseScript,
programmableLogicGlobalScript,

-- Transfer logic
permissionedTransferScript,
freezeAndSezieTransferScript,

-- Utils
scriptPolicyIdV3
)
where

import Cardano.Api qualified as C
import Cardano.Api.Shelley qualified as C
import Convex.PlutusLedger.V1 (transCredential, transPolicyId,
import Convex.PlutusLedger.V1 (transCredential, transPolicyId, transPubKeyHash,
transStakeCredential)
import Convex.PlutusLedger.V3 (transTxOutRef)
import Plutarch (ClosedTerm, Config (..), LogLevel (..), TracingMode (..), (#))
import Plutarch.Builtin (pdata, pforgetData)
import Plutarch.ByteString (PByteString)
import Plutarch.Lift (pconstant)
import Plutarch.Script (serialiseScript)
import SmartTokens.Contracts.ExampleTransferLogic (mkFreezeAndSeizeTransfer,
mkPermissionedTransfer)
import SmartTokens.Contracts.Issuance (mkProgrammableLogicMinting)
import SmartTokens.Contracts.ProgrammableLogicBase (mkProgrammableLogicBase,
mkProgrammableLogicGlobal)
Expand Down Expand Up @@ -89,6 +97,18 @@ programmableLogicGlobalScript paramsPolId =
let script = tryCompile prodConfig $ mkProgrammableLogicGlobal # pdata (pconstant $ transPolicyId paramsPolId)
in C.PlutusScriptSerialised $ serialiseScript script

permissionedTransferScript :: C.Hash C.PaymentKey -> C.PlutusScript C.PlutusScriptV3
permissionedTransferScript cred =
let script = tryCompile prodConfig $ mkPermissionedTransfer # pdata (pconstant $ transPubKeyHash cred)
in C.PlutusScriptSerialised $ serialiseScript script

freezeAndSezieTransferScript :: C.PolicyId -> C.PlutusScript C.PlutusScriptV3
freezeAndSezieTransferScript blacklistPolicyId =
-- TODO: maybe mkFreezeAndSeizeTransfer should be called mkFreezeTransfer as
-- seizing is handled separately
let script = tryCompile prodConfig $ mkFreezeAndSeizeTransfer # pdata (pconstant $ transPolicyId blacklistPolicyId)
in C.PlutusScriptSerialised $ serialiseScript script

-- Utilities
scriptPolicyIdV3 :: C.PlutusScript C.PlutusScriptV3 -> C.PolicyId
scriptPolicyIdV3 = C.scriptPolicyId . C.PlutusScript C.PlutusScriptV3
1 change: 1 addition & 0 deletions src/wst-poc.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ library
Wst.Offchain.BuildTx.LinkedList
Wst.Offchain.BuildTx.ProgrammableLogic
Wst.Offchain.BuildTx.ProtocolParams
Wst.Offchain.BuildTx.TransferLogic
Wst.Offchain.Endpoints.Deployment
Wst.Offchain.Endpoints.Env
Wst.Offchain.Scripts
Expand Down

0 comments on commit 53e4668

Please sign in to comment.