├── buy.json ├── close.json ├── .gitignore ├── hie.yaml ├── src ├── Utility.hs ├── SerialiseJSON.hs └── Market │ ├── Trace.hs │ ├── Types.hs │ ├── Onchain.hs │ └── Offchain.hs ├── README.md ├── app ├── market-plutus.hs └── datum-json.hs ├── martify.cabal ├── cabal.project ├── market_mainnet_final.plutus ├── market_testnet_final.plutus └── LICENSE /buy.json: -------------------------------------------------------------------------------- 1 | {"constructor":0,"fields":[]} -------------------------------------------------------------------------------- /close.json: -------------------------------------------------------------------------------- 1 | {"constructor":1,"fields":[]} 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | dist-* 3 | cabal-dev 4 | *.o 5 | *.hi 6 | *.hie 7 | *.chi 8 | *.chs.h 9 | *.dyn_o 10 | *.dyn_hi 11 | .hpc 12 | .hsenv 13 | .cabal-sandbox/ 14 | cabal.sandbox.config 15 | *.prof 16 | *.aux 17 | *.hp 18 | *.eventlog 19 | .stack-work/ 20 | cabal.project.local 21 | cabal.project.local~ 22 | .HTF/ 23 | .ghc.environment.* 24 | -------------------------------------------------------------------------------- /hie.yaml: -------------------------------------------------------------------------------- 1 | cradle: 2 | cabal: 3 | 4 | - path: "./src" 5 | component: "lib:martify" 6 | 7 | - path: "./app/market-plutus.hs" 8 | component: "exe:market-plutus" 9 | 10 | - path: "./app/datum-json.hs" 11 | component: "exe:datum-json" 12 | 13 | - path: "./app/update-datum-json.hs" 14 | component: "exe:update-datum-json" 15 | -------------------------------------------------------------------------------- /src/Utility.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | 3 | module Utility 4 | ( walletPubKeyHash 5 | , wallet 6 | , companyPkh 7 | , companyPkhReal 8 | , mp 9 | , mpReal 10 | , mpMainnet ) where 11 | 12 | import Plutus.V1.Ledger.Crypto (PubKeyHash) 13 | import Wallet.Emulator.Wallet (Wallet, knownWallet, walletPubKeyHash) 14 | 15 | import Prelude hiding ((.)) 16 | 17 | import Market.Types (MarketParams(..)) 18 | 19 | wallet :: Integer -> Wallet 20 | wallet = knownWallet 21 | 22 | companyPkh :: PubKeyHash 23 | companyPkh = walletPubKeyHash $ wallet 1 24 | 25 | 26 | mp :: MarketParams 27 | mp = MarketParams companyPkh 28 | 29 | 30 | 31 | companyPkhReal :: PubKeyHash 32 | companyPkhReal = "09aaedfc2c267948a623a4dddd093327c235c3fa88a47f14d41a7347" 33 | 34 | 35 | mpReal :: MarketParams 36 | mpReal = MarketParams companyPkhReal 37 | 38 | 39 | 40 | 41 | companyPkhMainnet :: PubKeyHash 42 | companyPkhMainnet = "09aaedfc2c267948a623a4dddd093327c235c3fa88a47f14d41a7347" 43 | 44 | 45 | mpMainnet :: MarketParams 46 | mpMainnet = MarketParams companyPkhMainnet -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Martify 2 | NFT Marketplace on the Cardano Blockchain. Powered by Plutus Smart Contracts 3 | A simple contract that allows sales of NFTs. The seller (owner of an NFT) constructs the contract with parameters unique to the sale and locks the NFT there. 4 | A buyer can then unlock the NFT by submitting a transaction verifying the several requirements defined in the validator. 5 | *** 6 | ### Test in emulator 7 | * Clone the repository 8 | * Run `cabal repl` in the repo 9 | * Run `:l src/Market/Trace.hs` in the repl 10 | * Run `test` 11 | * To modify the testing scenario, open and modify the `src/Market/Trace.hs` file 12 | *** 13 | 14 | 15 | ## Donations 16 | We would really appreciate financial support to our project, it helps us spend more time and effort on it. 17 | 18 | ADA Address : addr1vycmju7zumcgq5wsdmdwkxafhhuu3xeed60auwngww2fkjcr38qjm 19 | 20 | ## Contract usage 21 | The Martify contract for marketplaces is being used by several marketplaces currently live on Cardano. 22 | The list of marketplaces based on our contracts is : 23 | * https://jpg.store 24 | * https://martify.io 25 | * https://filthyrichhorses.com/marketplace 26 | -------------------------------------------------------------------------------- /app/market-plutus.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-} 2 | 3 | import Cardano.Api.Shelley 4 | ( writeFileTextEnvelope, 5 | Error(displayError), 6 | PlutusScript, 7 | PlutusScriptV1, 8 | ScriptData(ScriptDataNumber), 9 | toAlonzoData ) 10 | import Cardano.Ledger.Alonzo.Data as Alonzo ( Data(Data) ) 11 | import qualified Plutus.V1.Ledger.Api as Plutus 12 | 13 | import Prelude 14 | import System.Environment ( getArgs ) 15 | import qualified Data.ByteString.Short as SBS 16 | 17 | import Market.Onchain as O2 (apiBuyScript, buyScriptAsShortBs) 18 | import Utility (mpReal, mpMainnet) 19 | 20 | 21 | main :: IO () 22 | main = do 23 | args <- getArgs 24 | let nargs = length args 25 | let scriptnum = if nargs > 0 then read (head args) else 42 26 | let scriptname = if nargs > 1 then args!!1 else "market_mainnet_final.plutus" 27 | putStrLn $ "Writing output to: " ++ scriptname 28 | writePlutusScript scriptnum scriptname (O2.apiBuyScript mpMainnet) (O2.buyScriptAsShortBs mpMainnet) 29 | 30 | 31 | writePlutusScript :: Integer -> FilePath -> PlutusScript PlutusScriptV1 -> SBS.ShortByteString -> IO () 32 | writePlutusScript scriptnum filename scriptSerial scriptSBS = 33 | do 34 | case Plutus.defaultCostModelParams of 35 | Just m -> 36 | let Alonzo.Data pData = toAlonzoData (ScriptDataNumber scriptnum) 37 | (logout, e) = Plutus.evaluateScriptCounting Plutus.Verbose m scriptSBS [pData] 38 | in do print ("Log output" :: String) >> print logout 39 | case e of 40 | Left evalErr -> print ("Eval Error" :: String) >> print evalErr 41 | Right exbudget -> print ("Ex Budget" :: String) >> print exbudget 42 | Nothing -> error "defaultCostModelParams failed" 43 | result <- writeFileTextEnvelope filename Nothing scriptSerial 44 | case result of 45 | Left err -> print $ displayError err 46 | Right () -> return () 47 | -------------------------------------------------------------------------------- /app/datum-json.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE ImportQualifiedPost #-} 2 | {-# LANGUAGE NumericUnderscores #-} 3 | {-# LANGUAGE OverloadedStrings #-} 4 | 5 | import Data.Aeson as Json ( encode ) 6 | import Data.ByteString.Lazy qualified as LB 7 | import System.Environment ( getArgs ) 8 | import Prelude 9 | import Data.String (fromString) 10 | 11 | import Cardano.Api 12 | ( scriptDataToJson, 13 | ScriptDataJsonSchema(ScriptDataJsonDetailedSchema) ) 14 | import Cardano.Api.Shelley ( fromPlutusData ) 15 | import qualified PlutusTx 16 | 17 | import Market.Types (NFTSale(..)) 18 | 19 | -- This module is here to convert Haskell Data Types to JSON files, particularly used for NFTSale custom Datum Type. 20 | -- To use this enter `cabal run datum-json `. 21 | -- The needs to be in different format than the one usually used. 22 | -- When the marketplace is released, we will update this section with instructions on how to convert to the required format. 23 | 24 | -- Constructs the JSON file for the datum, used as input to --tx-in-datum-file in cardano-cli 25 | main :: IO () 26 | main = do 27 | [price', seller', tn' ,cs', raddr', rprct'] <- getArgs 28 | let price = read price' 29 | seller = fromString seller' 30 | tn = fromString tn' 31 | cs = fromString cs' 32 | raddr = fromString raddr' 33 | rprct = read rprct' 34 | nfts = NFTSale seller price cs tn raddr rprct 35 | writeData ("datum-" ++ show cs ++ "-" ++ tn' ++ ".json") nfts 36 | putStrLn "Done" 37 | -- Datum also needs to be passed when sending the token to the script (aka putting for sale) 38 | -- When doing this, the datum needs to be hashed, see Alonzo-purple exercise-solutions on how to hash a datum 39 | 40 | writeData :: PlutusTx.ToData a => FilePath -> a -> IO () 41 | writeData file isData = do 42 | print file 43 | LB.writeFile file (toJsonString isData) 44 | 45 | toJsonString :: PlutusTx.ToData a => a -> LB.ByteString 46 | toJsonString = 47 | Json.encode 48 | . scriptDataToJson ScriptDataJsonDetailedSchema 49 | . fromPlutusData 50 | . PlutusTx.toData 51 | -------------------------------------------------------------------------------- /src/SerialiseJSON.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE ImportQualifiedPost #-} 2 | {-# LANGUAGE NumericUnderscores #-} 3 | {-# LANGUAGE OverloadedStrings #-} 4 | 5 | module SerialiseJSON (testR, testD) where 6 | 7 | import Data.Aeson as Json ( encode ) 8 | import Data.ByteString.Lazy qualified as LB 9 | 10 | 11 | import Cardano.Api 12 | ( scriptDataToJson, 13 | ScriptDataJsonSchema(ScriptDataJsonDetailedSchema) ) 14 | import Cardano.Api.Shelley ( fromPlutusData ) 15 | import qualified PlutusTx 16 | 17 | import Market.Types (SaleAction(..), NFTSale(..)) 18 | 19 | -- This module is here to convert Haskell Data Types to JSON files, particularly used for Redeemer and Datum 20 | -- To use this enter `cabal repl` ; `:l src/SerialiseJSON.hs` ; `testR` or `testD` 21 | 22 | 23 | -- Constructs the JSON file for the Buy Redeemer constructor, used as input to --tx-in-redeemer-file 24 | testR :: IO () 25 | testR = do 26 | writeData "update.json" Close 27 | putStrLn "Done" 28 | 29 | 30 | nftEx :: NFTSale 31 | nftEx = NFTSale 32 | { nPrice = 1 33 | , nSeller = "4b4355acf5e549d4cd26408723d1c8d87a98db7598e40bb1cd3b343e" 34 | , nToken = "Vendere" 35 | , nCurrency = "8b050684e8d7e1abb2b79227f44c4c767025decb140e64efc783d046" 36 | , nRoyAddr = nSeller nftEx 37 | , nRoyPrct = 0 38 | } -- This is an example to fill with real data 39 | -- The `nSeller` needs to be in Base16 format, not Bech32 (addr1...). 40 | -- To easily get the Base16 version, go to Cardanoscan.io, search the address in format addr1... 41 | -- The address is written in two formats, the first being Bech32 aka addr1... and the other (in light gray) being in Base16 42 | 43 | -- Constructs the JSON file for the nftEx datum, used as input to --tx-in-datum-file 44 | testD :: IO () 45 | testD = do 46 | writeData "datum.json" nftEx 47 | putStrLn "Done" 48 | -- Datum also needs to be passed when sending the token to the script (aka putting for sale) 49 | -- When doing this, the datum needs to be hashed, see Alonzo-purple exercise-solutions on how to hash a datum 50 | 51 | writeData :: PlutusTx.ToData a => FilePath -> a -> IO () 52 | writeData file isData = LB.writeFile file (toJsonString isData) 53 | 54 | toJsonString :: PlutusTx.ToData a => a -> LB.ByteString 55 | toJsonString = 56 | Json.encode 57 | . scriptDataToJson ScriptDataJsonDetailedSchema 58 | . fromPlutusData 59 | . PlutusTx.toData -------------------------------------------------------------------------------- /src/Market/Trace.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TypeApplications #-} 2 | {-# LANGUAGE DataKinds #-} 3 | {-# LANGUAGE NumericUnderscores #-} 4 | {-# LANGUAGE OverloadedStrings #-} 5 | 6 | module Market.Trace 7 | ( test 8 | ) where 9 | 10 | 11 | import Plutus.Trace.Emulator as Emulator 12 | ( activateContractWallet, waitNSlots, runEmulatorTraceIO', callEndpoint, EmulatorConfig(..) ) 13 | import Control.Monad (void) 14 | import PlutusTx.Prelude as Plutus ( ($), (<>), Either(..) ) 15 | import Ledger.Value as Value (singleton) 16 | import qualified Data.Map as Map 17 | import qualified Ledger.Ada as Ada 18 | 19 | import Prelude (IO) 20 | import Data.Default (def) 21 | 22 | import Utility (wallet, mp) 23 | import Market.Offchain (endpoints) 24 | import Market.Types (StartParams(..), BuyParams(..)) 25 | 26 | nftEx1 :: StartParams 27 | nftEx1 = StartParams 28 | { sPrice = 100 29 | , sTn = "Martify" 30 | , sCs = "66" 31 | } -- This is an example token, 32 | -- As these are the parameters of the validator, this info should be provided by the user of the contract 33 | 34 | nftEx2 :: StartParams 35 | nftEx2 = StartParams 36 | { sPrice = 100 37 | , sTn = "Martify2" 38 | , sCs = "66" 39 | } 40 | 41 | nftEx1' :: BuyParams 42 | nftEx1' = BuyParams 43 | { bTn = sTn nftEx1 44 | , bCs = sCs nftEx1 45 | } 46 | 47 | nftEx2' :: BuyParams 48 | nftEx2' = BuyParams 49 | { bTn = sTn nftEx2 50 | , bCs = sCs nftEx2 51 | } 52 | 53 | 54 | test :: IO () 55 | test = do 56 | let dist = Map.fromList [ (wallet 1, Ada.lovelaceValueOf 10_000_000) 57 | , (wallet 2, Ada.lovelaceValueOf 10_000_000) 58 | , (wallet 3, Ada.lovelaceValueOf 10_000_000 59 | <> Value.singleton (sCs nftEx2) (sTn nftEx2) 1 60 | <> Value.singleton (sCs nftEx1) (sTn nftEx1) 1) 61 | , (wallet 4, Ada.lovelaceValueOf 10_000_000) 62 | , (wallet 5, Ada.lovelaceValueOf 10_000_000) 63 | , (wallet 6, Ada.lovelaceValueOf 10_000_000) 64 | ] 65 | emCfg = EmulatorConfig (Left dist) def def 66 | runEmulatorTraceIO' def emCfg $ do 67 | h1 <- activateContractWallet (wallet 1) endpoints 68 | h2 <- activateContractWallet (wallet 2) endpoints 69 | h3 <- activateContractWallet (wallet 3) endpoints 70 | h4 <- activateContractWallet (wallet 4) endpoints 71 | void $ Emulator.waitNSlots 1 72 | callEndpoint @"sendToken" h1 0 73 | void $ Emulator.waitNSlots 1 74 | callEndpoint @"start" h3 nftEx2 75 | void $ Emulator.waitNSlots 1 76 | callEndpoint @"start" h3 nftEx1 77 | void $ Emulator.waitNSlots 1 78 | callEndpoint @"buy'" h4 (nftEx1', nftEx2') 79 | void $ Emulator.waitNSlots 1 80 | 81 | -------------------------------------------------------------------------------- /martify.cabal: -------------------------------------------------------------------------------- 1 | Cabal-Version: 2.4 2 | Name: martify 3 | Version: 0.1.0.0 4 | Build-Type: Simple 5 | 6 | Author: Alain Magazin, Abdelkrim Dib 7 | Maintainer: alain.magazin@outlook.com, 8 | Copyright: © 2021 Alain Magazin, Abdelkrim Dib. 9 | License: Apache-2.0 10 | License-files: LICENSE 11 | 12 | common base 13 | build-depends: base >= 4.14 && < 4.15 14 | 15 | common project-config 16 | default-language: Haskell2010 17 | 18 | default-extensions: NoImplicitPrelude 19 | OverloadedStrings 20 | 21 | ghc-options: -Wall 22 | -Wcompat 23 | -Wincomplete-record-updates 24 | -Wincomplete-uni-patterns 25 | -Wpartial-fields 26 | -Wredundant-constraints 27 | -Wunused-packages 28 | 29 | library 30 | exposed-modules: Market.Types 31 | , Market.Onchain 32 | , Market.Offchain 33 | , Market.Trace 34 | , Utility 35 | , SerialiseJSON 36 | build-depends: aeson 37 | , base ^>=4.14.1.0 38 | , base16-bytestring 39 | , binary 40 | , bech32 41 | , containers 42 | , bytestring 43 | , data-default 44 | , dlist 45 | , freer-extras 46 | , lens 47 | , nonempty-containers 48 | , playground-common 49 | , plutus-contract 50 | , plutus-chain-index 51 | , plutus-ledger 52 | , plutus-ledger-api 53 | , plutus-tx-plugin 54 | , plutus-tx 55 | , prettyprinter 56 | , tagged 57 | , text 58 | , serialise 59 | , cardano-api 60 | hs-source-dirs: src 61 | default-language: Haskell2010 62 | ghc-options: -Wall -fobject-code -fno-ignore-interface-pragmas -fno-omit-interface-pragmas -fno-strictness -fno-spec-constr -fno-specialise 63 | 64 | 65 | executable market-plutus 66 | import: base, project-config 67 | hs-source-dirs: app 68 | main-is: market-plutus.hs 69 | ghc-options: -threaded -rtsopts "-with-rtsopts=-T" 70 | 71 | build-depends: cardano-api 72 | , martify 73 | , plutus-ledger-api 74 | , bytestring 75 | , cardano-ledger-alonzo 76 | 77 | 78 | executable datum-json 79 | import: base, project-config 80 | hs-source-dirs: app 81 | main-is: datum-json.hs 82 | ghc-options: -threaded -rtsopts "-with-rtsopts=-T" 83 | 84 | build-depends: martify 85 | , bytestring 86 | , cardano-api 87 | , plutus-tx 88 | , aeson 89 | -------------------------------------------------------------------------------- /src/Market/Types.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DeriveAnyClass #-} 2 | {-# LANGUAGE DataKinds #-} 3 | {-# LANGUAGE DeriveGeneric #-} 4 | {-# LANGUAGE ScopedTypeVariables #-} 5 | {-# LANGUAGE TemplateHaskell #-} 6 | {-# LANGUAGE MultiParamTypeClasses #-} 7 | {-# LANGUAGE TypeOperators #-} 8 | 9 | module Market.Types 10 | ( SaleAction (..) 11 | , SaleSchema 12 | , StartParams (..) 13 | , BuyParams (..) 14 | , NFTSale (..) 15 | , MarketParams (..) 16 | ) 17 | where 18 | 19 | import Data.Aeson (ToJSON, FromJSON) 20 | import GHC.Generics (Generic) 21 | import Prelude (Show (..)) 22 | import qualified Prelude as Pr 23 | 24 | import Schema (ToSchema) 25 | import qualified PlutusTx 26 | import PlutusTx.Prelude as Plutus ( Eq(..), (&&), Integer ) 27 | import Ledger ( TokenName, CurrencySymbol, PubKeyHash, ValidatorHash ) 28 | import Plutus.Contract ( Endpoint, type (.\/) ) 29 | 30 | newtype MarketParams = MarketParams 31 | { feeAddr :: PubKeyHash 32 | } deriving (Generic, ToJSON, FromJSON) 33 | 34 | PlutusTx.makeIsDataIndexed ''MarketParams [('MarketParams, 0)] 35 | PlutusTx.makeLift ''MarketParams 36 | 37 | 38 | data NFTSale = NFTSale 39 | { nSeller :: !PubKeyHash 40 | , nPrice :: !Plutus.Integer 41 | , nCurrency :: !CurrencySymbol 42 | , nToken :: !TokenName 43 | , nRoyAddr :: !PubKeyHash 44 | , nRoyPrct :: !Plutus.Integer 45 | } deriving (Generic, ToJSON, FromJSON) 46 | 47 | instance Eq NFTSale where 48 | {-# INLINABLE (==) #-} 49 | a == b = (nSeller a == nSeller b) && 50 | (nPrice a == nPrice b) && 51 | (nCurrency a == nCurrency b) && 52 | (nToken a == nToken b) && 53 | (nRoyAddr a == nRoyAddr b) && 54 | (nRoyPrct a == nRoyPrct b) 55 | 56 | PlutusTx.makeIsDataIndexed ''NFTSale [('NFTSale, 0)] 57 | PlutusTx.makeLift ''NFTSale 58 | 59 | 60 | data SaleAction = Buy | Close 61 | deriving Show 62 | 63 | PlutusTx.makeIsDataIndexed ''SaleAction [('Buy, 0), ('Close, 1)] 64 | PlutusTx.makeLift ''SaleAction 65 | 66 | 67 | -- We define two different params for the two endpoints start and buy with the minimal info needed. 68 | -- Therefore the user doesn't have to provide more that what's needed to execute the said action. 69 | {- For StartParams we ommit the seller 70 | because we automatically input the address of the wallet running the startSale enpoint 71 | 72 | For BuyParams we ommit seller and price 73 | because we can read that in datum which can be obtained with just cs and tn of the sold token -} 74 | 75 | data BuyParams = BuyParams 76 | { bCs :: CurrencySymbol 77 | , bTn :: TokenName 78 | } deriving (Pr.Eq, Pr.Ord, Show, Generic, ToJSON, FromJSON, ToSchema) 79 | 80 | 81 | data StartParams = StartParams 82 | { sPrice :: Integer 83 | , sCs :: CurrencySymbol 84 | , sTn :: TokenName 85 | } deriving (Pr.Eq, Pr.Ord, Show, Generic, ToJSON, FromJSON, ToSchema) 86 | 87 | 88 | type SaleSchema = Endpoint "close" BuyParams 89 | .\/ 90 | Endpoint "buy" BuyParams 91 | .\/ 92 | Endpoint "buy'" (BuyParams, BuyParams) 93 | .\/ 94 | Endpoint "start" StartParams 95 | .\/ 96 | Endpoint "updateContract" ValidatorHash 97 | .\/ 98 | Endpoint "sendToken" Integer -------------------------------------------------------------------------------- /src/Market/Onchain.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE NoImplicitPrelude #-} 3 | {-# LANGUAGE OverloadedStrings #-} 4 | {-# LANGUAGE TemplateHaskell #-} 5 | {-# LANGUAGE TypeApplications #-} 6 | {-# LANGUAGE TypeFamilies #-} 7 | {-# LANGUAGE DerivingStrategies #-} 8 | 9 | module Market.Onchain 10 | ( apiBuyScript 11 | , buyScriptAsShortBs 12 | , typedBuyValidator 13 | , Sale 14 | , buyValidator 15 | , buyValidatorHash 16 | , nftDatum 17 | ) where 18 | 19 | import qualified Data.ByteString.Lazy as LB 20 | import qualified Data.ByteString.Short as SBS 21 | import Codec.Serialise ( serialise ) 22 | 23 | import Cardano.Api.Shelley (PlutusScript (..), PlutusScriptV1) 24 | import qualified PlutusTx 25 | import PlutusTx.Prelude as Plutus 26 | ( Bool(..), Eq((==)), (.), (||), length, (&&), Integer, Maybe(..), (>=), fromInteger, (*), ($), (%), (-), map ) 27 | import Ledger 28 | ( PubKeyHash(..), 29 | ValidatorHash, 30 | Address(Address), 31 | validatorHash, 32 | DatumHash, 33 | Datum(..), 34 | txOutDatum, 35 | txSignedBy, 36 | ScriptContext(scriptContextTxInfo), 37 | ownHash, 38 | TxInfo, 39 | Validator, 40 | TxOut, 41 | txInfoSignatories, 42 | unValidatorScript, 43 | txInInfoResolved, 44 | txInfoInputs, 45 | valuePaidTo, 46 | txOutAddress) 47 | import qualified Ledger.Typed.Scripts as Scripts 48 | import qualified Plutus.V1.Ledger.Scripts as Plutus 49 | import Ledger.Value as Value ( valueOf ) 50 | import qualified Plutus.V1.Ledger.Ada as Ada (fromValue, Ada (getLovelace)) 51 | import Plutus.V1.Ledger.Credential (Credential(ScriptCredential)) 52 | 53 | 54 | import Market.Types (NFTSale(..), SaleAction(..), MarketParams(..)) 55 | 56 | 57 | {-# INLINABLE nftDatum #-} 58 | nftDatum :: TxOut -> (DatumHash -> Maybe Datum) -> Maybe NFTSale 59 | nftDatum o f = do 60 | dh <- txOutDatum o 61 | Datum d <- f dh 62 | PlutusTx.fromBuiltinData d 63 | 64 | {-# INLINABLE mkBuyValidator #-} 65 | mkBuyValidator :: MarketParams -> NFTSale -> SaleAction -> ScriptContext -> Bool 66 | mkBuyValidator mp nfts r ctx = case r of 67 | Buy -> checkFee (nPrice nfts) && 68 | (valueOf (valuePaidTo info sig) (nCurrency nfts) (nToken nfts) == 1) && 69 | checkSellerOut (nSeller nfts) (nRoyPrct nfts) (nPrice nfts) && 70 | checkRoyalty (nRoyAddr nfts) (nRoyPrct nfts) (nPrice nfts) && 71 | checkSingleBuy 72 | Close -> txSignedBy (scriptContextTxInfo ctx) (nSeller nfts) 73 | 74 | where 75 | info :: TxInfo 76 | info = scriptContextTxInfo ctx 77 | 78 | sig :: PubKeyHash 79 | sig = case txInfoSignatories info of 80 | [pubKeyHash] -> pubKeyHash 81 | 82 | checkSingleBuy :: Bool 83 | checkSingleBuy = let is = [ i | i <- map txInInfoResolved (txInfoInputs info), txOutAddress i == Address (ScriptCredential $ ownHash ctx) Nothing ] in 84 | length is == 1 85 | 86 | checkFee :: Integer -> Bool 87 | checkFee price = fromInteger (Ada.getLovelace (Ada.fromValue (valuePaidTo info (feeAddr mp)))) >= 1 % 100 * fromInteger price 88 | 89 | checkSellerOut :: PubKeyHash -> Integer -> Integer -> Bool 90 | checkSellerOut seller royPrct price = fromInteger (Ada.getLovelace (Ada.fromValue (valuePaidTo info seller))) >= (1000 - 10 - royPrct) % 1000 * fromInteger price 91 | 92 | checkRoyalty :: PubKeyHash -> Integer -> Integer -> Bool 93 | checkRoyalty royAddr royPrct price = (royPrct == 0) || (fromInteger (Ada.getLovelace (Ada.fromValue (valuePaidTo info royAddr))) >= royPrct % 1000 * fromInteger price) 94 | 95 | 96 | 97 | data Sale 98 | instance Scripts.ValidatorTypes Sale where 99 | type instance DatumType Sale = NFTSale 100 | type instance RedeemerType Sale = SaleAction 101 | 102 | 103 | typedBuyValidator :: MarketParams -> Scripts.TypedValidator Sale 104 | typedBuyValidator mp = Scripts.mkTypedValidator @Sale 105 | ($$(PlutusTx.compile [|| mkBuyValidator ||]) `PlutusTx.applyCode` PlutusTx.liftCode mp) 106 | $$(PlutusTx.compile [|| wrap ||]) 107 | where 108 | wrap = Scripts.wrapValidator @NFTSale @SaleAction 109 | 110 | 111 | buyValidator :: MarketParams -> Validator 112 | buyValidator = Scripts.validatorScript . typedBuyValidator 113 | 114 | buyValidatorHash :: MarketParams -> ValidatorHash 115 | buyValidatorHash = validatorHash . buyValidator 116 | 117 | buyScript :: MarketParams -> Plutus.Script 118 | buyScript = Ledger.unValidatorScript . buyValidator 119 | 120 | buyScriptAsShortBs :: MarketParams -> SBS.ShortByteString 121 | buyScriptAsShortBs = SBS.toShort . LB.toStrict . serialise . buyScript 122 | 123 | apiBuyScript :: MarketParams -> PlutusScript PlutusScriptV1 124 | apiBuyScript = PlutusScriptSerialised . buyScriptAsShortBs -------------------------------------------------------------------------------- /cabal.project: -------------------------------------------------------------------------------- 1 | index-state: 2021-08-14T00:00:00Z 2 | 3 | packages: ./. 4 | 5 | -- You never, ever, want this. 6 | write-ghc-environment-files: never 7 | 8 | -- Always build tests and benchmarks. 9 | tests: true 10 | benchmarks: true 11 | 12 | -- Plutus revision from 2021/12/06 13 | source-repository-package 14 | type: git 15 | location: https://github.com/input-output-hk/plutus-apps.git 16 | subdir: 17 | freer-extras 18 | playground-common 19 | plutus-contract 20 | plutus-chain-index 21 | plutus-ledger 22 | plutus-pab 23 | plutus-use-cases 24 | quickcheck-dynamic 25 | tag: plutus-starter-devcontainer/v1.0.14 26 | 27 | 28 | -- The following sections are copied from the 'plutus-apps' repository cabal.project at the revision 29 | -- given above. 30 | -- This is necessary because the 'plutus-apps' libraries depend on a number of other libraries which are 31 | -- not on Hackage, and so need to be pulled in as `source-repository-package`s themselves. Make sure to 32 | -- re-update this section from the template when you do an upgrade. 33 | 34 | -- We never, ever, want this. 35 | write-ghc-environment-files: never 36 | 37 | -- Always build tests and benchmarks. 38 | tests: true 39 | benchmarks: true 40 | 41 | -- The only sensible test display option 42 | test-show-details: streaming 43 | 44 | allow-newer: 45 | -- Copied from plutus-core 46 | size-based:template-haskell 47 | , ouroboros-consensus-byron:formatting 48 | , beam-core:aeson 49 | , beam-sqlite:aeson 50 | , beam-sqlite:dlist 51 | , beam-migrate:aeson 52 | 53 | -- Copied from plutus-core 54 | constraints: 55 | -- big breaking change here, inline-r doens't have an upper bound 56 | singletons < 3.0 57 | -- bizarre issue: in earlier versions they define their own 'GEq', in newer 58 | -- ones they reuse the one from 'some', but there isn't e.g. a proper version 59 | -- constraint from dependent-sum-template (which is the library we actually use). 60 | , dependent-sum > 0.6.2.0 61 | 62 | -- These packages appear in our dependency tree and are very slow to build. 63 | -- Empirically, turning off optimization shaves off ~50% build time. 64 | -- It also mildly improves recompilation avoidance. 65 | -- For deve work we don't care about performance so much, so this is okay. 66 | package cardano-ledger-alonzo 67 | optimization: False 68 | package ouroboros-consensus-shelley 69 | optimization: False 70 | package ouroboros-consensus-cardano 71 | optimization: False 72 | package cardano-api 73 | optimization: False 74 | 75 | -- Copied from plutus-core 76 | source-repository-package 77 | type: git 78 | location: https://github.com/Quid2/flat.git 79 | tag: ee59880f47ab835dbd73bea0847dab7869fc20d8 80 | 81 | -- Needs some patches, but upstream seems to be fairly dead (no activity in > 1 year) 82 | source-repository-package 83 | type: git 84 | location: https://github.com/input-output-hk/purescript-bridge.git 85 | tag: 6a92d7853ea514be8b70bab5e72077bf5a510596 86 | 87 | source-repository-package 88 | type: git 89 | location: https://github.com/input-output-hk/servant-purescript.git 90 | tag: a0c7c7e37c95564061247461aef4be505a853538 91 | 92 | -- Copied from plutus-core 93 | source-repository-package 94 | type: git 95 | location: https://github.com/input-output-hk/cardano-crypto.git 96 | tag: 07397f0e50da97eaa0575d93bee7ac4b2b2576ec 97 | 98 | -- Copied from plutus-core 99 | source-repository-package 100 | type: git 101 | location: https://github.com/input-output-hk/cardano-base 102 | tag: 4ea7e2d927c9a7f78ddc69738409a5827ab66b98 103 | subdir: 104 | base-deriving-via 105 | binary 106 | binary/test 107 | cardano-crypto-class 108 | cardano-crypto-praos 109 | cardano-crypto-tests 110 | measures 111 | orphans-deriving-via 112 | slotting 113 | strict-containers 114 | 115 | -- Copied from plutus-core 116 | source-repository-package 117 | type: git 118 | location: https://github.com/input-output-hk/cardano-prelude 119 | tag: fd773f7a58412131512b9f694ab95653ac430852 120 | subdir: 121 | cardano-prelude 122 | cardano-prelude-test 123 | 124 | source-repository-package 125 | type: git 126 | location: https://github.com/input-output-hk/cardano-addresses 127 | tag: d2f86caa085402a953920c6714a0de6a50b655ec 128 | subdir: 129 | core 130 | 131 | source-repository-package 132 | type: git 133 | location: https://github.com/input-output-hk/cardano-wallet 134 | tag: ae7569293e94241ef6829139ec02bd91abd069df 135 | subdir: 136 | lib/text-class 137 | lib/strict-non-empty-containers 138 | lib/core 139 | lib/test-utils 140 | lib/numeric 141 | 142 | source-repository-package 143 | type: git 144 | location: https://github.com/input-output-hk/ouroboros-network 145 | tag: 1f4973f36f689d6da75b5d351fb124d66ef1057d 146 | subdir: 147 | monoidal-synchronisation 148 | typed-protocols 149 | typed-protocols-cborg 150 | typed-protocols-examples 151 | ouroboros-network 152 | ouroboros-network-testing 153 | ouroboros-network-framework 154 | ouroboros-consensus 155 | ouroboros-consensus-byron 156 | ouroboros-consensus-cardano 157 | ouroboros-consensus-shelley 158 | io-sim 159 | io-classes 160 | network-mux 161 | ntp-client 162 | 163 | source-repository-package 164 | type: git 165 | location: https://github.com/input-output-hk/iohk-monitoring-framework 166 | -- Important Note: Read below, before changing this! 167 | tag: 46f994e216a1f8b36fe4669b47b2a7011b0e153c 168 | -- Are you thinking of updating this tag to some other commit? Please 169 | -- ensure that the commit you are about to use is the latest one from 170 | -- the *develop* branch of this repo: 171 | -- * 172 | -- (not master!) 173 | -- 174 | -- In particular we rely on the code from this PR: 175 | -- * 176 | -- being merged. 177 | subdir: 178 | iohk-monitoring 179 | tracer-transformers 180 | contra-tracer 181 | plugins/backend-aggregation 182 | plugins/backend-ekg 183 | plugins/backend-monitoring 184 | plugins/backend-trace-forwarder 185 | plugins/scribe-systemd 186 | 187 | source-repository-package 188 | type: git 189 | location: https://github.com/input-output-hk/cardano-ledger-specs 190 | tag: bf008ce028751cae9fb0b53c3bef20f07c06e333 191 | subdir: 192 | byron/ledger/impl 193 | cardano-ledger-core 194 | cardano-protocol-tpraos 195 | eras/alonzo/impl 196 | eras/byron/chain/executable-spec 197 | eras/byron/crypto 198 | eras/byron/crypto/test 199 | eras/byron/ledger/executable-spec 200 | eras/byron/ledger/impl/test 201 | eras/shelley/impl 202 | eras/shelley-ma/impl 203 | libs/non-integral 204 | libs/small-steps 205 | semantics/small-steps-test 206 | 207 | -- A lot of plutus-apps dependencies have to be synchronized with the dependencies of 208 | -- cardano-node. If you update cardano-node, please make sure that all dependencies 209 | -- of cardano-node are also updated. 210 | source-repository-package 211 | type: git 212 | location: https://github.com/input-output-hk/cardano-node.git 213 | tag: b6ca519f97a0e795611a63174687e6bb70c9f752 214 | subdir: 215 | cardano-api 216 | cardano-node 217 | cardano-cli 218 | cardano-config 219 | 220 | source-repository-package 221 | type: git 222 | location: https://github.com/input-output-hk/optparse-applicative 223 | tag: 7497a29cb998721a9068d5725d49461f2bba0e7a 224 | 225 | source-repository-package 226 | type: git 227 | location: https://github.com/input-output-hk/Win32-network 228 | tag: 3825d3abf75f83f406c1f7161883c438dac7277d 229 | 230 | source-repository-package 231 | type: git 232 | location: https://github.com/input-output-hk/goblins 233 | tag: cde90a2b27f79187ca8310b6549331e59595e7ba 234 | 235 | -- A lot of plutus-apps dependencies have to be syncronized with the dependencies of 236 | -- plutus. If you update plutus, please make sure that all dependencies of plutus 237 | -- are also updated 238 | source-repository-package 239 | type: git 240 | location: https://github.com/input-output-hk/plutus 241 | tag: 3f089ccf0ca746b399c99afe51e063b0640af547 242 | subdir: 243 | plutus-core 244 | plutus-ledger-api 245 | plutus-tx 246 | plutus-tx-plugin 247 | word-array 248 | prettyprinter-configurable 249 | stubs/plutus-ghc-stub -------------------------------------------------------------------------------- /market_mainnet_final.plutus: -------------------------------------------------------------------------------- 1 | { 2 | "type": "PlutusScriptV1", 3 | "description": "", 4 | "cborHex": "5912df5912dc0100003323322333222332233223232333222323332223233333333222222223233322232333322223232332232333222323332223232332233223232333332222233223322332233223322332222323232323232232232325335303833300d3333573466e1cd55cea805a400046666664444446666660ba00c00a0080060040026eb8d5d0a8059bad35742a0146eb8d5d0a8049bae35742a0106eb8d5d0a8039bad357426ae89401c8d4138d4c13ccd5ce2490350543100050499263333573466e1d40112002205423333573466e1d40152000205623504f353050335738921035054310005149926498cccd5cd19b8735573aa004900011980819191919191919191919191999ab9a3370e6aae75402920002333333333301e33502c232323333573466e1cd55cea80124000466048607e6ae854008c0c4d5d09aba2500223505e35305f3357389201035054310006049926135573ca00226ea8004d5d0a80519a8160169aba150093335503375ca0646ae854020ccd540cdd728191aba1500733502c04835742a00c66a05866aa0b20a2eb4d5d0a8029919191999ab9a3370e6aae754009200023350263232323333573466e1cd55cea80124000466a05c66a08eeb4d5d0a80118261aba135744a00446a0c46a60c666ae712401035054310006449926135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae7540092000233502c33504775a6ae854008c130d5d09aba250022350623530633357389201035054310006449926135573ca00226ea8004d5d09aba2500223505e35305f3357389201035054310006049926135573ca00226ea8004d5d0a80219a8163ae35742a00666a05866aa0b2eb88004d5d0a801181f1aba135744a00446a0b46a60b666ae71241035054310005c49926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea00290031181198201aba135573ca00646666ae68cdc3a801240084604460946ae84d55cf280211999ab9a3370ea006900111811181a9aba135573ca00a46666ae68cdc3a802240004604a6eb8d5d09aab9e50062350553530563357389201035054310005749926499264984d55cea80089baa001357426ae8940088d4138d4c13ccd5ce249035054310005049926104f13504d35304e3357389201035054350004f4984d55cf280089baa001135573a6ea80044d5d1280089aba25001135744a00226ae8940044d55cf280089baa0012212330010030022001222222222212333333333300100b00a00900800700600500400300220012212330010030022001122123300100300212001122123300100300212001122123300100300212001212222300400521222230030052122223002005212222300100520011232230023758002640026aa072446666aae7c004940388cd4034c010d5d080118019aba200203323232323333573466e1cd55cea801a4000466600e6464646666ae68cdc39aab9d5002480008cc034c0c4d5d0a80119a8098169aba135744a00446a06c6a606e66ae71241035054310003849926135573ca00226ea8004d5d0a801999aa805bae500a35742a00466a01eeb8d5d09aba25002235032353033335738921035054310003449926135744a00226aae7940044dd50009110919980080200180110009109198008018011000899aa800bae75a224464460046eac004c8004d540cc88c8cccd55cf80112804919a80419aa81898031aab9d5002300535573ca00460086ae8800c0b84d5d08008891001091091198008020018900089119191999ab9a3370ea002900011a80418029aba135573ca00646666ae68cdc3a801240044a01046a0526a605466ae712401035054310002b499264984d55cea80089baa001121223002003112200112001232323333573466e1cd55cea8012400046600c600e6ae854008dd69aba135744a00446a0466a604866ae71241035054310002549926135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088d407cd4c080cd5ce24810350543100021499261375400224464646666ae68cdc3a800a40084a00e46666ae68cdc3a8012400446a014600c6ae84d55cf280211999ab9a3370ea00690001280511a8111a981199ab9c490103505431000244992649926135573aa00226ea8004484888c00c0104488800844888004480048c8cccd5cd19b8750014800880188cccd5cd19b8750024800080188d4068d4c06ccd5ce249035054310001c499264984d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308d407cd4c080cd5ce2481035054310002149926499264992649926135573aa00826aae79400c4d55cf280109aab9e500113754002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024646464646666ae68cdc3a800a400446660106eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d400920002300a300b357426aae7940188d4040d4c044cd5ce2490350543100012499264984d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e500423500a35300b3357389201035054310000c499264984d55cea80089baa001212230020032122300100320011122232323333573466e1cd55cea80124000466aa016600c6ae854008c014d5d09aba25002235007353008335738921035054310000949926135573ca00226ea8004498480048004448848cc00400c008448004488008488004800488888848cccccc00401c01801401000c0088004448c8c00400488cc00cc008008004cc8c8cc88cc88c8c8c8ccc888c8c8cc88c8c8ccc888c8c8cccc8888c8cc88c8c8cc88c8c8c8c8cc88ccc888c8c8ccc888ccc888cccccccc88888888cc88ccccc88888cccc8888cc88cc88cc88ccc888cc88cc88cc88cc88cc88c8c8c888c888c94cd4c0e000c54cd4c15ccc0f8c0dcccc01cccd54c11c48005410d4140cc02401940052201004881003303f3303d4800920c801303735303b005222222005153353057333573466e1cccc01cccd54c11c48005410d4140cc0254cd4d4138d4c0314004888888888800c4c16c588854cd4d41400044008884c17c594004d4c0ec014888888010d4c0ec01488888800d20020590581533530573303e303733300733355304712001504350503300935303b0052222220065001489004881003303f3303d3370266e0520d00f48050d4c0ec01488888800520d00f303735303b00522222200515335305753353057333573466e1d40112000059058105913303e303733300733355304712001504350503300935303b0052222220025001489004881003303f3303d5004483403cc0dcd4c0ec0148888880144ccd5cd19b87323320013200133355304e12001323350562233350560030010023505300133505522230033002001200122337000029001000a4000664464600266aa60722400246a6024002440026a601ea0084444444444014640026aa0c844a66a6a0a4002200644264a66a60bc66446a6036004446a603a006446466a608200a466a60840084a66a60ce666ae68cdc78010008348340a80188341034119a982100210341299a9833999ab9a3371e0040020d20d02a00620d02a66a6a04200642a66a6a0440044266a607e004466a6080004466a6088004466a608a0044660d000400240d6466a608a00440d64660d00040024440d644466a608400840d6444a66a60d8666ae68cdc38030018370368a99a9836199ab9a3370e00a0040dc0da2666ae68cdc38020008370368836883688330a99a9a8108009083308331a980b8019110019980d181d9a9aa8331a9808804911a980a8011111111111299a9a813a9999a981a00590a81490a81490a81490999aa983089000a80e11a981180091299a9837299a9837199ab9a3371e6a607a004440046a607a008440040e00de2666ae68cdc39a981e801110009a981e80211000838037883789a8168018a816005909a9811000911a98130009111a98158019119a98270011183c0b1299a9a8180021099aa83e8010008983c0b098378b11001280e09980380180088009802000a827a828240040b20b020b020b020b020b0266446a601c0044444444444a66a6a040666aa60b224002a0284a66a60c8666ae68cdc780600083303289a8118008a8110019083308321a9805001110011a981d80291111100309a98048009100109a981b80091111100091119191800802990009aa82f9119a9a826800a4000446a6aa0c000444a66a60b4666ae68cdc780100482e02d8980380089803001990009aa82f1119a9a826000a4000446a6aa0be00444a66a60b2666ae68cdc780100382d82d080089803001899a82611299a9a807001108018800a806911a98028009111111111199aa982509000911a980a8011111a980d0019119a981e8011299a9831199ab9a3371e0280020c80c6266a0b400a00e200e400ea0a60124424660020060044002444444444424666666666600201601401201000e00c00a0080060044002442466002006004400244424666002008006004400244246600200600440022424460040062244002240022442466002006004240022442466002006004240022442466002006004240022424446006008224440042244400224002424444600800a424444600600a424444600400a424444600200a40024424660020060044002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024244600400644424466600200a008006400242446004006424460020064002640026aa058442244a66a6a0380022a03c44266a03e600800466aa600c24002008002466a01800290010910010910009000911111091999998008038030028020018011000911980299b820025335301c333573466e1c005200001e01d14800054cd4c070ccd5cd19b890014800007407852002133702900024004a66a6038666ae68cdc4000a400003c03a266e0520000011001223535005002223535007003223253353020333573466e1c01400c08808454cd4c080ccd5cd19b870040020220211022150011500115335301f333573466e24cdc100200099b820020030210201020102122353500400222353500600322330073370400800466e0800c00488c94cd4c068ccd5cd19b870024800007006c4d4090d4c080cd5ce248103505433000214984cd4014cdc2001a80099b84002500113301853353019333573466e20009200001b01a13370290000010801299a980c999ab9a33710002900000d80d099b8148000004400448848cc00400c00848004488cd54c02c480048d4d5407c00488cd54088008cd54c038480048d4d5408800488cd54094008ccd4d540340048cc0812000001223302100200123302000148000004cd54c02c480048d4d5407c00488cd54088008ccd4d540280048cd54c03c480048d4d5408c00488cd54098008d5404400400488ccd5540200640080048cd54c03c480048d4d5408c00488cd54098008d5403c004004ccd55400c050008004444888ccd54c018480054038cd54c02c480048d4d5407c00488cd54088008d54034004ccd54c0184800488d4d54080008894cd4c068ccd54c04048004c8cd406088ccd4d402c00c88008008004d4d402400488004cd4024894cd4c0700084078400406c8d4d5408c00488cc028008014018400c4cd404801000d403c004cd54c02c480048d4d5407c00488c8cd5408c00cc004014c8004d54090894cd4d40480044d5403400c884d4d54094008894cd4c07ccc0300080204cd5404801c0044c01800c00848848cc00400c00848004c8004d5406c88448894cd4d40300044008884cc014008ccd54c01c480040140100044484888c00c01044884888cc0080140104484888c004010448004c8004d540588844894cd4d401800454020884cd4024c010008cd54c01848004010004c8004d5405488448894cd4d40180044d402400c884ccd4030014c010008ccd54c01c480040140100044488008488488cc00401000c4800448d4d400c0048800448d4d40080048800848848cc00400c0084800488ccd5cd19b8f0020010060053200135500c22253353004333573466e1c005200000600510021330030013370a00400224400424400240024466e00008004988d4018d4c008cd5ce2481024c6700003498480048004448848cc00400c008448004498448c8c00400488cc00cc00800800522011c09aaedfc2c267948a623a4dddd093327c235c3fa88a47f14d41a73470001" 5 | } 6 | -------------------------------------------------------------------------------- /market_testnet_final.plutus: -------------------------------------------------------------------------------- 1 | { 2 | "type": "PlutusScriptV1", 3 | "description": "", 4 | "cborHex": "5912df5912dc0100003323322333222332233223232333222323332223233333333222222223233322232333322223232332232333222323332223232332233223232333332222233223322332233223322332222323232323232232232325335303833300d3333573466e1cd55cea805a400046666664444446666660ba00c00a0080060040026eb8d5d0a8059bad35742a0146eb8d5d0a8049bae35742a0106eb8d5d0a8039bad357426ae89401c8d4138d4c13ccd5ce2490350543100050499263333573466e1d40112002205423333573466e1d40152000205623504f353050335738921035054310005149926498cccd5cd19b8735573aa004900011980819191919191919191919191999ab9a3370e6aae75402920002333333333301e33502c232323333573466e1cd55cea80124000466048607e6ae854008c0c4d5d09aba2500223505e35305f3357389201035054310006049926135573ca00226ea8004d5d0a80519a8160169aba150093335503375ca0646ae854020ccd540cdd728191aba1500733502c04835742a00c66a05866aa0b20a2eb4d5d0a8029919191999ab9a3370e6aae754009200023350263232323333573466e1cd55cea80124000466a05c66a08eeb4d5d0a80118261aba135744a00446a0c46a60c666ae712401035054310006449926135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae7540092000233502c33504775a6ae854008c130d5d09aba250022350623530633357389201035054310006449926135573ca00226ea8004d5d09aba2500223505e35305f3357389201035054310006049926135573ca00226ea8004d5d0a80219a8163ae35742a00666a05866aa0b2eb88004d5d0a801181f1aba135744a00446a0b46a60b666ae71241035054310005c49926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea00290031181198201aba135573ca00646666ae68cdc3a801240084604460946ae84d55cf280211999ab9a3370ea006900111811181a9aba135573ca00a46666ae68cdc3a802240004604a6eb8d5d09aab9e50062350553530563357389201035054310005749926499264984d55cea80089baa001357426ae8940088d4138d4c13ccd5ce249035054310005049926104f13504d35304e3357389201035054350004f4984d55cf280089baa001135573a6ea80044d5d1280089aba25001135744a00226ae8940044d55cf280089baa0012212330010030022001222222222212333333333300100b00a00900800700600500400300220012212330010030022001122123300100300212001122123300100300212001122123300100300212001212222300400521222230030052122223002005212222300100520011232230023758002640026aa072446666aae7c004940388cd4034c010d5d080118019aba200203323232323333573466e1cd55cea801a4000466600e6464646666ae68cdc39aab9d5002480008cc034c0c4d5d0a80119a8098169aba135744a00446a06c6a606e66ae71241035054310003849926135573ca00226ea8004d5d0a801999aa805bae500a35742a00466a01eeb8d5d09aba25002235032353033335738921035054310003449926135744a00226aae7940044dd50009110919980080200180110009109198008018011000899aa800bae75a224464460046eac004c8004d540cc88c8cccd55cf80112804919a80419aa81898031aab9d5002300535573ca00460086ae8800c0b84d5d08008891001091091198008020018900089119191999ab9a3370ea002900011a80418029aba135573ca00646666ae68cdc3a801240044a01046a0526a605466ae712401035054310002b499264984d55cea80089baa001121223002003112200112001232323333573466e1cd55cea8012400046600c600e6ae854008dd69aba135744a00446a0466a604866ae71241035054310002549926135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088d407cd4c080cd5ce24810350543100021499261375400224464646666ae68cdc3a800a40084a00e46666ae68cdc3a8012400446a014600c6ae84d55cf280211999ab9a3370ea00690001280511a8111a981199ab9c490103505431000244992649926135573aa00226ea8004484888c00c0104488800844888004480048c8cccd5cd19b8750014800880188cccd5cd19b8750024800080188d4068d4c06ccd5ce249035054310001c499264984d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308d407cd4c080cd5ce2481035054310002149926499264992649926135573aa00826aae79400c4d55cf280109aab9e500113754002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024646464646666ae68cdc3a800a400446660106eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d400920002300a300b357426aae7940188d4040d4c044cd5ce2490350543100012499264984d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e500423500a35300b3357389201035054310000c499264984d55cea80089baa001212230020032122300100320011122232323333573466e1cd55cea80124000466aa016600c6ae854008c014d5d09aba25002235007353008335738921035054310000949926135573ca00226ea8004498480048004448848cc00400c008448004488008488004800488888848cccccc00401c01801401000c0088004448c8c00400488cc00cc008008004cc8c8cc88cc88c8c8c8ccc888c8c8cc88c8c8ccc888c8c8cccc8888c8cc88c8c8cc88c8c8c8c8cc88ccc888c8c8ccc888ccc888cccccccc88888888cc88ccccc88888cccc8888cc88cc88cc88ccc888cc88cc88cc88cc88cc88c8c8c888c888c94cd4c0e000c54cd4c15ccc0f8c0dcccc01cccd54c11c48005410d4140cc02401940052201004881003303f3303d4800920c801303735303b005222222005153353057333573466e1cccc01cccd54c11c48005410d4140cc0254cd4d4138d4c0314004888888888800c4c16c588854cd4d41400044008884c17c594004d4c0ec014888888010d4c0ec01488888800d20020590581533530573303e303733300733355304712001504350503300935303b0052222220065001489004881003303f3303d3370266e0520d00f48050d4c0ec01488888800520d00f303735303b00522222200515335305753353057333573466e1d40112000059058105913303e303733300733355304712001504350503300935303b0052222220025001489004881003303f3303d5004483403cc0dcd4c0ec0148888880144ccd5cd19b87323320013200133355304e12001323350562233350560030010023505300133505522230033002001200122337000029001000a4000664464600266aa60722400246a6024002440026a601ea0084444444444014640026aa0c844a66a6a0a4002200644264a66a60bc66446a6036004446a603a006446466a608200a466a60840084a66a60ce666ae68cdc78010008348340a80188341034119a982100210341299a9833999ab9a3371e0040020d20d02a00620d02a66a6a04200642a66a6a0440044266a607e004466a6080004466a6088004466a608a0044660d000400240d6466a608a00440d64660d00040024440d644466a608400840d6444a66a60d8666ae68cdc38030018370368a99a9836199ab9a3370e00a0040dc0da2666ae68cdc38020008370368836883688330a99a9a8108009083308331a980b8019110019980d181d9a9aa8331a9808804911a980a8011111111111299a9a813a9999a981a00590a81490a81490a81490999aa983089000a80e11a981180091299a9837299a9837199ab9a3371e6a607a004440046a607a008440040e00de2666ae68cdc39a981e801110009a981e80211000838037883789a8168018a816005909a9811000911a98130009111a98158019119a98270011183c0b1299a9a8180021099aa83e8010008983c0b098378b11001280e09980380180088009802000a827a828240040b20b020b020b020b020b0266446a601c0044444444444a66a6a040666aa60b224002a0284a66a60c8666ae68cdc780600083303289a8118008a8110019083308321a9805001110011a981d80291111100309a98048009100109a981b80091111100091119191800802990009aa82f9119a9a826800a4000446a6aa0c000444a66a60b4666ae68cdc780100482e02d8980380089803001990009aa82f1119a9a826000a4000446a6aa0be00444a66a60b2666ae68cdc780100382d82d080089803001899a82611299a9a807001108018800a806911a98028009111111111199aa982509000911a980a8011111a980d0019119a981e8011299a9831199ab9a3371e0280020c80c6266a0b400a00e200e400ea0a60124424660020060044002444444444424666666666600201601401201000e00c00a0080060044002442466002006004400244424666002008006004400244246600200600440022424460040062244002240022442466002006004240022442466002006004240022442466002006004240022424446006008224440042244400224002424444600800a424444600600a424444600400a424444600200a40024424660020060044002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024244600400644424466600200a008006400242446004006424460020064002640026aa058442244a66a6a0380022a03c44266a03e600800466aa600c24002008002466a01800290010910010910009000911111091999998008038030028020018011000911980299b820025335301c333573466e1c005200001e01d14800054cd4c070ccd5cd19b890014800007407852002133702900024004a66a6038666ae68cdc4000a400003c03a266e0520000011001223535005002223535007003223253353020333573466e1c01400c08808454cd4c080ccd5cd19b870040020220211022150011500115335301f333573466e24cdc100200099b820020030210201020102122353500400222353500600322330073370400800466e0800c00488c94cd4c068ccd5cd19b870024800007006c4d4090d4c080cd5ce248103505433000214984cd4014cdc2001a80099b84002500113301853353019333573466e20009200001b01a13370290000010801299a980c999ab9a33710002900000d80d099b8148000004400448848cc00400c00848004488cd54c02c480048d4d5407c00488cd54088008cd54c038480048d4d5408800488cd54094008ccd4d540340048cc0812000001223302100200123302000148000004cd54c02c480048d4d5407c00488cd54088008ccd4d540280048cd54c03c480048d4d5408c00488cd54098008d5404400400488ccd5540200640080048cd54c03c480048d4d5408c00488cd54098008d5403c004004ccd55400c050008004444888ccd54c018480054038cd54c02c480048d4d5407c00488cd54088008d54034004ccd54c0184800488d4d54080008894cd4c068ccd54c04048004c8cd406088ccd4d402c00c88008008004d4d402400488004cd4024894cd4c0700084078400406c8d4d5408c00488cc028008014018400c4cd404801000d403c004cd54c02c480048d4d5407c00488c8cd5408c00cc004014c8004d54090894cd4d40480044d5403400c884d4d54094008894cd4c07ccc0300080204cd5404801c0044c01800c00848848cc00400c00848004c8004d5406c88448894cd4d40300044008884cc014008ccd54c01c480040140100044484888c00c01044884888cc0080140104484888c004010448004c8004d540588844894cd4d401800454020884cd4024c010008cd54c01848004010004c8004d5405488448894cd4d40180044d402400c884ccd4030014c010008ccd54c01c480040140100044488008488488cc00401000c4800448d4d400c0048800448d4d40080048800848848cc00400c0084800488ccd5cd19b8f0020010060053200135500c22253353004333573466e1c005200000600510021330030013370a00400224400424400240024466e00008004988d4018d4c008cd5ce2481024c6700003498480048004448848cc00400c008448004498448c8c00400488cc00cc00800800522011c09aaedfc2c267948a623a4dddd093327c235c3fa88a47f14d41a73470001" 5 | } 6 | -------------------------------------------------------------------------------- /src/Market/Offchain.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE NumericUnderscores #-} 2 | {-# LANGUAGE DataKinds #-} 3 | {-# LANGUAGE OverloadedStrings #-} 4 | {-# LANGUAGE TypeApplications #-} 5 | 6 | module Market.Offchain 7 | ( endpoints 8 | ) 9 | where 10 | 11 | import qualified Data.Map as Map 12 | import Data.Monoid as Mnd ( (<>), mconcat ) 13 | import Control.Monad ( void, forever ) 14 | import Data.Aeson (ToJSON) 15 | import Data.Text (Text) 16 | import Prelude (String, fromIntegral, ceiling, Float, (*), (-), (/), show, and, const) 17 | 18 | 19 | import Plutus.Contract as Contract 20 | ( AsContractError, 21 | logError, 22 | logInfo, 23 | awaitTxConfirmed, 24 | endpoint, 25 | ownPubKeyHash, 26 | submitTxConstraintsWith, 27 | utxosAt, 28 | utxosTxOutTxAt, 29 | handleError, 30 | select, 31 | Contract, 32 | Promise(awaitPromise) ) 33 | import qualified PlutusTx 34 | import PlutusTx.Prelude as Plutus 35 | ( return, Bool, Maybe(..), Eq((==)), (<$>), ($), Integer, (++), isJust, map ) 36 | import Ledger 37 | ( scriptAddress, 38 | pubKeyHash, 39 | getCardanoTxId, 40 | pubKeyHashAddress, 41 | CurrencySymbol, 42 | TokenName, 43 | ValidatorHash, 44 | Redeemer(Redeemer), 45 | Datum(Datum), 46 | TxOut(txOutValue), 47 | TxOutRef, 48 | ChainIndexTxOut, toTxOut ) 49 | import Ledger.Constraints as Constraints 50 | ( otherScript, 51 | typedValidatorLookups, 52 | unspentOutputs, 53 | mustPayToPubKey, 54 | mustPayToTheScript, 55 | mustSpendScriptOutput, mustPayToOtherScript ) 56 | import Ledger.Value as Value 57 | ( singleton, 58 | valueOf ) 59 | import qualified Plutus.V1.Ledger.Ada as Ada (lovelaceValueOf) 60 | import Plutus.ChainIndex.Tx ( ChainIndexTx(_citxData) ) 61 | 62 | import Market.Types (MarketParams(..), NFTSale(..), SaleAction(..), SaleSchema, StartParams(..), BuyParams(..)) 63 | import Market.Onchain as O2 ( Sale, typedBuyValidator, buyValidator, buyValidatorHash, nftDatum ) 64 | import Utility (wallet, walletPubKeyHash, mp) 65 | 66 | 67 | startSale :: StartParams -> Contract w SaleSchema Text () 68 | startSale sp = do 69 | pkh <- Contract.ownPubKeyHash 70 | utxos <- utxosAt (pubKeyHashAddress pkh) 71 | let val = Value.singleton (sCs sp) (sTn sp) 1 72 | nfts = NFTSale { nSeller = pkh, nToken = sTn sp, nCurrency = sCs sp, nPrice = sPrice sp, nRoyAddr = walletPubKeyHash $ wallet 5, nRoyPrct = 0 } 73 | lookups = Constraints.unspentOutputs utxos <> 74 | Constraints.typedValidatorLookups (O2.typedBuyValidator mp) 75 | tx = Constraints.mustPayToTheScript nfts val 76 | ledgerTx <- submitTxConstraintsWith @O2.Sale lookups tx 77 | void $ awaitTxConfirmed $ getCardanoTxId ledgerTx 78 | Contract.logInfo @String "startSale transaction confirmed" 79 | 80 | 81 | buy :: BuyParams -> Contract w SaleSchema Text () 82 | buy bp = do 83 | pkh <- Contract.ownPubKeyHash 84 | sale <- findSale (bCs bp, bTn bp) 85 | case sale of 86 | Nothing -> Contract.logError @String "No sale found" 87 | Just (oref, o, nfts) -> do 88 | let r = Redeemer $ PlutusTx.toBuiltinData Buy 89 | val = Value.singleton (nCurrency nfts) (nToken nfts) 1 90 | valAdaS = Ada.lovelaceValueOf (ceiling ((1 - 0.02 - (fromIntegral (nRoyPrct nfts) / 100)) Prelude.* fromIntegral (nPrice nfts) :: Float)) 91 | valAdaF = Ada.lovelaceValueOf (ceiling (0.02 Prelude.* fromIntegral (nPrice nfts) :: Float)) 92 | let lookups = Constraints.typedValidatorLookups (O2.typedBuyValidator mp) <> 93 | Constraints.unspentOutputs (Map.singleton oref o) <> 94 | Constraints.otherScript (O2.buyValidator mp) 95 | tx = Constraints.mustSpendScriptOutput oref r <> 96 | Constraints.mustPayToPubKey pkh val <> 97 | Constraints.mustPayToPubKey (nSeller nfts) valAdaS <> 98 | Constraints.mustPayToPubKey (feeAddr mp) valAdaF 99 | if nRoyPrct nfts == 0 then do 100 | ledgerTx <- submitTxConstraintsWith lookups tx 101 | void $ awaitTxConfirmed $ getCardanoTxId ledgerTx 102 | Contract.logInfo @String "buy transaction confirmed" 103 | else do 104 | let valRoy = Ada.lovelaceValueOf (ceiling (fromIntegral (nRoyPrct nfts) / 100 Prelude.* fromIntegral (nPrice nfts) :: Float)) 105 | txFinal = Constraints.mustPayToPubKey (nRoyAddr nfts) valRoy <> tx 106 | ledgerTx <- submitTxConstraintsWith lookups txFinal 107 | void $ awaitTxConfirmed $ getCardanoTxId ledgerTx 108 | Contract.logInfo @String "buy transaction confirmed" 109 | 110 | 111 | buy' :: (BuyParams, BuyParams) -> Contract w SaleSchema Text () 112 | buy' (bp1, bp2) = do 113 | pkh <- Contract.ownPubKeyHash 114 | sale1 <- findSale (bCs bp1, bTn bp1) 115 | case sale1 of 116 | Nothing -> Contract.logError @String "No sale found" 117 | Just (oref, o, nfts) -> do 118 | sale2 <- findSale (bCs bp2, bTn bp2) 119 | case sale2 of 120 | Nothing -> Contract.logError @String "No sale found2" 121 | Just (oref', o', nfts') -> do 122 | let r = Redeemer $ PlutusTx.toBuiltinData Buy 123 | val = Value.singleton (nCurrency nfts) (nToken nfts) 1 124 | val' = Value.singleton (nCurrency nfts') (nToken nfts') 1 125 | valAdaS = Ada.lovelaceValueOf (ceiling ((1 - 0.01 - (fromIntegral (nRoyPrct nfts) / 100)) Prelude.* fromIntegral (nPrice nfts) :: Float)) 126 | valAdaF = Ada.lovelaceValueOf (ceiling (0.01 Prelude.* fromIntegral (nPrice nfts) :: Float)) 127 | let lookups = Constraints.typedValidatorLookups (O2.typedBuyValidator mp) <> 128 | Constraints.unspentOutputs (Map.singleton oref o) <> 129 | Constraints.unspentOutputs (Map.singleton oref' o') <> 130 | Constraints.otherScript (O2.buyValidator mp) 131 | tx = Constraints.mustSpendScriptOutput oref r <> 132 | Constraints.mustSpendScriptOutput oref' r <> 133 | Constraints.mustPayToPubKey pkh val <> 134 | Constraints.mustPayToPubKey pkh val' <> 135 | Constraints.mustPayToPubKey (nSeller nfts) valAdaS <> 136 | Constraints.mustPayToPubKey (feeAddr mp) valAdaF 137 | if nRoyPrct nfts == 0 then do 138 | ledgerTx <- submitTxConstraintsWith lookups tx 139 | void $ awaitTxConfirmed $ getCardanoTxId ledgerTx 140 | Contract.logInfo @String "buy transaction confirmed" 141 | else do 142 | let valRoy = Ada.lovelaceValueOf (ceiling (fromIntegral (nRoyPrct nfts) / 100 Prelude.* fromIntegral (nPrice nfts) :: Float)) 143 | txFinal = Constraints.mustPayToPubKey (nRoyAddr nfts) valRoy <> tx 144 | ledgerTx <- submitTxConstraintsWith lookups txFinal 145 | void $ awaitTxConfirmed $ getCardanoTxId ledgerTx 146 | Contract.logInfo @String "buy transaction confirmed" 147 | 148 | 149 | update :: (BuyParams, Integer) -> Contract w SaleSchema Text () 150 | update (bp, newprice) = do 151 | sale <- findSale (bCs bp, bTn bp) 152 | case sale of 153 | Nothing -> Contract.logError @String "No sale found" 154 | Just (oref, o, nfts) -> do 155 | let r = Redeemer $ PlutusTx.toBuiltinData Close 156 | val = Value.singleton (nCurrency nfts) (nToken nfts) 1 157 | nfts' = nfts { nPrice = newprice } 158 | lookups = Constraints.typedValidatorLookups (O2.typedBuyValidator mp) <> 159 | Constraints.otherScript (O2.buyValidator mp) <> 160 | Constraints.unspentOutputs (Map.singleton oref o) 161 | tx = Constraints.mustSpendScriptOutput oref r <> 162 | Constraints.mustPayToTheScript nfts' val 163 | ledgerTx <- submitTxConstraintsWith lookups tx 164 | void $ awaitTxConfirmed $ getCardanoTxId ledgerTx 165 | Contract.logInfo @String "Price updated" 166 | 167 | 168 | close :: BuyParams -> Contract w SaleSchema Text () 169 | close bp = do 170 | sale <- findSale (bCs bp, bTn bp) 171 | case sale of 172 | Nothing -> Contract.logError @String "No sale found" 173 | Just (oref, o, nfts) -> do 174 | let r = Redeemer $ PlutusTx.toBuiltinData Close 175 | val = Value.singleton (nCurrency nfts) (nToken nfts) 1 <> Ada.lovelaceValueOf 1724100 176 | lookups = Constraints.typedValidatorLookups (O2.typedBuyValidator mp) <> 177 | Constraints.otherScript (O2.buyValidator mp) <> 178 | Constraints.unspentOutputs (Map.singleton oref o) 179 | tx = Constraints.mustSpendScriptOutput oref r <> 180 | Constraints.mustPayToPubKey (nSeller nfts) val 181 | ledgerTx <- submitTxConstraintsWith lookups tx 182 | void $ awaitTxConfirmed $ getCardanoTxId ledgerTx 183 | Contract.logInfo @String "close transaction confirmed" 184 | 185 | 186 | findSale :: (AsContractError e, ToJSON e) => (CurrencySymbol, TokenName) -> Contract w SaleSchema e (Maybe (TxOutRef, ChainIndexTxOut, NFTSale)) 187 | findSale (cs, tn) = do 188 | utxos <- Map.filter f <$> utxosTxOutTxAt (scriptAddress $ O2.buyValidator mp) 189 | return $ case Map.toList utxos of 190 | [(oref, (o, citx))] -> do 191 | nfts <- nftDatum (toTxOut o) $ \dh -> Map.lookup dh $ _citxData citx 192 | Just (oref, o, nfts) 193 | _ -> Nothing 194 | 195 | where 196 | f :: (ChainIndexTxOut, Plutus.ChainIndex.Tx.ChainIndexTx) -> Bool 197 | f (o, _) = valueOf (txOutValue $ toTxOut o) cs tn == 1 198 | 199 | 200 | endpoints :: Contract () SaleSchema Text () 201 | endpoints = forever 202 | $ handleError logError 203 | $ awaitPromise 204 | $ start' `select` buy1 205 | `select` buy2 206 | `select` close' 207 | where 208 | start' = endpoint @"start" $ \nfts -> startSale nfts 209 | buy1 = endpoint @"buy" $ \bp -> buy bp 210 | buy2 = endpoint @"buy'" $ \(bp1, bp2) -> buy' (bp1, bp2) 211 | close' = endpoint @"close" $ \nfts -> close nfts 212 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------