packages feed

morley-upgradeable-0.3: src/Lorentz/Contracts/UserUpgradeable/V2.hs

-- SPDX-FileCopyrightText: 2020 Tocqueville Group
--
-- SPDX-License-Identifier: LicenseRef-MIT-TQ

{-# OPTIONS_GHC -Wno-orphans #-}

-- | The second version of a minimal user-upgradeable ledger. This version
-- is not designed to be upgraded further — it lacks InitiateMigration and
-- MigrateMyTokens entrypoints. However, it has MigrateFrom (callable from V1),
-- and mints new tokens when a user calls V1.MigrateMyTokens. Other functions
-- (either upgradeability-related or standard Transfer/GetTotalSupply may be
-- added if deemed desirable).
--
-- Note that the naming in this module is different from
-- Lorentz.Contracts.Upgradeable: by "migration" here we mean the process
-- of transferring the value from an old contract to the new one rather than
-- applying a transformation to storage. Thus, MigrationScript here is a lambda
-- that forges an operation to migrate user's funds rather than a function
-- that upgrades storage in-place.

module Lorentz.Contracts.UserUpgradeable.V2
  ( Parameter(..)
  , Storage(..)
  , mkStorage
  , userUpgradeableContract
  ) where

import Lorentz

import Lorentz.Contracts.UserUpgradeable.Migrations (MigrationTarget)
import qualified Lorentz.Contracts.UserUpgradeable.V1 as V1

data Storage = Storage
  { ledger :: Map Address Natural
    -- ^ We use a Map instead of a BigMap to simplify the implementation a bit.
  , previousVersion :: Maybe (TAddress V1.Parameter)
  , migrationTarget :: Maybe MigrationTarget
  }
  deriving stock Generic
  deriving anyclass (IsoValue, HasAnnotation)

type instance ErrorArg "userUpgradable'unauthorizedMigrateFrom" = ()

instance CustomErrorHasDoc "userUpgradable'unauthorizedMigrateFrom" where
  customErrClass = ErrClassActionException
  customErrDocMdCause = "Unauthorized call is performed."

mkStorage :: TAddress V1.Parameter -> Storage
mkStorage prevVersion = Storage
  { ledger = mempty
  , previousVersion = Just prevVersion
  , migrationTarget = Nothing
  }

data Parameter
  = MigrateFrom (Address, Natural)
  -- ^ When called by V1, mints new tokens to Address
  | GetBalance (View Address Natural)
  -- ^ Returns the balance of a holder.
  deriving stock Generic
  deriving anyclass IsoValue

instance ParameterHasEntrypoints Parameter where
  type ParameterEntrypointsDerivation Parameter = EpdPlain

userUpgradeableContract :: Contract Parameter Storage
userUpgradeableContract = defaultContract $ do
  unpair
  caseT @Parameter
    ( #cMigrateFrom /-> checkedCoerce_ # migrateFrom
    , #cGetBalance /-> view_ $ do
        dip (toField #ledger); get; ifSome nop (push 0)
    )

-- | Mints new tokens to Address if called by V1
migrateFrom :: '[(Address, Natural), Storage] :-> '[([Operation], Storage)]
migrateFrom = do
  dip ensurePrevVersion
  dip $ getField #ledger
  unpair; swap
  dip $ do
    dup
    dip $ do dip dup; get; ifSome nop (push @Natural 0)
    swap
  stackType @('[Natural, Natural, Address, Map Address Natural, Storage])
  add; some; swap; update
  setField #ledger
  nil; pair
  where
    ensurePrevVersion :: '[Storage] :-> '[Storage]
    ensurePrevVersion = do
      getField #previousVersion; checkedCoerce_
      ifSome (sender # eq) (push False)
      if_ nop $ failCustom_ #userUpgradable'unauthorizedMigrateFrom