使用Cabal编译Plutus脚本时遇Illegal type错误求助
Plutus金库脚本编译报错:Illegal type(需启用DataKinds)
尝试用Cabal编译代币奖励的Plutus金库脚本时,执行cabal build或cabal repl始终报Illegal type错误,提示需使用DataKinds。相关代码和错误信息如下,求排查。
Treasury.hs 文件
{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE NoImplicitPrelude #-} module Stake.StackingTreasury where import Ledger hiding (singleton) import qualified Ledger.Typed.Scripts as Scripts import Ledger.Value as Value import Ledger.Ada import qualified PlutusTx import PlutusTx.Prelude hiding (Semigroup (..), unless) import Stake.StackingTypes {-# INLINEABLE mkValidator #-} mkValidator :: TreasuryParam -> WithdrawalDatum -> StackingDetails -> ScriptContext -> Bool mkValidator tp dat b ctx = traceIfFalse "Only Issuer can change Treasury" signedByIssuer || traceIfFalse "Access token missing from input" inputHasAuthToken && traceIfFalse "Access token missing from contract output" contractOutputHasAuthToken && traceIfFalse "Output Value must match StackingDetails" checkValueToStackingContract && traceIfFalse "Treasury must keep remaining tokens" treasuryGetsTokensBack && traceIfFalse "redeemer is not datum" checkReIsOutDat where info :: TxInfo info = scriptContextTxInfo ctx signedByIssuer :: Bool signedByIssuer = txSignedBy info $ tTreasuryIssuerPkh tp -- Create a list of all CurrencySymbol in tx input inVals :: [CurrencySymbol] inVals = symbols $ valueSpent info -- Check that list of CurrencySymbols includes Auth CurrencySymbol inputHasAuthToken :: Bool inputHasAuthToken = tAccessTokenPolicyId tp `elem` inVals -- The Value to be included in Stacking Contract UTXO toStackingContract :: Value toStackingContract = valueLockedBy info (bountyContractHash tp) -- Check that the Auth Token is sent to Bounty Contract UTXO contractOutputHasAuthToken :: Bool contractOutputHasAuthToken = tAccessTokenPolicyId tp `elem` symbols toStackingContract -- Check that the Value sent to Contract UTXO matches what is specified in the Redeemer -- Note: For now, we can just remember to match Treasury Redeemer to Bounty Datum -- when we build transactions checkValueToStackingContract :: Bool checkValueToStackingContract = valueOf toStackingContract (tBountyTokenPolicyId tp) (tBountyTokenName tp) >= tokenAmount b -- The UTXO input from Treasury ownInput :: TxOut ownInput = case findOwnInput ctx of Nothing -> traceError "treasury input missing" Just i -> txInInfoResolved i -- The UTXO output back to Treasury ownOutput :: TxOut ownOutput = case getContinuingOutputs ctx of [o] -> o -- There must be exactly ONE output UTXO _ -> traceError "expected exactly one treasury output" -- Values of each treasuryInputValue :: Value treasuryInputValue = txOutValue ownInput treasuryOutputValue :: Value treasuryOutputValue = txOutValue ownOutput -- Compare Values from and to Treasury to make sure that Treasury gets the right value back. treasuryGetsLovelaceBack :: Bool treasuryGetsLovelaceBack = getLovelace ( fromValue treasuryInputValue) - getLovelace ( fromValue treasuryOutputValue) <= getLovelace ( fromValue toStackingContract) treasuryGetsTokensBack :: Bool treasuryGetsTokensBack = valueOf treasuryInputValue (tBountyTokenPolicyId tp) (tBountyTokenName tp) - valueOf treasuryOutputValue (tBountyTokenPolicyId tp) (tBountyTokenName tp) <= valueOf toStackingContract (tBountyTokenPolicyId tp) (tBountyTokenName tp) getStackingDatum :: Maybe StackingDatum getStackingDatum = let os = [ o | o <- txInfoOutputs info, txOutValue o == toStackingContract ] in case os of [o] -> stackingDatum o (`findDatum` info) _ -> Nothing checkReIsOutDat :: Bool checkReIsOutDat = case getStackingDatum of Nothing -> False Just ns -> bedIssuerPkh ns == issuerPkh b && bedContributorPkh ns == contributorPkh b && bedTokenAmount ns == tokenAmount b && bedExpirationTime ns == expirationTime b -- typedValidator :: TreasuryParam -> TypedValidator TreasuryTypes -- typedValidator tp = -- mkTypedValidator @TreasuryTypes -- $$(PlutusTx.compile [||mkValidator||]) `PlutusTx.applyCode` PlutusTx.liftCode tp -- $$(PlutusTx.compile [||wrap||]) -- where -- wrap = wrapValidator @WithdrawalDatum @StackingDetails -- validator :: TreasuryParam -> Validator -- validator = validatorScript . typedValidator typedValidator :: TreasuryParam -> Scripts.TypedValidator TreasuryTypes typedValidator tp = Scripts.mkTypedValidator @TreasuryTypes ($$(PlutusTx.compile [|| mkValidator ||]) `PlutusTx.applyCode` PlutusTx.liftCode tp) $$(PlutusTx.compile [|| wrap ||]) where wrap = Scripts.wrapValidator @WithdrawalDatum @StackingDetails validator :: TreasuryParam -> Validator validator = Scripts.validatorScript . typedValidator
StackingTypes.hs 文件
{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE TypeFamilies #-} module Stake.StackingTypes ( TreasuryParam (..) , WithdrawalDatum (..) , StackingDetails (..) , StackingAction (..) , StackingParam (..) , StackingDatum (..) , TreasuryTypes , StackingTypes , stackingDatum ) where import Data.Aeson (ToJSON, FromJSON) import GHC.Generics (Generic) import Schema (ToSchema) import Ledger hiding (singleton) import Ledger.Typed.Scripts import qualified PlutusTx import PlutusTx.Prelude hiding (Semigroup (..), unless) import Prelude (Show (..)) import qualified Prelude as Pr data TreasuryParam = TreasuryParam { tAccessTokenPolicyId :: !CurrencySymbol , bountyContractHash :: !ValidatorHash , tBountyTokenPolicyId :: !CurrencySymbol , tBountyTokenName :: !TokenName , tTreasuryIssuerPkh :: !PubKeyHash } deriving (Pr.Eq, Pr.Ord, Show, Generic, ToJSON, FromJSON, ToSchema) PlutusTx.makeLift ''TreasuryParam data WithdrawalDatum = WithdrawalDatum { bountyCount :: !Integer , treasuryKey :: !PubKeyHash } deriving (Pr.Eq, Pr.Ord, Show, Generic, ToJSON, FromJSON, ToSchema) PlutusTx.unstableMakeIsData ''WithdrawalDatum -- Completion Status - not 0 or 1, but partial -- Completion status as, for example a % of what is completed? data StackingDetails = StackingDetails { issuerPkh :: !PubKeyHash , contributorPkh :: !PubKeyHash , tokenAmount :: !Integer , expirationTime :: !POSIXTime } deriving (Pr.Eq, Pr.Ord, Show, Generic, ToJSON, FromJSON, ToSchema) instance Eq StackingDetails where {-# INLINABLE (==) #-} StackingDetails iP cP tA eT == StackingDetails iP' cP' tA' eT' = (iP == iP') && (cP == cP') && (tA == tA') && (eT == eT') -- Alternative way of comparisons -- a == b = (issuerPkh a == issuerPkh b) && -- (contributorPkh a == contributorPkh b) && -- (lovelaceAmount a == lovelaceAmount b) && -- (expirationTime a == expirationTime b) PlutusTx.unstableMakeIsData ''StackingDetails PlutusTx.makeLift ''StackingDetails data TreasuryTypes instance ValidatorTypes TreasuryTypes where type DatumType TreasuryTypes = WithdrawalDatum type RedeemerType TreasuryTypes = StackingDetails -- BountyEscrow -- INLINABLE to use On Chain {-# INLINABLE stackingDatum #-} stackingDatum :: TxOut -> (DatumHash -> Maybe Datum) -> Maybe StackingDatum stackingDatum o f = do dh <- txOutDatum o Datum d <- f dh PlutusTx.fromBuiltinData d data StackingDatum = StackingDatum { bedIssuerPkh :: !PubKeyHash , bedContributorPkh :: !PubKeyHash , bedTokenAmount :: !Integer , bedExpirationTime :: !POSIXTime } deriving (Pr.Eq, Pr.Ord, Show, Generic, ToJSON, FromJSON, ToSchema) instance Eq StackingDatum where {-# INLINABLE (==) #-} StackingDatum bIP bCP bTA bET == StackingDatum bIP' bCP' bTA' bET' = (bIP == bIP') && (bCP == bCP') && (bTA == bTA') && (bET == bET') -- Alternative way of comparisons -- a == b = (bedIssuerPkh a == bedIssuerPkh b) && -- (bedContributorPkh a == bedContributorPkh b) && -- (bedLovelaceAmount a == bedLovelaceAmount b) && -- (bedTokenAmount a == bedTokenAmount b) && -- (bedExpirationTime a == bedExpirationTime b) PlutusTx.unstableMakeIsData ''StackingDatum PlutusTx.makeLift ''StackingDatum data StackingParam = StackingParam { bountyTokenPolicyId :: !CurrencySymbol , bountyTokenName :: !TokenName , accessTokenPolicyId :: !CurrencySymbol , treasuryIssuerPkh :: !PubKeyHash } deriving (Pr.Eq, Pr.Ord, Show, Generic, ToJSON, FromJSON, ToSchema) PlutusTx.makeLift ''StackingParam data StackingAction = Cancel BuiltinByteString | Claim deriving Show PlutusTx.makeIsDataIndexed ''StackingAction [('Cancel, 0), ('Claim, 1) ] PlutusTx.makeLift ''StackingAction data StackingTypes instance ValidatorTypes StackingTypes where type DatumType StackingTypes = StackingDatum type RedeemerType StackingTypes = StackingAction
错误信息
src/Stake/StackingTreasury.hs:115:9: error: • Illegal type: ‘"stacking-contract-1.0.0.0-inplace:Stake.StackingTreasury:(115,9)-(115,45)"’ Perhaps you intended to use DataKinds • In the result of the splice: $PlutusTx.compile template-haskell-2.16.0.0:Language.Haskell.TH.Syntax.unsafeTExpCoerce [|| mkValidator ||] To see what the splice expanded to, use -ddump-splices In the Template Haskell splice $$(PlutusTx.compile [|| mkValidator ||]) In the first argument of ‘PlutusTx.applyCode’, namely ‘$$(PlutusTx.compile [|| mkValidator ||])’ | 115 | ($$(PlutusTx.compile [|| mkValidator ||]) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
解决方案
1. 为Treasury.hs启用DataKinds扩展
虽然StackingTypes.hs已经开启了DataKinds,但Treasury.hs作为调用模块,同样需要启用该扩展——因为它使用了TreasuryTypes这个类型族实例,而类型族的解析依赖DataKinds。在Treasury.hs的语言扩展列表顶部添加:
{-# LANGUAGE DataKinds #-}
2. 调整模板Haskell编译写法(避免类型拼接歧义)
原代码中用applyCode传递参数的方式可能引发模板Haskell的类型推导问题,建议改为在编译的lambda中直接绑定参数:
typedValidator :: TreasuryParam -> Scripts.TypedValidator TreasuryTypes typedValidator tp = Scripts.mkTypedValidator @TreasuryTypes $$(PlutusTx.compile [|| \tp' -> mkValidator tp' ||]) $$(PlutusTx.compile [|| Scripts.wrapValidator @WithdrawalDatum @StackingDetails ||])
这种写法更直观,也能避免模板Haskell在拼接代码时的类型错误。
3. 验证类型一致性
确认mkValidator的类型与TreasuryTypes定义的类型完全匹配:
TreasuryTypes的DatumType是WithdrawalDatum,RedeemerType是StackingDetailsmkValidator的签名是TreasuryParam -> WithdrawalDatum -> StackingDetails -> ScriptContext -> Bool,两者完全匹配,这部分无需修改,但编译前需确认无隐性类型冲突。
4. 检查Plutus依赖版本
确保项目中plutus-core、plutus-tx、plutus-ledger-api等依赖的版本与官方Plutus模板一致,版本不匹配常导致模板Haskell编译时的奇怪类型错误。
内容的提问来源于stack exchange,提问作者Hari Krishna
相关产品推荐
相关产品推荐

