Skip to main content

Contract Architecture & Patterns

Technical Implementation Guide

Overview

This documentation outlines the implementation of a real-world asset tokenization system on Cardano, utilizing the Agora protocol for governance. The system enables the creation of fractionalized ownership of physical assets through secure smart contracts, with built-in governance mechanisms for collective decision-making.

The implementation combines vault management for asset handling, Agora's three-phase governance system, token policies for both fractional ownership and governance rights, and managed contribution windows. All components work together to create a secure, compliant, and flexible tokenization platform.

System Architecture

File structure

Structure
plutus/
├── core/
│   ├── VaultFactory.hs        # Creates vault instances
│   ├── VaultRegistry.hs       # Global vault tracking
│   ├── FractionalizedVault.hs # Core vault functionality
│   └── VaultValidator.hs      # Main validation logic
├── governance/agora/
│   ├── ProposalValidator.hs   # GovernanceProposal validation logic
│   ├── ExecutorValidator.LockPhase.hs           # ProposalLock executionphase implementation
│   ├── VotingPhase.hs         # Voting phase logic
│   ├── ExecutionPhase.hs      # Execution phase handling
│   └── VotingValidator.ThresholdValidator.hs  # VoteGovernance handlingthresholds
├── tokens/
│   ├── FractionalPolicy.hs    # Fractional token minting
│   └── GovernancePolicy.hs    # GovernanceAgora governance token minting
├── windows/
│   ├── AssetWindowValidator.hs     # Asset contribution period
│   └── InvestmentWindowValidator.hs # Investment period
├── types/
│   ├── VaultTypes.hs          # Core type definitions
│   ├── GovernanceTypes.AgoraTypes.hs          # GovernanceAgora relatedgovernance types 
│   └── WindowTypes.hs         # Window related types
├── effects/
│   ├── ProposalEffects.hs     # Proposal action execution
│   └── RequirementEffects.hs  # Requirement modifications
└── utils/
    ├── Validators.hs          # Common validation functions
    └── Scripts.hs             # Script utilities

Core ContractsVault Components

The vault system manages the fundamental asset tokenization functionality.

VaultFactoryVault Factory

Responsible for creating new vault instances with proper initialization:

data VaultParams = VaultParams {
    vaultType :: 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

VaultRegistryVault Registry

Maintains the global state of all vaults:

data RegistryDatum = RegistryDatum {
    vaults :: Map VaultId VaultInfo
    ,VaultInfo,
    totalVaults :: Integer
}

data VaultInfo = VaultInfo {
    vaultState :: VaultState
    ,VaultState,
    assetIds :: [AssetId]
    ,
    tokenPolicy :: CurrencySymbol
}
mkRegistryValidator :: TypedValidator RegistrySchema
mkRegistryValidator = mkTypedValidator @RegistrySchema
    $$(PlutusTx.compile [|| validateRegistry ||])
    $$(PlutusTx.compile [|| wrap ||])
  where
    wrap = wrapValidator @RegistryDatum @RegistryRedeemer

FractionalizedVaultFractionalized Vault

Manages the core vault operations and state:

data VaultDatum = VaultDatum {
    assets :: [AssetDetails]
    ,
    fractionalization :: FractionalizationParams
    ,FractionalizationParams,
    state :: VaultState
}

data AssetDetails = AssetDetails {
    assetId :: AssetId
    ,AssetId,
    amount :: Integer
    ,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

Agora Governance ModuleIntegration

The Agora protocol provides a structured governance system with three distinct phases.

Core Governance Structures

data ProposalDatumGovernanceSettings = ProposalDatumGovernanceSettings {
    creationThreshold :: Percentage,    -- Min FT % to create proposal
    startThreshold :: Percentage,       -- Min FT % to start voting
    voteThreshold :: Percentage,        -- Min staked FT for valid vote
    executionThreshold :: Percentage,   -- Min votes for execution
    cosigningThreshold :: Percentage,   -- Min FT for cosigning
    lockDuration :: POSIXTime          -- Lock phase duration
}

data ProposalPhase = 
    LockPhase
  | VotingPhase
  | ExecutionPhase

Proposal Management

data ProposalParams = ProposalParams {
    description :: Text,
    votingDuration :: POSIXTime,
    lockDuration :: POSIXTime,
    executionDuration :: POSIXTime,
    requiredCosigners :: [PubKeyHash],
    effects :: [ProposalEffect]
}

createProposal :: VaultId -> ProposalParams -> Contract w s Text ProposalId
createProposal vaultId params = do
    validateCreatorStake
    proposalId <- submitTx $ mustPayToScript proposalValidator (proposalDatum params)
    emitEvent $ ProposalCreated proposalId
    pure proposalId

Phase Transitions

data PhaseTransition = 
    StartVoting
  | StartLockPhase
  | StartExecution

validatePhaseTransition :: ProposalDatum -> PhaseTransition -> ScriptContext -> Bool
validatePhaseTransition datum transition ctx = case transition of
    StartVoting ->
        meetStartThreshold &&
        timeToVote
    StartLockPhase ->
        votingComplete &&
        sufficientVotes
    StartExecution ->
        lockPhaseComplete &&
        cosignersSigned

Token Management

Governance Token

data GovernanceToken = GovernanceToken {
    policyId :: CurrencySymbol,
    tokenName :: TokenName,
    totalSupply :: Integer
,}

descriptionvalidateGovernanceToken :: BuiltinByteStringAssetParams ,-> startTimeGovernanceToken ::-> POSIXTimeScriptContext ,-> endTimeBool
::validateGovernanceToken POSIXTimeparams ,token votes :: Map PubKeyHash Integer
    , status :: ProposalStatus
    }

data VoteRedeemerctx =
    VotecorrectSupply Integer&&
    |correctDistribution Execute&&
    |
    Cancel

mkGovernanceValidator :: TypedValidator GovernanceSchema
mkGovernanceValidator = mkTypedValidator @GovernanceSchema
    $$(PlutusTx.compile [|| validateGovernance ||])
    $$(PlutusTx.compile [|| wrap ||])
  where
    wrap = wrapValidator @ProposalDatum @VoteRedeemerhasGovernanceMetadata

Fractional Token Minting

data FractionalTokenParams = FractionalTokenParams {
    tokenName :: TokenName
    ,TokenName,
    decimals :: Integer
    ,Integer,
    totalSupply :: Integer
}

mkTokenPolicy :: FractionalTokenParams -> MintingPolicy
mkTokenPolicy params = mkMintingPolicyScript
    ($$(PlutusTx.compile [|| validateMinting ||])
        `PlutusTx.applyCode` PlutusTx.liftCode params)
PlutusTx.makeLift ''FractionalTokenParams

Window Management

Asset Window

data WindowDatum = WindowDatum {
    windowStart :: POSIXTime
    ,POSIXTime,
    windowEnd :: POSIXTime
    ,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

ExampleTypes 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

Other filesSystem

ValidatorsVault Types

VaultValidator.hs

  • Main validation logic for vault operations
  • Handles state transitions
  • Validates transaction constraints
  • Ensures proper authorization
  • Coordinates with other validators

Validators.hs

  • Shared validation utilities
  • Common constraint checks
  • Helper functions for script contexts
  • Reusable validation patterns

Scripts.hs

  • Script compilation utilities
  • Script optimization functions
  • On-chain code helpers
  • Parameterization utilities

Types Files

VaultTypes.hs

data VaultState = 
    Draft
  | Active 
  | Locked
  | Terminated

data VaultParams = VaultParams {
    vaultType :: VaultType,
    assetTypes :: [AssetType],
    contributorPolicy :: PolicyId,
    investorPolicy :: PolicyId,
    settings :: VaultSettings
}

data VaultAction =
    Initialize VaultParams
  | AddAsset AssetDetails
  | RemoveAsset AssetId  
  | LockAssets
  | UnlockAssets

GovernanceTypes.hs

Governance Types

data ProposalState =
    Pending
  | VotingActive
  | Locked
  | Executed
  | Failed

data VoteType = 
    Approve 
  | Reject

data ProposalAction =
    CreateProposal ProposalParams
  | CastVote VoteDetails
  | ExecuteProposal
  | CancelProposal

Implementation Notes

Best Practices

WindowTypes.hsThe implementation adheres to several key principles:

data
    WindowState
  • Strong =typing NotStartedensures |that Activeinvalid |states Completedare |unrepresentable Failedin datathe WindowParamstype =system. WindowParamsEvery {operation startTimehas ::explicit POSIXTime,type durationdefinitions ::that Integer,capture allowedAssetsits ::requirements [AssetId],and minimumValueconstraints.
  • ::
  • Explicit Integerdatum }and
redeemer

Instructures conclusion

clearly define the state transitions and valid operations. Each validator precisely specifies what constitutes a valid state change.
  • The UTXO model is used efficiently, with careful consideration given to datum design and state management. This helps minimize resource usage and transaction costs.
  • Validator patterns follow established security practices, with comprehensive checks and clear error messages. Multiple validation layers ensure system integrity.
  • Security Considerations

    EachThe contractimplementation followsincludes Cardano/Plutusseveral bestsecurity practices:measures:

    1. Multiple validation layers verify all operations
    2. Strong typing
    3. Explicitprevents datum/redeemer structures
    4. Proper validator patterns
    5. Clearinvalid state transitions
    6. EfficientExplicit UTXOaccess managementcontrol through stake-based governance
    7. Comprehensive audit trail of all operations
    8. Time-locked phases prevent rushed decisions

    Integration Points

    The system's main integration points are:

    1. Asset registration and verification
    2. Governance token distribution
    3. Proposal creation and execution
    4. Time Window management and transitions
    5. Effect implementation and validation