Contract Architecture & Patterns
File structure
plutus/
├── core/
│ ├── VaultFactory.hs # Creates vault instances
│ ├── VaultRegistry.hs # Global vault tracking
│ ├── FractionalizedVault.hs # Core vault functionality
│ └── VaultValidator.hs # Main validation logic
├── governance/
│ ├── ProposalValidator.hs # Governance validation
│ ├── ExecutorValidator.hs # Proposal execution
│ └── VotingValidator.hs # Vote handling
├── tokens/
│ ├── FractionalPolicy.hs # Fractional token minting
│ └── GovernancePolicy.hs # Governance token minting
├── windows/
│ ├── AssetWindowValidator.hs # Asset contribution period
│ └── InvestmentWindowValidator.hs # Investment period
├── types/
│ ├── VaultTypes.hs # Core type definitions
│ ├── GovernanceTypes.hs # Governance related types
│ └── WindowTypes.hs # Window related types
└── utils/
├── Validators.hs # Common validation functions
└── Scripts.hs # Script utilities
Core Contracts
VaultFactory
data VaultParams = VaultParams
{ vaultType :: VaultType
, assetTypes :: [AssetType]
, settings :: VaultSettings
}
newtype VaultFactory = VaultFactory
{ createVault :: VaultParams -> Contract w s Text VaultId
}
mkVaultValidator :: VaultParams -> TypedValidator VaultSchema
mkVaultValidator params = mkTypedValidator @VaultSchema
($$(PlutusTx.compile [|| validateVault ||])
`PlutusTx.applyCode` PlutusTx.liftCode params)
$$(PlutusTx.compile [|| wrap ||])
where
wrap = wrapValidator @VaultDatum @VaultRedeemer
VaultRegistry
data RegistryDatum = RegistryDatum
{ vaults :: Map VaultId VaultInfo
, totalVaults :: Integer
}
data VaultInfo = VaultInfo
{ vaultState :: VaultState
, assetIds :: [AssetId]
, tokenPolicy :: CurrencySymbol
}
mkRegistryValidator :: TypedValidator RegistrySchema
mkRegistryValidator = mkTypedValidator @RegistrySchema
$$(PlutusTx.compile [|| validateRegistry ||])
$$(PlutusTx.compile [|| wrap ||])
where
wrap = wrapValidator @RegistryDatum @RegistryRedeemer
FractionalizedVault
data VaultDatum = VaultDatum
{ assets :: [AssetDetails]
, fractionalization :: FractionalizationParams
, state :: VaultState
}
data AssetDetails = AssetDetails
{ assetId :: AssetId
, amount :: Integer
, locked :: Bool
}
mkVaultScript :: VaultParams -> Scripts.TypedValidator VaultSchema
mkVaultScript params = Scripts.mkTypedValidator @VaultSchema
($$(PlutusTx.compile [|| validateVault ||])
`PlutusTx.applyCode` PlutusTx.liftCode params)
$$(PlutusTx.compile [|| wrap ||])
where
wrap = Scripts.wrapValidator @VaultDatum @VaultRedeemer
Governance Module
data ProposalDatum = ProposalDatum
{ proposalId :: Integer
, description :: BuiltinByteString
, startTime :: POSIXTime
, endTime :: POSIXTime
, votes :: Map PubKeyHash Integer
, status :: ProposalStatus
}
data VoteRedeemer =
Vote Integer |
Execute |
Cancel
mkGovernanceValidator :: TypedValidator GovernanceSchema
mkGovernanceValidator = mkTypedValidator @GovernanceSchema
$$(PlutusTx.compile [|| validateGovernance ||])
$$(PlutusTx.compile [|| wrap ||])
where
wrap = wrapValidator @ProposalDatum @VoteRedeemer
Token Minting
data FractionalTokenParams = FractionalTokenParams
{ tokenName :: TokenName
, decimals :: Integer
, totalSupply :: Integer
}
mkTokenPolicy :: FractionalTokenParams -> MintingPolicy
mkTokenPolicy params = mkMintingPolicyScript
($$(PlutusTx.compile [|| validateMinting ||])
`PlutusTx.applyCode` PlutusTx.liftCode params)
PlutusTx.makeLift ''FractionalTokenParams
Asset Window
data WindowDatum = WindowDatum
{ windowStart :: POSIXTime
, windowEnd :: POSIXTime
, contributions :: Map AssetId Contribution
}
data WindowRedeemer =
Contribute AssetContribution |
CloseWindow
mkWindowValidator :: TypedValidator WindowSchema
mkWindowValidator = mkTypedValidator @WindowSchema
$$(PlutusTx.compile [|| validateWindow ||])
$$(PlutusTx.compile [|| wrap ||])
where
wrap = wrapValidator @WindowDatum @WindowRedeemer
Example Validator Logic
validateVault :: VaultParams -> VaultDatum -> VaultRedeemer -> ScriptContext -> Bool
validateVault params datum redeemer ctx =
traceIfFalse "Invalid operation" $ case redeemer of
AddAsset asset -> validateAssetAddition asset datum ctx
RemoveAsset asset -> validateAssetRemoval asset datum ctx
Lock -> validateLocking datum ctx
Unlock -> validateUnlocking datum ctx
validateGovernance :: ProposalDatum -> VoteRedeemer -> ScriptContext -> Bool
validateGovernance datum redeemer ctx =
traceIfFalse "Invalid governance action" $ case redeemer of
Vote amount -> validateVoting datum amount ctx
Execute -> validateExecution datum ctx
Cancel -> validateCancellation datum ctx
Each contract follows Cardano/Plutus best practices:
- Strong typing
- Explicit datum/redeemer structures
- Proper validator patterns
- Clear state transitions
- Efficient UTXO management