Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion MetaLamp/nft-marketplace/cabal.project
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ source-repository-package
plutus-use-cases
quickcheck-dynamic
web-ghc
tag: v2021-11-05
tag: v2021-11-22


-- The following sections are copied from the 'plutus-apps' repository cabal.project at the revision
Expand Down
2 changes: 1 addition & 1 deletion MetaLamp/nft-marketplace/config/plutus-pab.template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ nodeServerConfig:
mscKeptBlocks: 100
mscNetworkId: "1097911063" # Testnet network ID (main net = empty string)
mscSlotConfig:
scSlotZeroTime: 1591566291000 # Wednesday, July 29, 2020 21:44:51 - shelley launch time in milliseconds
scSlotZeroTime: 1596059091000 # Wednesday, July 29, 2020 21:44:51 - shelley launch time in milliseconds
scSlotLength: 1000 # In milliseconds
mscFeeConfig:
fcConstantFee:
Expand Down
3 changes: 1 addition & 2 deletions MetaLamp/nft-marketplace/nix/pkgs/haskell/haskell.nix
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ let
inherit compiler-nix-name;

sha256map = {
"https://github.com/input-output-hk/plutus-apps.git"."v2021-11-05" = "00pv5ds99lf6lmws3a3ipsn9amg56ayc9b0wqki2gky464dm6gzr";
"https://github.com/input-output-hk/plutus-apps.git"."v2021-11-22" = "10m0gmnakjnpnzyvjs3ksfpxgjcq3pdphr4gf8v7fjhr9fjbc45n";
"https://github.com/Quid2/flat.git"."ee59880f47ab835dbd73bea0847dab7869fc20d8" = "1lrzknw765pz2j97nvv9ip3l1mcpf2zr4n56hwlz0rk7wq7ls4cm";
"https://github.com/input-output-hk/purescript-bridge.git"."366fc70b341e2633f3ad0158a577d52e1cd2b138" = "18j0rysfccbmfpbw2d1rsjkpd5h84alpsn6b5rwzdxw9h5vqi9m5";
"https://github.com/input-output-hk/servant-purescript.git"."ebea59c7bdfc0338d83fca772b9a57e28560bcde" = "0gjcq4y61kwb4w70pnswn5dp23wd13dac8d9hz84j374cm1kshsn";
Expand All @@ -34,7 +34,6 @@ let
"https://github.com/input-output-hk/cardano-node.git"."b6ca519f97a0e795611a63174687e6bb70c9f752" = "0z5lpmqc98fwg3xzpzxkfslbxdjwfyyw8bn8yq0574sf4942vqdn";
"https://github.com/input-output-hk/Win32-network"."3825d3abf75f83f406c1f7161883c438dac7277d" = "19wahfv726fa3mqajpqdqhnl9ica3xmf68i254q45iyjcpj1psqx";
"https://github.com/input-output-hk/hedgehog-extras"."edf6945007177a638fbeb8802397f3a6f4e47c14" = "0wc7qzkc7j4ns2rz562h6qrx2f8xyq7yjcb7zidnj7f6j0pcd0i9";
"https://github.com/input-output-hk/cardano-wallet"."ae7569293e94241ef6829139ec02bd91abd069df" = "1mv1dhpkdj9ridm1fvq6jc85qs6zvbp172228rq72gyawjwrgvi6";
"https://github.com/input-output-hk/cardano-addresses"."d2f86caa085402a953920c6714a0de6a50b655ec" = "0p6jbnd7ky2yf7bwb1350k8880py8dgqg39k49q02a6ij4ld01ay";
"https://github.com/input-output-hk/plutus"."3f089ccf0ca746b399c99afe51e063b0640af547" = "1nx8xmdgwmnsla4qg4k67f5md8vm3p1p9i25ndalrqdg40z90486";
"https://github.com/j-mueller/cardano-wallet"."6be73ab852c0592713dfe78218856d4a8a0ee69e" = "0rx5hvmbdv5dwb4qq39vyhisj0v75j21jbiivn3s3q9za6m6x1p4";
Expand Down
6 changes: 3 additions & 3 deletions MetaLamp/nft-marketplace/nix/sources.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
"homepage": "",
"owner": "input-output-hk",
"repo": "plutus-apps",
"rev": "v2021-11-05",
"sha256": "00pv5ds99lf6lmws3a3ipsn9amg56ayc9b0wqki2gky464dm6gzr",
"rev": "v2021-11-22",
"sha256": "10m0gmnakjnpnzyvjs3ksfpxgjcq3pdphr4gf8v7fjhr9fjbc45n",
"type": "tarball",
"url": "https://github.com/input-output-hk/plutus-apps/archive/v2021-11-05.tar.gz",
"url": "https://github.com/input-output-hk/plutus-apps/archive/v2021-11-22.tar.gz",
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
}
}
1 change: 1 addition & 0 deletions MetaLamp/nft-marketplace/plutus-starter.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ common lang
library
import: lang
exposed-modules:
Ext.Plutus.Ledger.Index
Ext.Plutus.Ledger.Time
Ext.Plutus.Ledger.Value
Ext.Plutus.PAB.Webserver.Server
Expand Down
11 changes: 11 additions & 0 deletions MetaLamp/nft-marketplace/src/Ext/Plutus/Ledger/Index.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Ext.Plutus.Ledger.Index where

import Ledger.Index (minAdaTxOut)
import Plutus.V1.Ledger.Ada (Ada, fromValue, lovelaceValueOf,
toValue)
import Plutus.V1.Ledger.Value (Value)

-- TODO: That should be configurable in future:
-- Read minUTxOValue from `testnet-shelley-genesis.json` cardano-node config
minAdaTxOutValue :: Value
minAdaTxOutValue = toValue minAdaTxOut
1 change: 1 addition & 0 deletions MetaLamp/nft-marketplace/src/Ext/Plutus/Ledger/Value.hs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{-# LANGUAGE NumericUnderscores #-}
module Ext.Plutus.Ledger.Value where

import Control.Lens (view)
Expand Down
1 change: 1 addition & 0 deletions MetaLamp/nft-marketplace/src/Plutus/Abstract/Percentage.hs
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ mkPercentage percentage@(numerator, denominator) =
if denominator /= 0 && 0 <= roundedPercentage && roundedPercentage <= 100
then pure $ Percentage percentage
else Nothing

Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import qualified Data.Aeson as J
import Data.Proxy (Proxy (..))
import Data.Text (Text)
import qualified Data.Text as T
import Ext.Plutus.Ledger.Index (minAdaTxOutValue)
import qualified GHC.Generics as Haskell
import Ledger
import Ledger.Ada (Ada (..))
Expand All @@ -31,6 +32,7 @@ import Plutus.Contract.Request (ownPubKeyHash)
import Plutus.Contract.StateMachine
import Plutus.Contracts.Currency as Currency
import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Core
import Plutus.V1.Ledger.Ada (lovelaceValueOf)
import qualified PlutusTx
import qualified PlutusTx.AssocMap as AssocMap
import PlutusTx.Prelude hiding
Expand All @@ -54,7 +56,7 @@ start StartMarketplaceParams {..} = do
saleFeePercentage <- maybe (throwError "Operator's fee value should be in [0, 100]") pure $ mkPercentage saleFee
let marketplace = Core.Marketplace pkh (Lovelace creationFee) saleFeePercentage
let client = Core.marketplaceClient marketplace
void $ mapError (T.pack . Haskell.show @SMContractError) $ runInitialise client (Core.MarketplaceDatum AssocMap.empty AssocMap.empty) mempty
void $ mapError (T.pack . Haskell.show @SMContractError) $ runInitialise client (Core.MarketplaceDatum AssocMap.empty AssocMap.empty) minAdaTxOutValue

logInfo @Haskell.String $ printf "started Marketplace %s at address %s" (Haskell.show marketplace) (Haskell.show $ Core.marketplaceAddress marketplace)
pure marketplace
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import qualified Data.Aeson as J
import Data.Monoid (Last (..))
import Data.Semigroup.Generic (GenericSemigroupMonoid (..))
import qualified Data.Text as T
import Ext.Plutus.Ledger.Index (minAdaTxOutValue)
import GHC.Generics (Generic)
import Ledger (Ada,
PubKeyHash,
Expand Down Expand Up @@ -83,7 +84,7 @@ startAuction StartAuctionParams{..} = do

_ <- handleError
(\e -> do { logError (AuctionFailed e); throwError (StateMachineContractError e) })
(SM.runInitialise client (initialState aOwner) aAsset)
(SM.runInitialise client (initialState aOwner) (aAsset + minAdaTxOutValue))

logInfo $ AuctionStarted auction
pure auction
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import Data.Aeson (FromJSON, ToJSON)
import qualified Data.Aeson as J
import Data.Monoid (Last (..))
import Data.Semigroup.Generic (GenericSemigroupMonoid (..))
import Ext.Plutus.Ledger.Index (minAdaTxOutValue)
import GHC.Generics (Generic)
import Ledger (Ada, PubKeyHash, Slot,
Value)
Expand Down Expand Up @@ -140,7 +141,7 @@ auctionTransition getAdditionalPayoutConstraints params@Auction{..} state@State{
let
additionalConstraints = getAdditionalPayoutConstraints params state
constraints =
Constraints.mustPayToPubKey highestBidder aAsset -- and the highest bidder the asset
Constraints.mustPayToPubKey highestBidder (aAsset + minAdaTxOutValue) -- and the highest bidder the asset
<> additionalConstraints
<> Constraints.mustValidateIn (Interval.from aEndTime) -- When the auction has ended,
newState = State { stateData = Finished h, stateValue = mempty }
Expand All @@ -151,7 +152,8 @@ auctionTransition getAdditionalPayoutConstraints params@Auction{..} state@State{
constraints =
Constraints.mustValidateIn (Interval.to aEndTime) -- While the auction hasn't ended,
<> Constraints.mustPayToPubKey highestBidder (Ada.toValue highestBid) -- and the highest bidder the asset
<> Constraints.mustPayToPubKey aOwner aAsset -- and the highest bidder the asset
<> Constraints.mustPayToPubKey aOwner (aAsset + minAdaTxOutValue) -- and the highest bidder the asset
-- TODO: is it okay that buyer receive additional 2ADA? Should we initially add them to the bid price?
newState = State { stateData = Canceled, stateValue = mempty }
in Just (constraints, newState)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import qualified Plutus.Contracts.NftMarketplace.OnChain.Core.Marketplace as Mar
import qualified Plutus.Contracts.Services.Sale.Core as Core
import qualified Plutus.Contracts.Services.Sale.StateMachine as Core

import Ext.Plutus.Ledger.Index (minAdaTxOutValue)
import Plutus.Contract.Request (ownPubKeyHash)
import qualified PlutusTx
import qualified PlutusTx.AssocMap as AssocMap
Expand Down Expand Up @@ -66,7 +67,7 @@ openSale OpenSaleParams {..} = do
saleOperatorFee = ospSaleFee
}
let client = Core.saleClient sale
void $ mapError (T.pack . Haskell.show @SMContractError) $ runInitialise client Core.SaleOngoing ospSaleValue
void $ mapError (T.pack . Haskell.show @SMContractError) $ runInitialise client Core.SaleOngoing (ospSaleValue + minAdaTxOutValue)

logInfo @Haskell.String $ printf "Opened Sale %s at address %s" (Haskell.show sale) (Haskell.show $ Core.saleAddress sale)
pure sale
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ module Plutus.Contracts.Services.Sale.StateMachine where
import qualified Control.Lens as Lens
import qualified Data.Aeson as J
import qualified Data.Text as T
import Ext.Plutus.Ledger.Index (minAdaTxOutValue)
import qualified GHC.Generics as Haskell
import Ledger
import qualified Ledger.Ada as Ada
Expand Down Expand Up @@ -71,18 +72,20 @@ transition :: GetAdditionalConstraints -> Sale -> State SaleDatum -> SaleRedeeme
transition additionalConstraints sale@Sale{..} state redeemer = case (stateData state, redeemer) of
(SaleOngoing, Redeem)
-> Just ( Constraints.mustBeSignedBy saleOwner <>
Constraints.mustPayToPubKey saleOwner val
Constraints.mustPayToPubKey saleOwner saleValueWithMinAdaTxOut
, State SaleClosed mempty
)
(SaleOngoing, Buy buyer) | saleValue == val
(SaleOngoing, Buy buyer) | saleValueWithMinAdaTxOut == val
-> Just ( Constraints.mustBeSignedBy buyer <>
Constraints.mustPayToPubKey buyer saleValue <>
Constraints.mustPayToPubKey buyer saleValueWithMinAdaTxOut <>
-- TODO: is it okay that buyer receive additional 2ADA? Should we initially add them to the sale price?
additionalConstraints sale
, State SaleClosed mempty
)
_ -> Nothing
where
val = stateValue state
saleValueWithMinAdaTxOut = saleValue + minAdaTxOutValue

{-# INLINABLE isFinal #-}
isFinal :: SaleDatum -> Bool
Expand Down
2 changes: 1 addition & 1 deletion MetaLamp/nft-marketplace/test/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ tests =
"All tests"
[ testGroup
"NFT Marketplace"
[Start.tests, CreateNft.tests, Bundles.tests, Sale.tests, Auction.tests]
[Start.tests, CreateNft.tests, Bundles.tests, Auction.tests, Sale.tests]
, testGroup "Abstract" [RemoteData.tests, Percentage.tests]
]
17 changes: 12 additions & 5 deletions MetaLamp/nft-marketplace/test/Marketplace/Fixtures/Script.hs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE NumericUnderscores #-}
{-# LANGUAGE OverloadedStrings #-}
module Marketplace.Fixtures.Script where

import Ledger (Address,
Expand All @@ -10,6 +10,7 @@ import Ledger.Ada (Ada (..))
import qualified Ledger.Value as V
import qualified Marketplace.Fixtures.Wallet as Fixtures
import Plutus.Abstract.Percentage (Percentage (..))
import Plutus.Abstract.PercentageInterface (calculatePercentageRounded)
import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace
import Wallet.Emulator.Types (Wallet (..),
walletPubKeyHash)
Expand All @@ -19,11 +20,17 @@ marketplace =
Marketplace.Marketplace {
Marketplace.marketplaceOperator = walletPubKeyHash Fixtures.ownerWallet,
Marketplace.marketplaceSaleFee = percentage,
Marketplace.marketplaceNFTFee = Lovelace 100000 -- 0.1 ADA
Marketplace.marketplaceNFTFee = marketplaceCreationFee
}

marketplaceCreationFee :: Ada
marketplaceCreationFee = Lovelace 2_100_000 -- 2.1 ADA (should be gte then minAdaTxOut)

percentage :: Percentage
percentage = Percentage (5, 2)
percentage = Percentage (7, 2)

roundedPercentage :: Integer -> Integer
roundedPercentage price = calculatePercentageRounded percentage price

marketplaceAddress :: Address
marketplaceAddress = Marketplace.marketplaceAddress marketplace
47 changes: 33 additions & 14 deletions MetaLamp/nft-marketplace/test/Marketplace/Spec/Auction.hs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ import Data.Void (Void)
import Ext.Plutus.Ledger.Time (Seconds (..),
addToBeginningOfTime)
import Ledger (Value)
import Ledger.Ada (lovelaceValueOf)
import Ledger.Ada (Ada (..),
lovelaceValueOf,
toValue)
import Ledger.Index (minAdaTxOut)
import qualified Ledger.Value as V
import qualified Marketplace.Fixtures as Fixtures
import qualified Marketplace.Spec.Bundles as Bundles
Expand All @@ -37,6 +40,8 @@ import qualified PlutusTx.AssocMap as AssocMap
import Test.Tasty
import qualified Utils
import Wallet.Emulator.Wallet (walletAddress)


tests :: TestTree
tests =
testGroup
Expand Down Expand Up @@ -158,10 +163,14 @@ closeLotParams :: Marketplace.CloseLotParams
closeLotParams = Marketplace.CloseLotParams {
Marketplace.clpItemId = Marketplace.UserNftId Fixtures.catTokenIpfsCid
}

highestBid :: Integer
highestBid = 75 * Fixtures.oneAdaInLovelace

bidOnAuctionParams :: Marketplace.BidOnAuctionParams
bidOnAuctionParams = Marketplace.BidOnAuctionParams {
Marketplace.boapItemId = Marketplace.UserNftId Fixtures.catTokenIpfsCid,
Marketplace.boapBid = fromInteger $ 25 * Fixtures.oneAdaInLovelace
Marketplace.boapBid = fromInteger $ 75 * Fixtures.oneAdaInLovelace
}

startAnAuctionTrace :: Trace.EmulatorTrace (Trace.ContractHandle (ContractResponse String Text Marketplace.UserContractState) Marketplace.MarketplaceUserSchema Void)
Expand Down Expand Up @@ -353,10 +362,13 @@ closeLotParamsB = Marketplace.CloseLotParams {
Marketplace.clpItemId = Marketplace.UserBundleId Fixtures.cids
}

highestBidB :: Integer
highestBidB = 95 * Fixtures.oneAdaInLovelace

bidOnAuctionParamsB :: Marketplace.BidOnAuctionParams
bidOnAuctionParamsB = Marketplace.BidOnAuctionParams {
Marketplace.boapItemId = Marketplace.UserBundleId Fixtures.cids,
Marketplace.boapBid = fromInteger $ 35 * Fixtures.oneAdaInLovelace
Marketplace.boapBid = fromInteger highestBidB
}

startAnAuctionTraceB :: Trace.EmulatorTrace (Trace.ContractHandle (ContractResponse String Text Marketplace.UserContractState) Marketplace.MarketplaceUserSchema Void)
Expand Down Expand Up @@ -495,23 +507,30 @@ buyOnAuctionValueCheckB =

marketplaceOperatorFeeCheck :: TracePredicate
marketplaceOperatorFeeCheck =
walletFundsChange Fixtures.ownerWallet $ lovelaceValueOf 725000
-- 25000000 * 2.5 /100 = 625000 - fee by complete auction
-- 100000 - fee by minting token
walletFundsChange Fixtures.ownerWallet $ toValue (Fixtures.marketplaceCreationFee + auctionFee - minAdaTxOut)
where
auctionFee = Lovelace $ Fixtures.roundedPercentage highestBid

sellerProfitWithFeeCheck :: TracePredicate
sellerProfitWithFeeCheck =
walletFundsChange Fixtures.userWallet $ lovelaceValueOf 24275000
-- 25000000 - 725000 = 24275000
walletFundsChange Fixtures.userWallet $ toValue (highestBidAda - Fixtures.marketplaceCreationFee - minAdaTxOut - auctionFee)
where
auctionFee = Lovelace $ Fixtures.roundedPercentage highestBid
highestBidAda = Lovelace highestBid

marketplaceOperatorFeeCheckB :: TracePredicate
marketplaceOperatorFeeCheckB =
walletFundsChange Fixtures.ownerWallet $ lovelaceValueOf 1175000
-- 35000000 * 2.5 /100 = 875000 - fee by complete auction
-- 100000 * 2 = 200000 - fee by minting 2 tokens
-- 100000 - fee by bundling
walletFundsChange Fixtures.ownerWallet $ toValue (totalMintingFee + totalBundlingFee + auctionFee - minAdaTxOut)
where
totalMintingFee = Fixtures.marketplaceCreationFee * 2
totalBundlingFee = Fixtures.marketplaceCreationFee
auctionFee = Lovelace $ Fixtures.roundedPercentage highestBidB

sellerProfitWithFeeCheckB :: TracePredicate
sellerProfitWithFeeCheckB =
walletFundsChange Fixtures.userWallet $ lovelaceValueOf 33825000
-- 35000000 - 1175000 = 33825000
walletFundsChange Fixtures.userWallet $ toValue (highestBidAda - totalMintingFee - totalBundlingFee - minAdaTxOut - auctionFee)
where
totalMintingFee = Fixtures.marketplaceCreationFee * 2
totalBundlingFee = Fixtures.marketplaceCreationFee
auctionFee = Lovelace $ Fixtures.roundedPercentage highestBidB
highestBidAda = Lovelace highestBidB
Loading