packages feed

morley-upgradeable-0.3: src/Lorentz/Contracts/UpgradeableUnsafeLedger/V1.hs

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

-- | A buggy implementation of Unsafe ledger, returns balances multiplied by 2

module Lorentz.Contracts.UpgradeableUnsafeLedger.V1
  ( UnsafeLedgerV1
  , Interface
  , migrate
  , unsafeLedgerContract

  -- The following are used in V2
  , UStoreTemplate
  , UStoreV1
  , TransferParams
  , transfer
  , getTotalSupply
  ) where

import Lorentz

import Lorentz.Contracts.Upgradeable.Common
import Lorentz.UStore

data UnsafeLedgerV1 :: VersionKind

type Interface =
  [ "transfer" ?: TransferParams
  , "getTotalSupply" ?: Void_ () Natural
  , "getBalance" ?: Void_ Address (Maybe Natural)
  ]

type TransferParams = (Address, Natural)

data UStoreTemplate = UStoreTemplate
  { ledger      :: Address |~> Natural
  , totalSupply :: UStoreField Natural
  } deriving stock (Eq, Generic)

type UStoreV1 = UStore UStoreTemplate

instance KnownContractVersion UnsafeLedgerV1 where
  type VerInterface UnsafeLedgerV1 = Interface
  type VerUStoreTemplate UnsafeLedgerV1 = UStoreTemplate
  contractVersion _ = 1

-- | Like in UpgradeableCounter, this function  populates the empty UStore_
--   with entries and initial values for each field. The result is expected
--   to adhere to V1.UStoreTemplate
migrate :: '[UStore_] :-> '[UStore_]
migrate = checkedCoercing_ @_ @UStoreV1 $ do
  push @Natural 500
  dup
  dip $ ustoreSetField #totalSupply
  sender
  ustoreInsert #ledger

unsafeLedgerContract :: UContractRouter UnsafeLedgerV1
unsafeLedgerContract = mkUContractRouter $ do
  caseUParamT @Interface
    ( #transfer /-> transfer
    , #getTotalSupply /-> getTotalSupply
    , #getBalance /-> buggyGetBalance
    )
    uparamFallbackFail

transfer :: '[TransferParams, UStoreV1]
         :-> '[([Operation], UStoreV1)]
transfer = do
  debitSource; creditTo; nil; pair;

getTotalSupply :: '[Void_ () Natural, UStoreV1]
               :-> '[([Operation], UStoreV1)]
getTotalSupply = void_ (do drop @(); ustoreToField #totalSupply)

-- Buggy getBalance returns balance multiplied by 2
buggyGetBalance :: '[Void_ Address (Maybe Natural), UStoreV1]
                :-> '[([Operation], UStoreV1)]
buggyGetBalance = void_ $ do
  ustoreGet #ledger
  if IsSome
  then push @Natural 2 >> mul >> some
  else none

debitSource :: '[TransferParams, UStoreV1]
            :-> '[TransferParams, UStoreV1]
debitSource = do
  dip $ do
    sender
    dip dup
    ustoreGet #ledger
    assertSome [mt|Sender address is not in ledger|]
  swap
  dip (dup # cdr)
  subGt0
  swap
  dip (do sender; ustoreUpdate #ledger)

creditTo :: '[TransferParams, UStoreV1] :-> '[UStoreV1]
creditTo = do
  dup; car
  swap
  dip (dip dup # ustoreGet #ledger)
  swap
  if IsSome then dip (dup >> cdr) >> add @Natural else (dup >> cdr)
  some
  dip (car)
  swap
  ustoreUpdate #ledger

subGt0 :: Natural ': Natural ': s :-> Maybe Natural ': s
subGt0 = do
  sub;
  dup; assertGe0 [mt|Transferred value is greater than balance|]
  dup; eq0
  if Holds
  then drop >> none
  else isNat