From 250a49aba2ccbd5301cd38049b3e9e8a1b9432d2 Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Fri, 2 Jul 2021 20:50:48 +0700 Subject: [PATCH 001/217] add nft model description --- MetaLamp/nft-marketplace/.gitignore | 24 +++ MetaLamp/nft-marketplace/LICENSE | 201 ++++++++++++++++++ MetaLamp/nft-marketplace/README.md | 54 +++++ MetaLamp/nft-marketplace/cabal.project | 150 +++++++++++++ MetaLamp/nft-marketplace/hie.yaml | 2 + MetaLamp/nft-marketplace/plutus-starter.cabal | 59 +++++ .../Contracts/NftMarketplace/OnChain/NFT.hs | 24 +++ 7 files changed, 514 insertions(+) create mode 100644 MetaLamp/nft-marketplace/.gitignore create mode 100644 MetaLamp/nft-marketplace/LICENSE create mode 100644 MetaLamp/nft-marketplace/README.md create mode 100644 MetaLamp/nft-marketplace/cabal.project create mode 100644 MetaLamp/nft-marketplace/hie.yaml create mode 100644 MetaLamp/nft-marketplace/plutus-starter.cabal create mode 100644 MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/NFT.hs diff --git a/MetaLamp/nft-marketplace/.gitignore b/MetaLamp/nft-marketplace/.gitignore new file mode 100644 index 000000000..d30d08b07 --- /dev/null +++ b/MetaLamp/nft-marketplace/.gitignore @@ -0,0 +1,24 @@ +dist +dist-* +cabal-dev +*.o +*.hi +*.hie +*.chi +*.chs.h +*.dyn_o +*.dyn_hi +.hpc +.hsenv +.cabal-sandbox/ +cabal.sandbox.config +*.prof +*.aux +*.hp +*.eventlog +.stack-work/ +cabal.project.local +cabal.project.local~ +.HTF/ +.ghc.environment.* +.psc-ide-port \ No newline at end of file diff --git a/MetaLamp/nft-marketplace/LICENSE b/MetaLamp/nft-marketplace/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/MetaLamp/nft-marketplace/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/MetaLamp/nft-marketplace/README.md b/MetaLamp/nft-marketplace/README.md new file mode 100644 index 000000000..c1aaaa9c4 --- /dev/null +++ b/MetaLamp/nft-marketplace/README.md @@ -0,0 +1,54 @@ +# Lending Pool + +Lending Pool is a smart contract application on Plutus Platform. +The smart contract protocol is based on [Aave](https://aave.com/), but does not strictly follow it. + +## Setting up + +- Install nix +- Clone https://github.com/input-output-hk/plutus +- Set up your machine to build things with Nix, following the Plutus README (make sure to set up the binary cache!) + +## The Plutus Application Backend (PAB) usage + +We have provided two PAB applications in `./pab` and `./pab-simulation`. The first one is made for real world usage and interaction through frontend [client](client/README.md), the second one is a big test scenario. +With the PAB we can serve and interact with contracts over a web API. You can read more about the PAB here: [PAB Architecture](https://github.com/input-output-hk/plutus/blob/master/plutus-pab/ARCHITECTURE.adoc). + +1. Enter the nix shell (cd to the cloned Plutus repo): + +``` +git checkout 5cdd2c3d708bf4c33514681dee096da6463273b7 +nix-shell +``` + +2. Build the PAB executables (cd to plutus-use-cases/MetaLamp/lending-pool): + +``` +cabal build all +``` + +3. Run the PAB binary: + +``` +cabal run pab +``` + +This will then start up the server on port 8080. + +4. To run test simulation do: + +``` +cabal run pab-simulation +``` + +## Client + +See the client [readme](client/README.md). + +## Troubleshooting + +See [note](client/README.md/#Troubleshooting). + +## Protocol functionality + +See the description of user endpoints [here](src/Plutus/Contracts/Endpoints.hs) diff --git a/MetaLamp/nft-marketplace/cabal.project b/MetaLamp/nft-marketplace/cabal.project new file mode 100644 index 000000000..964bef10c --- /dev/null +++ b/MetaLamp/nft-marketplace/cabal.project @@ -0,0 +1,150 @@ +index-state: 2021-02-24T00:00:00Z + +packages: ./. + +-- You never, ever, want this. +write-ghc-environment-files: never + +-- Always build tests and benchmarks. +tests: true +benchmarks: true + +source-repository-package + type: git + location: https://github.com/input-output-hk/plutus.git + subdir: + freer-extras + playground-common + plutus-core + plutus-contract + plutus-ledger + plutus-ledger-api + plutus-tx + plutus-tx-plugin + plutus-pab + plutus-use-cases + prettyprinter-configurable + quickcheck-dynamic + word-array + tag: 5cdd2c3d708bf4c33514681dee096da6463273b7 + +-- The following sections are copied from the 'plutus' repository cabal.project at the revision +-- given above. +-- This is necessary because the 'plutus' libraries depend on a number of other libraries which are +-- not on Hackage, and so need to be pulled in as `source-repository-package`s themselves. Make sure to +-- re-update this section from the template when you do an upgrade. +package eventful-sql-common + ghc-options: -XDerivingStrategies -XStandaloneDeriving -XUndecidableInstances -XDataKinds -XFlexibleInstances + +allow-newer: + -- Has a commit to allow newer aeson, not on Hackage yet + monoidal-containers:aeson + -- Pins to an old version of Template Haskell, unclear if/when it will be updated + , size-based:template-haskell + + -- The following two dependencies are needed by plutus. + , eventful-sql-common:persistent + , eventful-sql-common:persistent-template + +constraints: + -- aws-lambda-haskell-runtime-wai doesn't compile with newer versions + aws-lambda-haskell-runtime <= 3.0.3 + -- big breaking change here, inline-r doens't have an upper bound + , singletons < 3.0 + -- breaks eventful even more than it already was + , persistent-template < 2.12 + +extra-packages: ieee, filemanip + +-- Needs some patches, but upstream seems to be fairly dead (no activity in > 1 year) +source-repository-package + type: git + location: https://github.com/shmish111/purescript-bridge.git + tag: 6a92d7853ea514be8b70bab5e72077bf5a510596 + +source-repository-package + type: git + location: https://github.com/shmish111/servant-purescript.git + tag: a76104490499aa72d40c2790d10e9383e0dbde63 + +source-repository-package + type: git + location: https://github.com/input-output-hk/cardano-crypto.git + tag: f73079303f663e028288f9f4a9e08bcca39a923e + +-- Needs a fix (https://github.com/wenkokke/unlit/pull/11) and a Hackage release +source-repository-package + type: git + location: https://github.com/michaelpj/unlit.git + tag: 9ca1112093c5ffd356fc99c7dafa080e686dd748 + +source-repository-package + type: git + location: https://github.com/input-output-hk/cardano-base + tag: 4251c0bb6e4f443f00231d28f5f70d42876da055 + subdir: + binary + binary/test + slotting + cardano-crypto-class + cardano-crypto-praos + +source-repository-package + type: git + location: https://github.com/input-output-hk/cardano-prelude + tag: ee4e7b547a991876e6b05ba542f4e62909f4a571 + subdir: + cardano-prelude + cardano-prelude-test + +source-repository-package + type: git + location: https://github.com/input-output-hk/ouroboros-network + tag: 6cb9052bde39472a0555d19ade8a42da63d3e904 + subdir: + typed-protocols + typed-protocols-examples + ouroboros-network + ouroboros-network-testing + ouroboros-network-framework + io-sim + io-sim-classes + network-mux + Win32-network + +source-repository-package + type: git + location: https://github.com/input-output-hk/iohk-monitoring-framework + tag: a89c38ed5825ba17ca79fddb85651007753d699d + subdir: + iohk-monitoring + tracer-transformers + contra-tracer + plugins/backend-ekg + +source-repository-package + type: git + location: https://github.com/input-output-hk/cardano-ledger-specs + tag: 097890495cbb0e8b62106bcd090a5721c3f4b36f + subdir: + byron/chain/executable-spec + byron/crypto + byron/crypto/test + byron/ledger/executable-spec + byron/ledger/impl + byron/ledger/impl/test + semantics/executable-spec + semantics/small-steps-test + shelley/chain-and-ledger/dependencies/non-integer + shelley/chain-and-ledger/executable-spec + shelley-ma/impl + +source-repository-package + type: git + location: https://github.com/input-output-hk/goblins + tag: cde90a2b27f79187ca8310b6549331e59595e7ba + +source-repository-package + type: git + location: https://github.com/Quid2/flat.git + tag: 95e5d7488451e43062ca84d5376b3adcc465f1cd diff --git a/MetaLamp/nft-marketplace/hie.yaml b/MetaLamp/nft-marketplace/hie.yaml new file mode 100644 index 000000000..04cd24395 --- /dev/null +++ b/MetaLamp/nft-marketplace/hie.yaml @@ -0,0 +1,2 @@ +cradle: + cabal: diff --git a/MetaLamp/nft-marketplace/plutus-starter.cabal b/MetaLamp/nft-marketplace/plutus-starter.cabal new file mode 100644 index 000000000..083a5843a --- /dev/null +++ b/MetaLamp/nft-marketplace/plutus-starter.cabal @@ -0,0 +1,59 @@ +cabal-version: 2.4 +name: plutus-starter +version: 0.1.0.0 + +-- A short (one-line) description of the package. +-- synopsis: + +-- A longer description of the package. +-- description: + +-- A URL where users can report bugs. +-- bug-reports: + +license: Apache-2.0 +license-files: LICENSE +author: Your name +maintainer: Your email + +-- A copyright notice. +-- copyright: +-- category: +-- extra-source-files: CHANGELOG.md + +library + exposed-modules: + Plutus.Abstract.State Plutus.Abstract.State.Select + build-depends: + base >= 4.9 && < 5, + aeson, + bytestring, + containers, + text, + freer-simple, + freer-extras, + prettyprinter, + lens, + -- Plutus: + playground-common, + plutus-contract, + plutus-tx-plugin, + plutus-tx, + plutus-ledger, + plutus-ledger-api, + plutus-use-cases, + plutus-pab + hs-source-dirs: src + default-language: Haskell2010 + ghc-options: + -- See Plutus Tx readme + -fobject-code -fno-ignore-interface-pragmas -fno-omit-interface-pragmas + +executable pab-simulation + main-is: Main.hs + hs-source-dirs: pab-simulation + ghc-options: + -threaded + build-depends: + base >= 4.9 && < 5, + plutus-starter diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/NFT.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/NFT.hs new file mode 100644 index 000000000..fec65bc95 --- /dev/null +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/NFT.hs @@ -0,0 +1,24 @@ +module Plutus.Contracts.NftMarketplace.OnChain.NFT where + +data NFT = NFT + { nftId :: Text + , nftName :: Text + , nftDescription :: Text + , nftIssuer :: Maybe PubKey + , nftIpfsCid :: Text + } + +-- Marketplace model: +-- 1. Store only files from IPFS network +-- 2. NFT ID is a hash of IpfsCid +-- 3. Store "data NFT" above in scripts datum as (Map nftId "data NFT") +-- 4. TokenName of a NFT is the nftId +-- 5. Only user with NFT (inside his wallet) identified by the nftId has access to view "data NFT" +-- 6. Nobody can modify "data NFT" +-- 7. Nobody can mint another NFT with same nftId +-- 8. Nobody can burn NFT (?) +-- TODO do we store NFT metadata from Cardano in IPFS also? + +-- TODO wrap "data NFT" into (isOnSale, "data NFT") +-- to give access to view "data NFT" which are on sale for other users +-- (only user with NFT inside wallet could change isOnSale) From d7407acec16cdbd84d81243302b38d7634ea6e90 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Mon, 19 Jul 2021 14:53:14 +0700 Subject: [PATCH 002/217] add main modules --- MetaLamp/nft-marketplace/pab-simulation/Main.hs | 4 ++++ MetaLamp/nft-marketplace/plutus-starter.cabal | 2 +- .../Contracts/NftMarketplace/OffChain/Endpoints.hs | 1 + .../src/Plutus/Contracts/NftMarketplace/OnChain/NFT.hs | 10 ++++++---- MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs | 4 ++++ 5 files changed, 16 insertions(+), 5 deletions(-) create mode 100644 MetaLamp/nft-marketplace/pab-simulation/Main.hs create mode 100644 MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Endpoints.hs create mode 100644 MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs diff --git a/MetaLamp/nft-marketplace/pab-simulation/Main.hs b/MetaLamp/nft-marketplace/pab-simulation/Main.hs new file mode 100644 index 000000000..45d5a05e1 --- /dev/null +++ b/MetaLamp/nft-marketplace/pab-simulation/Main.hs @@ -0,0 +1,4 @@ +import Plutus.PAB.Simulation (runNftMarketplace) + +main :: IO () +main = runNftMarketplace diff --git a/MetaLamp/nft-marketplace/plutus-starter.cabal b/MetaLamp/nft-marketplace/plutus-starter.cabal index 083a5843a..4328c5053 100644 --- a/MetaLamp/nft-marketplace/plutus-starter.cabal +++ b/MetaLamp/nft-marketplace/plutus-starter.cabal @@ -23,7 +23,7 @@ maintainer: Your email library exposed-modules: - Plutus.Abstract.State Plutus.Abstract.State.Select + Plutus.Contracts.NftMarketplace.OffChain.Endpoints Plutus.PAB.Simulation Plutus.Contracts.NftMarketplace.OnChain.NFT build-depends: base >= 4.9 && < 5, aeson, diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Endpoints.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Endpoints.hs new file mode 100644 index 000000000..ae5aa1fef --- /dev/null +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Endpoints.hs @@ -0,0 +1 @@ +module Plutus.Contracts.NftMarketplace.OffChain.Endpoints where diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/NFT.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/NFT.hs index fec65bc95..6fae914e1 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/NFT.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/NFT.hs @@ -1,11 +1,13 @@ module Plutus.Contracts.NftMarketplace.OnChain.NFT where +import qualified Data.Text as T + data NFT = NFT - { nftId :: Text - , nftName :: Text - , nftDescription :: Text + { nftId :: T.Text + , nftName :: T.Text + , nftDescription :: T.Text , nftIssuer :: Maybe PubKey - , nftIpfsCid :: Text + , nftIpfsCid :: T.Text } -- Marketplace model: diff --git a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs new file mode 100644 index 000000000..5ab604867 --- /dev/null +++ b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs @@ -0,0 +1,4 @@ +module Plutus.PAB.Simulation where + +runNftMarketplace :: IO () +runNftMarketplace = pure () From cc14e4fa1812270c309b6cac030de813f895d3ec Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Mon, 19 Jul 2021 20:32:57 +0700 Subject: [PATCH 003/217] add nix config --- MetaLamp/nft-marketplace/Makefile | 3 + MetaLamp/nft-marketplace/README.md | 3 +- MetaLamp/nft-marketplace/cabal.project | 55 +++-- MetaLamp/nft-marketplace/default.nix | 35 +++ MetaLamp/nft-marketplace/nix/default.nix | 20 ++ MetaLamp/nft-marketplace/nix/lib/ci.nix | 201 ++++++++++++++++++ MetaLamp/nft-marketplace/nix/pkgs/default.nix | 34 +++ .../nix/pkgs/haskell/default.nix | 35 +++ .../nix/pkgs/haskell/haskell.nix | 54 +++++ MetaLamp/nft-marketplace/nix/sources.json | 14 ++ MetaLamp/nft-marketplace/nix/sources.nix | 174 +++++++++++++++ MetaLamp/nft-marketplace/package-lock.json | 3 + MetaLamp/nft-marketplace/release.nix | 52 +++++ MetaLamp/nft-marketplace/shell.nix | 21 ++ 14 files changed, 689 insertions(+), 15 deletions(-) create mode 100644 MetaLamp/nft-marketplace/Makefile create mode 100644 MetaLamp/nft-marketplace/default.nix create mode 100644 MetaLamp/nft-marketplace/nix/default.nix create mode 100644 MetaLamp/nft-marketplace/nix/lib/ci.nix create mode 100644 MetaLamp/nft-marketplace/nix/pkgs/default.nix create mode 100644 MetaLamp/nft-marketplace/nix/pkgs/haskell/default.nix create mode 100644 MetaLamp/nft-marketplace/nix/pkgs/haskell/haskell.nix create mode 100644 MetaLamp/nft-marketplace/nix/sources.json create mode 100644 MetaLamp/nft-marketplace/nix/sources.nix create mode 100644 MetaLamp/nft-marketplace/package-lock.json create mode 100644 MetaLamp/nft-marketplace/release.nix create mode 100644 MetaLamp/nft-marketplace/shell.nix diff --git a/MetaLamp/nft-marketplace/Makefile b/MetaLamp/nft-marketplace/Makefile new file mode 100644 index 000000000..78eafd665 --- /dev/null +++ b/MetaLamp/nft-marketplace/Makefile @@ -0,0 +1,3 @@ +fmt: + find pab src -type f -name \*.hs -exec \ + stylish-haskell --inplace '{}' + diff --git a/MetaLamp/nft-marketplace/README.md b/MetaLamp/nft-marketplace/README.md index c1aaaa9c4..3d3d6db7e 100644 --- a/MetaLamp/nft-marketplace/README.md +++ b/MetaLamp/nft-marketplace/README.md @@ -14,10 +14,9 @@ The smart contract protocol is based on [Aave](https://aave.com/), but does not We have provided two PAB applications in `./pab` and `./pab-simulation`. The first one is made for real world usage and interaction through frontend [client](client/README.md), the second one is a big test scenario. With the PAB we can serve and interact with contracts over a web API. You can read more about the PAB here: [PAB Architecture](https://github.com/input-output-hk/plutus/blob/master/plutus-pab/ARCHITECTURE.adoc). -1. Enter the nix shell (cd to the cloned Plutus repo): +1. Enter the nix shell (from `lending-pool` directory): ``` -git checkout 5cdd2c3d708bf4c33514681dee096da6463273b7 nix-shell ``` diff --git a/MetaLamp/nft-marketplace/cabal.project b/MetaLamp/nft-marketplace/cabal.project index 964bef10c..ad1a2aaad 100644 --- a/MetaLamp/nft-marketplace/cabal.project +++ b/MetaLamp/nft-marketplace/cabal.project @@ -1,4 +1,4 @@ -index-state: 2021-02-24T00:00:00Z +index-state: 2021-04-13T00:00:00Z packages: ./. @@ -26,7 +26,7 @@ source-repository-package prettyprinter-configurable quickcheck-dynamic word-array - tag: 5cdd2c3d708bf4c33514681dee096da6463273b7 + tag: plutus-starter-devcontainer/v1.0.6 -- The following sections are copied from the 'plutus' repository cabal.project at the revision -- given above. @@ -56,6 +56,12 @@ constraints: extra-packages: ieee, filemanip +-- Drops an instance breaking our code. Should be released to Hackage eventually. +source-repository-package + type: git + location: https://github.com/Quid2/flat.git + tag: 95e5d7488451e43062ca84d5376b3adcc465f1cd + -- Needs some patches, but upstream seems to be fairly dead (no activity in > 1 year) source-repository-package type: git @@ -70,7 +76,7 @@ source-repository-package source-repository-package type: git location: https://github.com/input-output-hk/cardano-crypto.git - tag: f73079303f663e028288f9f4a9e08bcca39a923e + tag: ce8f1934e4b6252084710975bd9bbc0a4648ece4 -- Needs a fix (https://github.com/wenkokke/unlit/pull/11) and a Hackage release source-repository-package @@ -81,18 +87,20 @@ source-repository-package source-repository-package type: git location: https://github.com/input-output-hk/cardano-base - tag: 4251c0bb6e4f443f00231d28f5f70d42876da055 + tag: a715c7f420770b70bbe95ca51d3dec83866cb1bd subdir: binary binary/test slotting cardano-crypto-class cardano-crypto-praos + cardano-crypto-tests + strict-containers source-repository-package type: git location: https://github.com/input-output-hk/cardano-prelude - tag: ee4e7b547a991876e6b05ba542f4e62909f4a571 + tag: fd773f7a58412131512b9f694ab95653ac430852 subdir: cardano-prelude cardano-prelude-test @@ -100,22 +108,25 @@ source-repository-package source-repository-package type: git location: https://github.com/input-output-hk/ouroboros-network - tag: 6cb9052bde39472a0555d19ade8a42da63d3e904 + tag: e50613562d6d4a0f933741fcf590b0f69a1eda67 subdir: typed-protocols typed-protocols-examples ouroboros-network ouroboros-network-testing ouroboros-network-framework + ouroboros-consensus + ouroboros-consensus-byron + ouroboros-consensus-cardano + ouroboros-consensus-shelley io-sim io-sim-classes network-mux - Win32-network source-repository-package type: git location: https://github.com/input-output-hk/iohk-monitoring-framework - tag: a89c38ed5825ba17ca79fddb85651007753d699d + tag: 34abfb7f4f5610cabb45396e0496472446a0b2ca subdir: iohk-monitoring tracer-transformers @@ -125,7 +136,7 @@ source-repository-package source-repository-package type: git location: https://github.com/input-output-hk/cardano-ledger-specs - tag: 097890495cbb0e8b62106bcd090a5721c3f4b36f + tag: a3ef848542961079b7cd53d599e5385198a3035c subdir: byron/chain/executable-spec byron/crypto @@ -137,14 +148,32 @@ source-repository-package semantics/small-steps-test shelley/chain-and-ledger/dependencies/non-integer shelley/chain-and-ledger/executable-spec + shelley/chain-and-ledger/shelley-spec-ledger-test shelley-ma/impl + cardano-ledger-core + alonzo/impl +-- A lot of plutus dependencies have to be synchronized with the dependencies of +-- cardano-node. If you update cardano-node, please make sure that all dependencies +-- of cardano-node are also updated. source-repository-package type: git - location: https://github.com/input-output-hk/goblins - tag: cde90a2b27f79187ca8310b6549331e59595e7ba + location: https://github.com/input-output-hk/cardano-node.git + tag: b3cabae6b3bf30a0b1b4e78bc4b67282dabad0a6 + subdir: + cardano-api source-repository-package type: git - location: https://github.com/Quid2/flat.git - tag: 95e5d7488451e43062ca84d5376b3adcc465f1cd + location: https://github.com/input-output-hk/Win32-network + tag: 94153b676617f8f33abe8d8182c37377d2784bd1 + +source-repository-package + type: git + location: https://github.com/input-output-hk/hedgehog-extras + tag: 8bcd3c9dc22cc44f9fcfe161f4638a384fc7a187 + +source-repository-package + type: git + location: https://github.com/input-output-hk/goblins + tag: cde90a2b27f79187ca8310b6549331e59595e7ba diff --git a/MetaLamp/nft-marketplace/default.nix b/MetaLamp/nft-marketplace/default.nix new file mode 100644 index 000000000..6c0824149 --- /dev/null +++ b/MetaLamp/nft-marketplace/default.nix @@ -0,0 +1,35 @@ +######################################################################## +# default.nix -- The top-level nix build file for plutus-starter. +# +# This file defines various attributes that are used for building and +# developing plutus-starter. +# +######################################################################## + +let + # Here a some of the various attributes for the variable 'packages': + # + # { pkgs + # plutus-starter: { + # haskell: { + # project # The Haskell project created by haskell-nix.project + # packages # All the packages defined by our project, including dependencies + # projectPackages # Just the packages in the project + # } + # hlint + # cabal-install + # stylish-haskell + # haskell-language-server + # } + # } + + packages = import ./nix; + + inherit (packages) pkgs plutus-starter; + project = plutus-starter.haskell.project; +in +{ + inherit pkgs plutus-starter; + + inherit project; +} diff --git a/MetaLamp/nft-marketplace/nix/default.nix b/MetaLamp/nft-marketplace/nix/default.nix new file mode 100644 index 000000000..6a866cbb2 --- /dev/null +++ b/MetaLamp/nft-marketplace/nix/default.nix @@ -0,0 +1,20 @@ +let + # Pratically, the only needed dependency is the plutus repository. + sources = import ./sources.nix { inherit pkgs; }; + + # We're going to get everything from the main plutus repository. This ensures + # we're using the same version of multiple dependencies such as nipxkgs, + # haskell-nix, cabal-install, compiler-nix-name, etc. + plutus = import sources.plutus {}; + pkgs = plutus.pkgs; + + haskell-nix = pkgs.haskell-nix; + + plutus-starter = import ./pkgs { + inherit pkgs haskell-nix sources plutus; + }; + +in +{ + inherit pkgs plutus-starter; +} diff --git a/MetaLamp/nft-marketplace/nix/lib/ci.nix b/MetaLamp/nft-marketplace/nix/lib/ci.nix new file mode 100644 index 000000000..80794a750 --- /dev/null +++ b/MetaLamp/nft-marketplace/nix/lib/ci.nix @@ -0,0 +1,201 @@ +{ pkgs }: + +let + # Generic nixpkgs, use *only* for lib functions that are stable across versions + lib = pkgs.lib; +in +rec { + # Borrowed from https://github.com/cachix/ghcide-nix/pull/4/files#diff-70bfff902f4dec33e545cac10ee5844d + # Tweaked to use builtins.mapAttrs instead of needing the one from nixpkgs lib + /* + dimension: name -> attrs -> function -> attrs + where + function: keyText -> value -> attrsOf package + + WARNING: Attribute names must not contain periods ("."). + See https://github.com/NixOS/nix/issues/3088 + + NOTE: The dimension name will be picked up by agent and web ui soon. + + Specifies a dimension of the build matrix. For example + + dimension "Example" { + withP = { p = true; } + withoutP = { p = false; } + } (key: # either "withP" or "withoutP" + { p }: # either p = true or p = false + myProject p + ) + + evaluates roughly to + + { + withP = myProject true; + withoutP = myProject false; + } + + Use nested calls for multiple dimensions. + + Example: + + dimension "System" { + "x86_64-linux" = {}; + # ... + }: (system: {}: + + dimension "Nixpkgs release" ( + { + "nixpkgs-19_03".nixpkgs = someSource + } // optionalAttrs (system != "...") { + "nixpkgs-unstable".nixpkgs = someOtherSource + } + ) (_key: { nixpkgs }: + + myProject system nixpkgs + + ) + ) + + evaluates roughly to + + { + x86_64-linux.nixpkgs-19_03 = myProject "x86_64-linux" someSource; + x86_64-linux.nixpkgs-unstable = myProject "x86_64-linux" someOtherSource; + ... + } + + If you need to make references across attributes, you can do so by binding + the result. Wherever you write + + dimension "My dimension" {} (key: value: f1 key value) + + You can also write + + let + myDimension = dimension "My dimension" {} (key: value: f2 key value myDimension) + in + myDimension + + This example builds a single test runner to reuse across releases: + + let + overlay = + testRunnerPkgs: self: super: { + # ... + }; + myProject = + { nixpkgs, + pkgs ? import nixpkgs { overlays = [ overlay ]; }, + testRunnerPkgs ? pkgs + }: pkgs; + in + + let + latest = "nixpkgs-19_03"; + releases = + dimension "Nixpkgs release" + { + nixpkgs-18_09.nixpkgs = someSource + nixpkgs-19_03.nixpkgs = someOtherSource + } + (_key: { nixpkgs }: + + myProject { + inherit nixpkgs; + testRunnerPkgs = releases."${latest}"; + } + + ); + in releases; + + */ + dimension = name: attrs: f: + builtins.mapAttrs + (k: v: + let o = f k v; + in o // { recurseForDerivations = o.recurseForDerivations or true; } + ) + attrs + // { meta.dimension.name = name; }; + + /* + Takes an attribute set and returns all the paths to derivations within it, i.e. + derivationPaths { a = { b = ; }; c = ; } == [ "a.b" "c" ] + This can be used with 'attrByPath' or the 'constitutents' of an aggregate Hydra job. + */ + derivationPaths = + let + names = x: lib.filter (n: n != "recurseForDerivations" && n != "meta") (builtins.attrNames x); + go = nameSections: attrs: + builtins.concatMap + (n: + let + v = builtins.getAttr n attrs; + newNameSections = nameSections ++ [ n ]; + in + if pkgs.lib.isDerivation v + then [ (builtins.concatStringsSep "." newNameSections) ] + else if builtins.isAttrs v + then go newNameSections v + else [ ] + ) + (names attrs); + in + go [ ]; + + # Creates an aggregate job with the given name from every derivation in the attribute set. + derivationAggregate = name: attrs: pkgs.releaseTools.aggregate { + inherit name; + constituents = derivationPaths attrs; + }; + + # A filter for removing packages that aren't supported on the current platform + # according to 'meta.platforms'. + platformFilterGeneric = pkgs: system: + # This needs to use the correct nixpkgs version so all the systems line up + let + lib = pkgs.lib; + platform = lib.systems.elaborate { inherit system; }; + # Can't just default to [] for platforms, since no meta.platforms + # means "all platforms" not "no platforms" + in + drv: + if drv ? meta && drv.meta ? platforms then + lib.any (lib.meta.platformMatch platform) drv.meta.platforms + else true; + + # Hydra doesn't like these attributes hanging around in "jobsets": it thinks they're jobs! + stripAttrsForHydra = filterAttrsOnlyRecursive (n: _: n != "recurseForDerivations" && n != "dimension"); + + # Keep derivations and attrsets with 'recurseForDerivations'. This ensures that we match the + # derivations that Hercules will see, and prevents Hydra from trying to pick up all sorts of bad stuff + # (like attrsets that contain themselves!). + filterDerivations = filterAttrsOnlyRecursive (n: attrs: lib.isDerivation attrs || attrs.recurseForDerivations or false); + + # A version of 'filterAttrsRecursive' that doesn't recurse into derivations. This prevents us from going into an infinite + # loop with the 'out' attribute on derivations. + # TODO: Surely this shouldn't be necessary. I think normal 'filterAttrsRecursive' will effectively cause infinite loops + # if you keep derivations and your predicate forces the value of the attribute, as this then triggers a loop on the + # 'out' attribute. Weird. + filterAttrsOnlyRecursive = pred: set: + lib.listToAttrs ( + lib.concatMap + (name: + let v = set.${name}; in + if pred name v then [ + (lib.nameValuePair name ( + if builtins.isAttrs v && !lib.isDerivation v then filterAttrsOnlyRecursive pred v + else v + )) + ] else [ ] + ) + (builtins.attrNames set) + ); + + # Takes an array of systems and returns a `name: system` AttrSet + # filterSystems :: [ string ] -> AttrSet + filterSystems = systems: lib.filterAttrs (_: v: builtins.elem v systems) { + linux = "x86_64-linux"; + darwin = "x86_64-darwin"; + }; +} diff --git a/MetaLamp/nft-marketplace/nix/pkgs/default.nix b/MetaLamp/nft-marketplace/nix/pkgs/default.nix new file mode 100644 index 000000000..18cef4750 --- /dev/null +++ b/MetaLamp/nft-marketplace/nix/pkgs/default.nix @@ -0,0 +1,34 @@ +{ pkgs +, sources +, plutus +, haskell-nix +}: +let + gitignore-nix = pkgs.callPackage plutus."gitignore.nix" { }; + + compiler-nix-name = plutus.plutus.haskell.compiler-nix-name; + + haskell = pkgs.callPackage ./haskell { + inherit gitignore-nix sources haskell-nix; + inherit compiler-nix-name; # Use the same GHC version as plutus + }; + + hlint = plutus.plutus.hlint; + + cabal-install = plutus.plutus.cabal-install; + + nodejs = plutus.pkgs.nodejs; + + purs = plutus.plutus.purs; + + spago = plutus.plutus.spago; + + stylish-haskell = plutus.plutus.stylish-haskell; + + haskell-language-server = plutus.plutus.haskell-language-server; + + cardano-repo-tool = plutus.plutus.cardano-repo-tool; +in +{ + inherit haskell hlint cabal-install nodejs purs spago stylish-haskell haskell-language-server cardano-repo-tool; +} diff --git a/MetaLamp/nft-marketplace/nix/pkgs/haskell/default.nix b/MetaLamp/nft-marketplace/nix/pkgs/haskell/default.nix new file mode 100644 index 000000000..67036b9d5 --- /dev/null +++ b/MetaLamp/nft-marketplace/nix/pkgs/haskell/default.nix @@ -0,0 +1,35 @@ +{ lib +, haskell-nix +, gitignore-nix +, sources +, compiler-nix-name +}: +let + # The Hackage index-state from cabal.project + index-state = + let + parseIndexState = rawCabalProject: + let + indexState = lib.lists.concatLists ( + lib.lists.filter (l: l != null) + (map (l: builtins.match "^index-state: *(.*)" l) + (lib.splitString "\n" rawCabalProject))); + in + lib.lists.head (indexState ++ [ null ]); + in + parseIndexState (builtins.readFile ../../../cabal.project); + + # The haskell project created by haskell-nix.cabalProject' + project = import ./haskell.nix { + inherit haskell-nix compiler-nix-name gitignore-nix; + }; + + # All the packages defined by our project, including dependencies + packages = project.hsPkgs; + + # Just the packages in the project + projectPackages = haskell-nix.haskellLib.selectProjectPackages packages; +in +rec { + inherit project projectPackages packages; +} diff --git a/MetaLamp/nft-marketplace/nix/pkgs/haskell/haskell.nix b/MetaLamp/nft-marketplace/nix/pkgs/haskell/haskell.nix new file mode 100644 index 000000000..517deaa42 --- /dev/null +++ b/MetaLamp/nft-marketplace/nix/pkgs/haskell/haskell.nix @@ -0,0 +1,54 @@ +############################################################################ +# Builds Haskell packages with Haskell.nix +############################################################################ +{ haskell-nix +, gitignore-nix +, compiler-nix-name +}: + +let + project = haskell-nix.project { + # 'cleanGit' cleans a source directory based on the files known by git + src = haskell-nix.haskellLib.cleanGit { + name = "plutus-starter"; + src = ../../../.; + }; + + inherit compiler-nix-name; + + sha256map = { + "https://github.com/Quid2/flat.git"."95e5d7488451e43062ca84d5376b3adcc465f1cd" = "06l31x3y93rjpryvlxnpsyq2zyxvb0z6lik6yq2fvh36i5zwvwa3"; + "https://github.com/input-output-hk/plutus.git"."plutus-starter-devcontainer/v1.0.6" = "1jzbcsdrv0b43dj7bwbd1fbk71f7gph6zzb8y29n9cn3j8illnyc"; + "https://github.com/shmish111/purescript-bridge.git"."6a92d7853ea514be8b70bab5e72077bf5a510596" = "13j64vv116in3c204qsl1v0ajphac9fqvsjp7x3zzfr7n7g61drb"; + "https://github.com/shmish111/servant-purescript.git"."a76104490499aa72d40c2790d10e9383e0dbde63" = "11nxxmi5bw66va7psvrgrw7b7n85fvqgfp58yva99w3v9q3a50v9"; + "https://github.com/input-output-hk/cardano-crypto.git"."ce8f1934e4b6252084710975bd9bbc0a4648ece4" = "1v2laq04piyj511b2m77hxjh9l1yd6k9kc7g6bjala4w3zdwa4ni"; + "https://github.com/michaelpj/unlit.git"."9ca1112093c5ffd356fc99c7dafa080e686dd748" = "145sffn8gbdn6xp9q5b75yd3m46ql5bnc02arzmpfs6wgjslfhff"; + "https://github.com/input-output-hk/cardano-base"."a715c7f420770b70bbe95ca51d3dec83866cb1bd" = "06l06mmb8cd4q37bnvfpgx1c5zgsl4xaf106dqva98738i8asj7j"; + "https://github.com/input-output-hk/cardano-prelude"."fd773f7a58412131512b9f694ab95653ac430852" = "02jddik1yw0222wd6q0vv10f7y8rdgrlqaiy83ph002f9kjx7mh6"; + "https://github.com/input-output-hk/ouroboros-network"."e50613562d6d4a0f933741fcf590b0f69a1eda67" = "0i192ksa69lpzjhzmhd2h1mramkvvikw04pqws18h5dly55f4z3k"; + "https://github.com/input-output-hk/iohk-monitoring-framework"."34abfb7f4f5610cabb45396e0496472446a0b2ca" = "1fdc0a02ipa385dnwa6r6jyc8jlg537i12hflfglkhjs2b7i92gs"; + "https://github.com/input-output-hk/cardano-ledger-specs"."a3ef848542961079b7cd53d599e5385198a3035c" = "02iwn2lcfcfvrnvcqnx586ncdnma23vdqvicxgr4f39vcacalzpd"; + "https://github.com/input-output-hk/cardano-node.git"."b3cabae6b3bf30a0b1b4e78bc4b67282dabad0a6" = "1csmji1bgi45wgrw7kqy19s4bbbpa78kjg3bz7mbiwb8vjgg9kvq"; + "https://github.com/input-output-hk/Win32-network"."94153b676617f8f33abe8d8182c37377d2784bd1" = "0pb7bg0936fldaa5r08nqbxvi2g8pcy4w3c7kdcg7pdgmimr30ss"; + "https://github.com/input-output-hk/hedgehog-extras"."8bcd3c9dc22cc44f9fcfe161f4638a384fc7a187" = "12viwpahjdfvlqpnzdgjp40nw31rvyznnab1hml9afpaxd6ixh70"; + "https://github.com/input-output-hk/goblins"."cde90a2b27f79187ca8310b6549331e59595e7ba" = "17c88rbva3iw82yg9srlxjv2ia5wjb9cyqw44hik565f5v9svnyg"; + }; + + modules = [ + { + packages = { + eventful-sql-common = { + # This is needed so evenful-sql-common will build with a newer version of persistent. + ghcOptions = [ "-XDerivingStrategies -XStandaloneDeriving -XUndecidableInstances -XDataKinds -XFlexibleInstances -XMultiParamTypeClasses" ]; + doHaddock = false; + }; + + # Broken due to haddock errors. Refer to https://github.com/input-output-hk/plutus/blob/master/nix/pkgs/haskell/haskell.nix + plutus-ledger.doHaddock = false; + plutus-use-cases.doHaddock = false; + }; + } + ]; + }; +in + project diff --git a/MetaLamp/nft-marketplace/nix/sources.json b/MetaLamp/nft-marketplace/nix/sources.json new file mode 100644 index 000000000..94979120f --- /dev/null +++ b/MetaLamp/nft-marketplace/nix/sources.json @@ -0,0 +1,14 @@ +{ + "plutus": { + "branch": "master", + "description": "The Plutus language implementation and tools", + "homepage": "", + "owner": "input-output-hk", + "repo": "plutus", + "rev": "plutus-starter-devcontainer/v1.0.6", + "sha256": "1jzbcsdrv0b43dj7bwbd1fbk71f7gph6zzb8y29n9cn3j8illnyc", + "type": "tarball", + "url": "https://github.com/input-output-hk/plutus/archive/plutus-starter-devcontainer/v1.0.6.tar.gz", + "url_template": "https://github.com///archive/.tar.gz" + } +} diff --git a/MetaLamp/nft-marketplace/nix/sources.nix b/MetaLamp/nft-marketplace/nix/sources.nix new file mode 100644 index 000000000..1938409dd --- /dev/null +++ b/MetaLamp/nft-marketplace/nix/sources.nix @@ -0,0 +1,174 @@ +# This file has been generated by Niv. + +let + + # + # The fetchers. fetch_ fetches specs of type . + # + + fetch_file = pkgs: name: spec: + let + name' = sanitizeName name + "-src"; + in + if spec.builtin or true then + builtins_fetchurl { inherit (spec) url sha256; name = name'; } + else + pkgs.fetchurl { inherit (spec) url sha256; name = name'; }; + + fetch_tarball = pkgs: name: spec: + let + name' = sanitizeName name + "-src"; + in + if spec.builtin or true then + builtins_fetchTarball { name = name'; inherit (spec) url sha256; } + else + pkgs.fetchzip { name = name'; inherit (spec) url sha256; }; + + fetch_git = name: spec: + let + ref = + if spec ? ref then spec.ref else + if spec ? branch then "refs/heads/${spec.branch}" else + if spec ? tag then "refs/tags/${spec.tag}" else + abort "In git source '${name}': Please specify `ref`, `tag` or `branch`!"; + in + builtins.fetchGit { url = spec.repo; inherit (spec) rev; inherit ref; }; + + fetch_local = spec: spec.path; + + fetch_builtin-tarball = name: throw + ''[${name}] The niv type "builtin-tarball" is deprecated. You should instead use `builtin = true`. + $ niv modify ${name} -a type=tarball -a builtin=true''; + + fetch_builtin-url = name: throw + ''[${name}] The niv type "builtin-url" will soon be deprecated. You should instead use `builtin = true`. + $ niv modify ${name} -a type=file -a builtin=true''; + + # + # Various helpers + # + + # https://github.com/NixOS/nixpkgs/pull/83241/files#diff-c6f540a4f3bfa4b0e8b6bafd4cd54e8bR695 + sanitizeName = name: + ( + concatMapStrings (s: if builtins.isList s then "-" else s) + ( + builtins.split "[^[:alnum:]+._?=-]+" + ((x: builtins.elemAt (builtins.match "\\.*(.*)" x) 0) name) + ) + ); + + # The set of packages used when specs are fetched using non-builtins. + mkPkgs = sources: system: + let + sourcesNixpkgs = + import (builtins_fetchTarball { inherit (sources.nixpkgs) url sha256; }) { inherit system; }; + hasNixpkgsPath = builtins.any (x: x.prefix == "nixpkgs") builtins.nixPath; + hasThisAsNixpkgsPath = == ./.; + in + if builtins.hasAttr "nixpkgs" sources + then sourcesNixpkgs + else if hasNixpkgsPath && ! hasThisAsNixpkgsPath then + import {} + else + abort + '' + Please specify either (through -I or NIX_PATH=nixpkgs=...) or + add a package called "nixpkgs" to your sources.json. + ''; + + # The actual fetching function. + fetch = pkgs: name: spec: + + if ! builtins.hasAttr "type" spec then + abort "ERROR: niv spec ${name} does not have a 'type' attribute" + else if spec.type == "file" then fetch_file pkgs name spec + else if spec.type == "tarball" then fetch_tarball pkgs name spec + else if spec.type == "git" then fetch_git name spec + else if spec.type == "local" then fetch_local spec + else if spec.type == "builtin-tarball" then fetch_builtin-tarball name + else if spec.type == "builtin-url" then fetch_builtin-url name + else + abort "ERROR: niv spec ${name} has unknown type ${builtins.toJSON spec.type}"; + + # If the environment variable NIV_OVERRIDE_${name} is set, then use + # the path directly as opposed to the fetched source. + replace = name: drv: + let + saneName = stringAsChars (c: if isNull (builtins.match "[a-zA-Z0-9]" c) then "_" else c) name; + ersatz = builtins.getEnv "NIV_OVERRIDE_${saneName}"; + in + if ersatz == "" then drv else + # this turns the string into an actual Nix path (for both absolute and + # relative paths) + if builtins.substring 0 1 ersatz == "/" then /. + ersatz else /. + builtins.getEnv "PWD" + "/${ersatz}"; + + # Ports of functions for older nix versions + + # a Nix version of mapAttrs if the built-in doesn't exist + mapAttrs = builtins.mapAttrs or ( + f: set: with builtins; + listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set)) + ); + + # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/lists.nix#L295 + range = first: last: if first > last then [] else builtins.genList (n: first + n) (last - first + 1); + + # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L257 + stringToCharacters = s: map (p: builtins.substring p 1 s) (range 0 (builtins.stringLength s - 1)); + + # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L269 + stringAsChars = f: s: concatStrings (map f (stringToCharacters s)); + concatMapStrings = f: list: concatStrings (map f list); + concatStrings = builtins.concatStringsSep ""; + + # https://github.com/NixOS/nixpkgs/blob/8a9f58a375c401b96da862d969f66429def1d118/lib/attrsets.nix#L331 + optionalAttrs = cond: as: if cond then as else {}; + + # fetchTarball version that is compatible between all the versions of Nix + builtins_fetchTarball = { url, name ? null, sha256 }@attrs: + let + inherit (builtins) lessThan nixVersion fetchTarball; + in + if lessThan nixVersion "1.12" then + fetchTarball ({ inherit url; } // (optionalAttrs (!isNull name) { inherit name; })) + else + fetchTarball attrs; + + # fetchurl version that is compatible between all the versions of Nix + builtins_fetchurl = { url, name ? null, sha256 }@attrs: + let + inherit (builtins) lessThan nixVersion fetchurl; + in + if lessThan nixVersion "1.12" then + fetchurl ({ inherit url; } // (optionalAttrs (!isNull name) { inherit name; })) + else + fetchurl attrs; + + # Create the final "sources" from the config + mkSources = config: + mapAttrs ( + name: spec: + if builtins.hasAttr "outPath" spec + then abort + "The values in sources.json should not have an 'outPath' attribute" + else + spec // { outPath = replace name (fetch config.pkgs name spec); } + ) config.sources; + + # The "config" used by the fetchers + mkConfig = + { sourcesFile ? if builtins.pathExists ./sources.json then ./sources.json else null + , sources ? if isNull sourcesFile then {} else builtins.fromJSON (builtins.readFile sourcesFile) + , system ? builtins.currentSystem + , pkgs ? mkPkgs sources system + }: rec { + # The sources, i.e. the attribute set of spec name to spec + inherit sources; + + # The "pkgs" (evaluated nixpkgs) to use for e.g. non-builtin fetchers + inherit pkgs; + }; + +in +mkSources (mkConfig {}) // { __functor = _: settings: mkSources (mkConfig settings); } diff --git a/MetaLamp/nft-marketplace/package-lock.json b/MetaLamp/nft-marketplace/package-lock.json new file mode 100644 index 000000000..48e341a09 --- /dev/null +++ b/MetaLamp/nft-marketplace/package-lock.json @@ -0,0 +1,3 @@ +{ + "lockfileVersion": 1 +} diff --git a/MetaLamp/nft-marketplace/release.nix b/MetaLamp/nft-marketplace/release.nix new file mode 100644 index 000000000..d83bfbf3f --- /dev/null +++ b/MetaLamp/nft-marketplace/release.nix @@ -0,0 +1,52 @@ +# The content of this file was partially copied from the equivalent file in the plutus repository. +# It is used by IOHK's Hydra for CI (building the project, running the tests, etc.) +# +# Therefore, do not worry too much about the structure. +let + packages = import ./.; + + pkgs = packages.pkgs; + haskellNix = pkgs.haskell-nix; + + # Just the packages in the project + projectPackages = haskellNix.haskellLib.selectProjectPackages packages.project.hsPkgs; + + inherit (import ./nix/lib/ci.nix { inherit pkgs; }) dimension filterAttrsOnlyRecursive filterDerivations stripAttrsForHydra derivationAggregate; + + # Collects haskell derivations and builds an attrset: + # + # { library = { ... } + # , tests = { ... } + # , benchmarks = { ... } + # , exes = { ... } + # , checks = { ... } + # } + # Where each attribute contains an attribute set + # with all haskell components of that type + mkHaskellDimension = pkgs: haskellProjects: + let + # retrieve all checks from a Haskell package + collectChecks = _: ps: pkgs.haskell-nix.haskellLib.collectChecks' ps; + # retrieve all components of a Haskell package + collectComponents = type: ps: pkgs.haskell-nix.haskellLib.collectComponents' type ps; + # Given a component type and the retrieve function, retrieve components from haskell packages + select = type: selector: (selector type) haskellProjects; + # { component-type : retriever-fn } + attrs = { + "library" = collectComponents; + "tests" = collectComponents; + "benchmarks" = collectComponents; + "exes" = collectComponents; + "checks" = collectChecks; + }; + in + dimension "Haskell component" attrs select; + + ciJobsets = stripAttrsForHydra (filterDerivations { + shell = (import ./shell.nix); + + build = pkgs.recurseIntoAttrs (mkHaskellDimension pkgs projectPackages); + }); +in + ciJobsets // { required = derivationAggregate "required-plutus-starter" ciJobsets; } + diff --git a/MetaLamp/nft-marketplace/shell.nix b/MetaLamp/nft-marketplace/shell.nix new file mode 100644 index 000000000..c3dca2a53 --- /dev/null +++ b/MetaLamp/nft-marketplace/shell.nix @@ -0,0 +1,21 @@ +let + packages = import ./.; + inherit (packages) pkgs plutus-starter; + inherit (plutus-starter) haskell; + +in + haskell.project.shellFor { + withHoogle = false; + + nativeBuildInputs = with plutus-starter; [ + hlint + cabal-install + nodejs + purs + spago + haskell-language-server + stylish-haskell + pkgs.niv + cardano-repo-tool + ]; + } From 5afd9abc4a76075d36e997d85a0b499a0511f88d Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Mon, 19 Jul 2021 20:57:02 +0700 Subject: [PATCH 004/217] fix imports --- .../Contracts/NftMarketplace/OnChain/NFT.hs | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/NFT.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/NFT.hs index 6fae914e1..e35b7c45a 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/NFT.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/NFT.hs @@ -1,14 +1,18 @@ module Plutus.Contracts.NftMarketplace.OnChain.NFT where -import qualified Data.Text as T +import qualified Data.Text as T +import Ledger +import Plutus.Contract +import PlutusTx.Prelude hiding (Semigroup (..)) -data NFT = NFT - { nftId :: T.Text - , nftName :: T.Text - , nftDescription :: T.Text - , nftIssuer :: Maybe PubKey - , nftIpfsCid :: T.Text - } +data NFT = + NFT + { nftId :: T.Text + , nftName :: T.Text + , nftDescription :: T.Text + , nftIssuer :: Maybe PubKeyHash + , nftIpfsCid :: T.Text + } -- Marketplace model: -- 1. Store only files from IPFS network From b9d704a8b8247e2d2d6192454e3466effa9ef797 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Tue, 20 Jul 2021 15:09:04 +0700 Subject: [PATCH 005/217] refactor marketplace model --- .../Contracts/NftMarketplace/OnChain/NFT.hs | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/NFT.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/NFT.hs index e35b7c45a..714a47cb5 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/NFT.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/NFT.hs @@ -5,24 +5,26 @@ import Ledger import Plutus.Contract import PlutusTx.Prelude hiding (Semigroup (..)) +-- newtype AssetClass = AssetClass { unAssetClass :: (CurrencySymbol, TokenName) } data NFT = NFT - { nftId :: T.Text + { nftId :: CurrencySymbol , nftName :: T.Text , nftDescription :: T.Text - , nftIssuer :: Maybe PubKeyHash - , nftIpfsCid :: T.Text + , nftIssuer :: Maybe PubKeyHash -- ???? what if issuer is the owner, wouldn't that violate privacy TODO rm field? } -- Marketplace model: --- 1. Store only files from IPFS network --- 2. NFT ID is a hash of IpfsCid --- 3. Store "data NFT" above in scripts datum as (Map nftId "data NFT") --- 4. TokenName of a NFT is the nftId --- 5. Only user with NFT (inside his wallet) identified by the nftId has access to view "data NFT" --- 6. Nobody can modify "data NFT" --- 7. Nobody can mint another NFT with same nftId --- 8. Nobody can burn NFT (?) +-- . Store only files from IPFS network. Get the IpfsCid +-- . NFT token should be minted first in a separate transaction so that state token won't be linked to IpfsCid +-- . Minting policy is parametrized by IpfsCid +-- . TokenName of a NFT is the IpfsCid +-- . NFT ID is the CurrencySymbol +-- . Store "data NFT" above in scripts datum as (Map nftId "data NFT") +-- . Only user with NFT (inside his wallet) identified by the nftId has access to view "data NFT" +-- . Nobody can modify "data NFT" +-- . Nobody can mint another NFT with same nftId +-- . Nobody can burn NFT (?) -- TODO do we store NFT metadata from Cardano in IPFS also? -- TODO wrap "data NFT" into (isOnSale, "data NFT") From 4d9de1533bcfd789834e4f90cdc81946b7955500 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Tue, 20 Jul 2021 18:57:18 +0700 Subject: [PATCH 006/217] add state machine datum and redeemer --- MetaLamp/nft-marketplace/README.md | 22 +++++-- MetaLamp/nft-marketplace/package-lock.json | 3 - MetaLamp/nft-marketplace/plutus-starter.cabal | 2 +- .../Contracts/NftMarketplace/Endpoints.hs | 5 ++ .../NftMarketplace/OffChain/Endpoints.hs | 1 - .../NftMarketplace/OffChain/Owner.hs | 1 + .../OnChain/Core/StateMachine.hs | 64 +++++++++++++++++++ .../Contracts/NftMarketplace/OnChain/NFT.hs | 31 --------- 8 files changed, 89 insertions(+), 40 deletions(-) delete mode 100644 MetaLamp/nft-marketplace/package-lock.json create mode 100644 MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/Endpoints.hs delete mode 100644 MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Endpoints.hs create mode 100644 MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Owner.hs create mode 100644 MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs diff --git a/MetaLamp/nft-marketplace/README.md b/MetaLamp/nft-marketplace/README.md index 3d3d6db7e..f34ad68fb 100644 --- a/MetaLamp/nft-marketplace/README.md +++ b/MetaLamp/nft-marketplace/README.md @@ -1,7 +1,21 @@ -# Lending Pool - -Lending Pool is a smart contract application on Plutus Platform. -The smart contract protocol is based on [Aave](https://aave.com/), but does not strictly follow it. +# NFT Marketplace + +Marketplace model: +. Store only files from IPFS network. Get the IpfsCid +. NFT token should be minted first in a separate transaction so that state token won't be linked to IpfsCid +. Minting policy is parametrized by IpfsCid +. TokenName of a NFT is the IpfsCid +. NFT ID is the CurrencySymbol +. Store "data NFT" above in scripts datum as (Map nftId "data NFT") +. Only user with NFT (inside his wallet) identified by the nftId has access to view "data NFT" +. Nobody can modify "data NFT" +. Nobody can mint another NFT with same nftId +. Nobody can burn NFT (?) +TODO do we store NFT metadata from Cardano in IPFS also? + +TODO wrap "data NFT" into (isOnSale, "data NFT") +to give access to view "data NFT" which are on sale for other users +(only user with NFT inside wallet could change isOnSale) ## Setting up diff --git a/MetaLamp/nft-marketplace/package-lock.json b/MetaLamp/nft-marketplace/package-lock.json deleted file mode 100644 index 48e341a09..000000000 --- a/MetaLamp/nft-marketplace/package-lock.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "lockfileVersion": 1 -} diff --git a/MetaLamp/nft-marketplace/plutus-starter.cabal b/MetaLamp/nft-marketplace/plutus-starter.cabal index 4328c5053..4a3445628 100644 --- a/MetaLamp/nft-marketplace/plutus-starter.cabal +++ b/MetaLamp/nft-marketplace/plutus-starter.cabal @@ -23,7 +23,7 @@ maintainer: Your email library exposed-modules: - Plutus.Contracts.NftMarketplace.OffChain.Endpoints Plutus.PAB.Simulation Plutus.Contracts.NftMarketplace.OnChain.NFT + Plutus.Contracts.NftMarketplace.Endpoints Plutus.Contracts.NftMarketplace.OffChain.Owner Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine Plutus.PAB.Simulation Plutus.Contracts.NftMarketplace.OnChain.NFT build-depends: base >= 4.9 && < 5, aeson, diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/Endpoints.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/Endpoints.hs new file mode 100644 index 000000000..df81fd9cd --- /dev/null +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/Endpoints.hs @@ -0,0 +1,5 @@ +module Plutus.Contracts.NftMarketplace.Endpoints + ( module Export + ) where + +import Plutus.Contracts.NftMarketplace.OffChain.Owner as Export diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Endpoints.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Endpoints.hs deleted file mode 100644 index ae5aa1fef..000000000 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Endpoints.hs +++ /dev/null @@ -1 +0,0 @@ -module Plutus.Contracts.NftMarketplace.OffChain.Endpoints where diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Owner.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Owner.hs new file mode 100644 index 000000000..73049d114 --- /dev/null +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Owner.hs @@ -0,0 +1 @@ +module Plutus.Contracts.NftMarketplace.OffChain.Owner where diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs new file mode 100644 index 000000000..2ac920d18 --- /dev/null +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs @@ -0,0 +1,64 @@ +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TemplateHaskell #-} + +module Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine where + +import qualified Data.Aeson as J +import qualified Data.Text as T +import qualified GHC.Generics as Haskell +import Ledger +import Plutus.Contract +import qualified PlutusTx +import qualified PlutusTx.AssocMap as AssocMap +import PlutusTx.Prelude hiding (Semigroup (..)) +import Prelude (Semigroup (..)) +import qualified Prelude as Haskell + +newtype Marketplace = + Marketplace + { marketplaceProtocolToken :: AssetClass + } + deriving (Haskell.Eq, Haskell.Ord, Haskell.Show, Haskell.Generic) + deriving anyclass (J.ToJSON, J.FromJSON) + +PlutusTx.makeLift ''Marketplace + +type IpfsCidHash = ByteString + +data NFT = + NFT + { nftId :: CurrencySymbol + , nftName :: ByteString + , nftDescription :: ByteString + , nftIssuer :: Maybe PubKeyHash + } + deriving (Haskell.Eq, Haskell.Show, Haskell.Generic) + deriving anyclass (J.ToJSON, J.FromJSON) + +PlutusTx.unstableMakeIsData ''NFT + +PlutusTx.makeLift ''NFT + +data MarketplaceRedeemer + = StartRedeemer + | AddNftRedeemer IpfsCidHash NFT + deriving (Haskell.Show) + +PlutusTx.unstableMakeIsData ''MarketplaceRedeemer + +PlutusTx.makeLift ''MarketplaceRedeemer + +data MarketplaceDatum = + MarketplaceDatum + { getMarketplaceDatum :: AssocMap.Map IpfsCidHash NFT + } + deriving (Haskell.Show) + +PlutusTx.unstableMakeIsData ''MarketplaceDatum + +PlutusTx.makeLift ''MarketplaceDatum diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/NFT.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/NFT.hs index 714a47cb5..1d769aad1 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/NFT.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/NFT.hs @@ -1,32 +1 @@ module Plutus.Contracts.NftMarketplace.OnChain.NFT where - -import qualified Data.Text as T -import Ledger -import Plutus.Contract -import PlutusTx.Prelude hiding (Semigroup (..)) - --- newtype AssetClass = AssetClass { unAssetClass :: (CurrencySymbol, TokenName) } -data NFT = - NFT - { nftId :: CurrencySymbol - , nftName :: T.Text - , nftDescription :: T.Text - , nftIssuer :: Maybe PubKeyHash -- ???? what if issuer is the owner, wouldn't that violate privacy TODO rm field? - } - --- Marketplace model: --- . Store only files from IPFS network. Get the IpfsCid --- . NFT token should be minted first in a separate transaction so that state token won't be linked to IpfsCid --- . Minting policy is parametrized by IpfsCid --- . TokenName of a NFT is the IpfsCid --- . NFT ID is the CurrencySymbol --- . Store "data NFT" above in scripts datum as (Map nftId "data NFT") --- . Only user with NFT (inside his wallet) identified by the nftId has access to view "data NFT" --- . Nobody can modify "data NFT" --- . Nobody can mint another NFT with same nftId --- . Nobody can burn NFT (?) --- TODO do we store NFT metadata from Cardano in IPFS also? - --- TODO wrap "data NFT" into (isOnSale, "data NFT") --- to give access to view "data NFT" which are on sale for other users --- (only user with NFT inside wallet could change isOnSale) From f75d4525204d881a39237ff6b81a465c014d71d8 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Tue, 20 Jul 2021 19:02:31 +0700 Subject: [PATCH 007/217] update nix config --- MetaLamp/nft-marketplace/nix/pkgs/default.nix | 12 ++++++++++-- MetaLamp/nft-marketplace/shell.nix | 3 +++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/MetaLamp/nft-marketplace/nix/pkgs/default.nix b/MetaLamp/nft-marketplace/nix/pkgs/default.nix index 18cef4750..98ffcd0e2 100644 --- a/MetaLamp/nft-marketplace/nix/pkgs/default.nix +++ b/MetaLamp/nft-marketplace/nix/pkgs/default.nix @@ -23,12 +23,20 @@ let spago = plutus.plutus.spago; + purty = plutus.plutus.purty; + + fix-purty = plutus.plutus.fixPurty; + + fix-stylish-haskell = plutus.plutus.fixStylishHaskell; + stylish-haskell = plutus.plutus.stylish-haskell; haskell-language-server = plutus.plutus.haskell-language-server; cardano-repo-tool = plutus.plutus.cardano-repo-tool; in -{ - inherit haskell hlint cabal-install nodejs purs spago stylish-haskell haskell-language-server cardano-repo-tool; +{ + inherit nodejs purs spago purty fix-purty; + inherit haskell hlint cabal-install stylish-haskell fix-stylish-haskell haskell-language-server; + inherit cardano-repo-tool; } diff --git a/MetaLamp/nft-marketplace/shell.nix b/MetaLamp/nft-marketplace/shell.nix index c3dca2a53..69f0ba40d 100644 --- a/MetaLamp/nft-marketplace/shell.nix +++ b/MetaLamp/nft-marketplace/shell.nix @@ -13,8 +13,11 @@ in nodejs purs spago + purty + fix-purty haskell-language-server stylish-haskell + fix-stylish-haskell pkgs.niv cardano-repo-tool ]; From cf7c6a59a0d2787142e9640bc29c492a0b6ff952 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Tue, 20 Jul 2021 21:13:27 +0700 Subject: [PATCH 008/217] add owner endpoints --- MetaLamp/nft-marketplace/README.md | 6 +- MetaLamp/nft-marketplace/plutus-starter.cabal | 2 +- .../src/Plutus/Abstract/ContractResponse.hs | 76 +++++++++++++++ .../src/Plutus/Abstract/OutputValue.hs | 19 ++++ .../src/Plutus/Abstract/TxUtils.hs | 97 +++++++++++++++++++ .../NftMarketplace/OffChain/Owner.hs | 60 ++++++++++++ .../Contracts/NftMarketplace/OnChain/Core.hs | 36 +++++++ .../OnChain/Core/StateMachine.hs | 57 +++++++++-- 8 files changed, 339 insertions(+), 14 deletions(-) create mode 100644 MetaLamp/nft-marketplace/src/Plutus/Abstract/ContractResponse.hs create mode 100644 MetaLamp/nft-marketplace/src/Plutus/Abstract/OutputValue.hs create mode 100644 MetaLamp/nft-marketplace/src/Plutus/Abstract/TxUtils.hs create mode 100644 MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core.hs diff --git a/MetaLamp/nft-marketplace/README.md b/MetaLamp/nft-marketplace/README.md index f34ad68fb..d96be1a55 100644 --- a/MetaLamp/nft-marketplace/README.md +++ b/MetaLamp/nft-marketplace/README.md @@ -11,12 +11,10 @@ Marketplace model: . Nobody can modify "data NFT" . Nobody can mint another NFT with same nftId . Nobody can burn NFT (?) +. To put item on sale user reveals "data NFT"s IpfsCid field + (only user with NFT inside wallet could change this field) TODO do we store NFT metadata from Cardano in IPFS also? -TODO wrap "data NFT" into (isOnSale, "data NFT") -to give access to view "data NFT" which are on sale for other users -(only user with NFT inside wallet could change isOnSale) - ## Setting up - Install nix diff --git a/MetaLamp/nft-marketplace/plutus-starter.cabal b/MetaLamp/nft-marketplace/plutus-starter.cabal index 4a3445628..94f17cd9e 100644 --- a/MetaLamp/nft-marketplace/plutus-starter.cabal +++ b/MetaLamp/nft-marketplace/plutus-starter.cabal @@ -23,7 +23,7 @@ maintainer: Your email library exposed-modules: - Plutus.Contracts.NftMarketplace.Endpoints Plutus.Contracts.NftMarketplace.OffChain.Owner Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine Plutus.PAB.Simulation Plutus.Contracts.NftMarketplace.OnChain.NFT + Plutus.Contracts.NftMarketplace.OnChain.Core Plutus.Abstract.ContractResponse Plutus.Abstract.OutputValue Plutus.Abstract.TxUtils Plutus.Contracts.NftMarketplace.Endpoints Plutus.Contracts.NftMarketplace.OffChain.Owner Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine Plutus.PAB.Simulation Plutus.Contracts.NftMarketplace.OnChain.NFT build-depends: base >= 4.9 && < 5, aeson, diff --git a/MetaLamp/nft-marketplace/src/Plutus/Abstract/ContractResponse.hs b/MetaLamp/nft-marketplace/src/Plutus/Abstract/ContractResponse.hs new file mode 100644 index 000000000..54d37010e --- /dev/null +++ b/MetaLamp/nft-marketplace/src/Plutus/Abstract/ContractResponse.hs @@ -0,0 +1,76 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeFamilies #-} + +module Plutus.Abstract.ContractResponse where + +import qualified Control.Lens as Lens +import Control.Monad hiding (fmap) +import qualified Data.ByteString as BS +import qualified Data.Map as Map +import Data.Monoid (Last (..)) +import Data.Proxy (Proxy (..)) +import Data.Text (Text, pack) +import qualified Data.Text as Text +import Data.Void (Void) +import Ledger hiding (singleton) +import Ledger.Constraints as Constraints +import Ledger.Constraints.OnChain as Constraints +import Ledger.Constraints.TxConstraints as Constraints +import qualified Ledger.Scripts as Scripts +import qualified Ledger.Typed.Scripts as Scripts +import Playground.Contract +import Plutus.Abstract.OutputValue (OutputValue (..)) +import qualified Plutus.Abstract.TxUtils as TxUtils +import Plutus.Contract hiding (when) +import Plutus.Contracts.Currency as Currency +import Plutus.V1.Ledger.Ada (adaValueOf, lovelaceValueOf) +import qualified Plutus.V1.Ledger.Address as Addr +import Plutus.V1.Ledger.Value as Value +import qualified PlutusTx +import qualified PlutusTx.AssocMap as AssocMap +import PlutusTx.Prelude hiding (Monoid (..), + Semigroup (..), mconcat, + unless) +import Prelude (Monoid (..), Semigroup (..), + show, subtract) +import qualified Prelude +import Text.Printf (printf) + +data ContractResponse e a = ContractSuccess a | ContractError e | ContractPending + deriving stock (Prelude.Eq, Show, Generic) + deriving anyclass (ToJSON, FromJSON) + +instance Semigroup (ContractResponse e a) where + a <> b = b + +instance Monoid (ContractResponse e a) where + mempty = ContractPending + mappend = (<>) + +withContractResponse :: forall l a p r s. + (HasEndpoint l p s, FromJSON p) + => Proxy l + -> (a -> r) + -> (p -> Contract (ContractResponse Text r) s Text a) + -> Contract (ContractResponse Text r) s Void () +withContractResponse _ g c = do + e <- runError $ do + p <- endpoint @l + _ <- tell ContractPending + errorHandler `handleError` c p + tell $ case e of + Left err -> ContractError err + Right a -> ContractSuccess $ g a + where + errorHandler e = do + logInfo @Text ("Error submiting the transaction: " <> e) + throwError e diff --git a/MetaLamp/nft-marketplace/src/Plutus/Abstract/OutputValue.hs b/MetaLamp/nft-marketplace/src/Plutus/Abstract/OutputValue.hs new file mode 100644 index 000000000..96fafabac --- /dev/null +++ b/MetaLamp/nft-marketplace/src/Plutus/Abstract/OutputValue.hs @@ -0,0 +1,19 @@ +{-# LANGUAGE DeriveFunctor #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE FunctionalDependencies #-} +{-# LANGUAGE TemplateHaskell #-} + +module Plutus.Abstract.OutputValue where + +import Control.Lens (makeClassy_) +import Ledger (TxOutRef, TxOutTx) +import qualified PlutusTx.Prelude as PlutuxTx + +data OutputValue a = + OutputValue { + ovOutRef :: TxOutRef, + ovOutTx :: TxOutTx, + ovValue :: a + } deriving (Prelude.Show, Prelude.Functor) + +makeClassy_ ''OutputValue diff --git a/MetaLamp/nft-marketplace/src/Plutus/Abstract/TxUtils.hs b/MetaLamp/nft-marketplace/src/Plutus/Abstract/TxUtils.hs new file mode 100644 index 000000000..b511312a0 --- /dev/null +++ b/MetaLamp/nft-marketplace/src/Plutus/Abstract/TxUtils.hs @@ -0,0 +1,97 @@ +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeFamilies #-} + +module Plutus.Abstract.TxUtils where + +import Control.Lens (review) +import Control.Monad (void) +import Data.ByteString (ByteString) +import qualified Data.Map as Map +import Data.Text (Text) +import Data.Void (Void) +import Ledger hiding (singleton) +import qualified Ledger.Constraints as Constraints +import qualified Ledger.Constraints.OnChain as Constraints +import qualified Ledger.Constraints.TxConstraints as Constraints +import Ledger.Typed.Scripts (DatumType, MonetaryPolicy, + RedeemerType, TypedValidator) +import qualified Ledger.Typed.Scripts as Scripts +import Plutus.Abstract.OutputValue (OutputValue (..)) +import Plutus.Contract +import Plutus.V1.Ledger.Contexts (ScriptContext, + scriptCurrencySymbol) +import qualified Plutus.V1.Ledger.Scripts as Scripts +import Plutus.V1.Ledger.Value (AssetClass (unAssetClass), + TokenName (..), assetClass, + assetClassValue, + assetClassValueOf) +import qualified PlutusTx +import PlutusTx.Prelude hiding (Semigroup (..)) +import Prelude (Semigroup (..)) +import qualified Prelude + +type TxPair a = (Constraints.ScriptLookups a, Constraints.TxConstraints (RedeemerType a) (DatumType a)) + +type IsScriptData a = (PlutusTx.IsData (RedeemerType a), PlutusTx.IsData (DatumType a)) + +submitTxPair :: (AsContractError e, IsScriptData a) => + TxPair a + -> Contract w s e Tx +submitTxPair = Prelude.uncurry submitTxConstraintsWith + +mustForgeValue :: (IsScriptData a) => + MonetaryPolicy + -> Value + -> TxPair a +mustForgeValue policy value = (lookups, tx) + where + lookups = Constraints.monetaryPolicy policy + tx = Constraints.mustForgeValue value + +mustPayToScript :: (IsScriptData a) => + TypedValidator a + -> PubKeyHash + -> DatumType a + -> Value + -> TxPair a +mustPayToScript script pkh datum value = (lookups, tx) + where + lookups = Constraints.ownPubKeyHash pkh <> Constraints.typedValidatorLookups script + tx = Constraints.mustPayToTheScript datum value + +mustSpendScriptOutputs :: (IsScriptData a) => + TypedValidator a + -> [OutputValue (RedeemerType a)] + -> TxPair a +mustSpendScriptOutputs script inputs = (lookups, tx) + where + unspent = Map.fromList $ fmap (\(OutputValue ref tx _) -> (ref, tx)) inputs + lookups = Constraints.otherScript (Scripts.validatorScript script) <> Constraints.unspentOutputs unspent + tx = Prelude.mconcat $ + fmap (\(OutputValue ref _ redeemer) -> Constraints.mustSpendScriptOutput ref (Redeemer $ PlutusTx.toData redeemer)) inputs + +mustSpendFromScript :: (IsScriptData a) => + TypedValidator a + -> [OutputValue (RedeemerType a)] + -> PubKeyHash + -> Value + -> TxPair a +mustSpendFromScript script inputs pkh value = (lookups, tx) <> mustSpendScriptOutputs script inputs + where + lookups = Constraints.ownPubKeyHash pkh + tx = Constraints.mustPayToPubKey pkh value + +mustRoundTripToScript :: (IsScriptData a) => + TypedValidator a + -> [OutputValue (RedeemerType a)] + -> DatumType a + -> PubKeyHash + -> Value + -> TxPair a +mustRoundTripToScript script inputs datum pkh value = mustSpendScriptOutputs script inputs <> mustPayToScript script pkh datum value diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Owner.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Owner.hs index 73049d114..8aaa051dd 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Owner.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Owner.hs @@ -1 +1,61 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} + module Plutus.Contracts.NftMarketplace.OffChain.Owner where + +import qualified Data.Aeson as J +import Data.Text (Text) +import qualified Data.Text as T +import qualified GHC.Generics as Haskell +import Ledger +import qualified Ledger.Typed.Scripts as Scripts +import Ledger.Value +import Plutus.Abstract.ContractResponse (ContractResponse, + withContractResponse) +import Plutus.Contract +import Plutus.Contract.StateMachine +import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Core +import qualified PlutusTx +import qualified PlutusTx.AssocMap as AssocMap +import PlutusTx.Prelude hiding + (Semigroup (..)) +import Prelude (Semigroup (..)) +import qualified Prelude as Haskell +import Control.Monad hiding (fmap) +import Data.Proxy (Proxy (..)) +import Plutus.Contracts.Currency as Currency +import Text.Printf (printf) + +-- | Starts the NFT Marketplace protocol: minting protocol NFT, creating empty nft storage +start :: () -> Contract w s Text Core.Marketplace +start () = start' $ do + pkh <- pubKeyHash <$> ownPubKey + fmap Currency.currencySymbol $ + mapError (T.pack . Haskell.show @Currency.CurrencyError) $ + Currency.forgeContract pkh [(Core.marketplaceProtocolName, 1)] + +start' :: Contract w s Text CurrencySymbol -> Contract w s Text Core.Marketplace +start' getMarketplaceToken = do + marketplaceToken <- getMarketplaceToken + pkh <- pubKeyHash <$> ownPubKey + let marketplace = Core.marketplace marketplaceToken + let client = Core.marketplaceClient marketplace + void $ mapError (T.pack . Haskell.show @SMContractError) $ runInitialise client (Core.MarketplaceDatum AssocMap.empty) mempty + + logInfo @Haskell.String $ printf "started Marketplace %s at address %s" (Haskell.show marketplace) (Haskell.show $ Core.marketplaceAddress marketplace) + pure marketplace + +type MarketplaceOwnerSchema = + Endpoint "start" () + +data OwnerContractState = Started Core.Marketplace + deriving stock (Haskell.Eq, Haskell.Ord, Haskell.Show, Haskell.Generic) + deriving anyclass (J.ToJSON, J.FromJSON) + +ownerEndpoints :: Contract (ContractResponse Text OwnerContractState) MarketplaceOwnerSchema Void () +ownerEndpoints = forever $ withContractResponse (Proxy @"start") Started start diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core.hs new file mode 100644 index 000000000..117ba0c2c --- /dev/null +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core.hs @@ -0,0 +1,36 @@ +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeApplications #-} + +module Plutus.Contracts.NftMarketplace.OnChain.Core + ( module Export + , module Plutus.Contracts.NftMarketplace.OnChain.Core + ) where + +import qualified Data.Aeson as J +import qualified Data.Text as T +import qualified GHC.Generics as Haskell +import Ledger +import qualified Ledger.Typed.Scripts as Scripts +import Ledger.Value +import Plutus.Contract +import Plutus.Contract.StateMachine +import Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine as Export +import qualified PlutusTx +import qualified PlutusTx.AssocMap as AssocMap +import PlutusTx.Prelude hiding + (Semigroup (..)) +import Prelude (Semigroup (..)) +import qualified Prelude as Haskell + +marketplaceProtocolName :: TokenName +marketplaceProtocolName = "NFT Marketplace" + +marketplace :: CurrencySymbol -> Marketplace +marketplace protocol = Marketplace (assetClass protocol marketplaceProtocolName) + +marketplaceValidator :: Marketplace -> Validator +marketplaceValidator = Scripts.validatorScript . marketplaceInst + +marketplaceAddress :: Marketplace -> Ledger.Address +marketplaceAddress = scriptAddress . marketplaceValidator diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs index 2ac920d18..f9bbcac84 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DerivingStrategies #-} @@ -5,25 +6,29 @@ {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TypeApplications #-} module Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine where -import qualified Data.Aeson as J -import qualified Data.Text as T -import qualified GHC.Generics as Haskell +import qualified Data.Aeson as J +import qualified Data.Text as T +import qualified GHC.Generics as Haskell import Ledger +import qualified Ledger.Typed.Scripts as Scripts +import Ledger.Value import Plutus.Contract +import Plutus.Contract.StateMachine import qualified PlutusTx -import qualified PlutusTx.AssocMap as AssocMap -import PlutusTx.Prelude hiding (Semigroup (..)) -import Prelude (Semigroup (..)) -import qualified Prelude as Haskell +import qualified PlutusTx.AssocMap as AssocMap +import PlutusTx.Prelude hiding (Semigroup (..)) +import Prelude (Semigroup (..)) +import qualified Prelude as Haskell newtype Marketplace = Marketplace { marketplaceProtocolToken :: AssetClass } - deriving (Haskell.Eq, Haskell.Ord, Haskell.Show, Haskell.Generic) + deriving stock (Haskell.Eq, Haskell.Ord, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON) PlutusTx.makeLift ''Marketplace @@ -36,8 +41,9 @@ data NFT = , nftName :: ByteString , nftDescription :: ByteString , nftIssuer :: Maybe PubKeyHash + , nftIpfsCid :: Maybe ByteString } - deriving (Haskell.Eq, Haskell.Show, Haskell.Generic) + deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON) PlutusTx.unstableMakeIsData ''NFT @@ -62,3 +68,36 @@ data MarketplaceDatum = PlutusTx.unstableMakeIsData ''MarketplaceDatum PlutusTx.makeLift ''MarketplaceDatum + +{-# INLINABLE transition #-} +transition :: Marketplace -> State MarketplaceDatum -> MarketplaceRedeemer -> Maybe (TxConstraints Void Void, State MarketplaceDatum) +transition marketplace s r = Nothing + where + token :: Value + token = assetClassValue (marketplaceProtocolToken marketplace) 1 + +{-# INLINABLE marketplaceStateMachine #-} +marketplaceStateMachine :: Marketplace -> StateMachine MarketplaceDatum MarketplaceRedeemer +marketplaceStateMachine marketplace = StateMachine + { smTransition = transition marketplace + , smFinal = const False + , smCheck = \d r ctx -> True + , smThreadToken = Just $ marketplaceProtocolToken marketplace + } + +{-# INLINABLE mkMarketplaceValidator #-} +mkMarketplaceValidator :: Marketplace -> MarketplaceDatum -> MarketplaceRedeemer -> ScriptContext -> Bool +mkMarketplaceValidator marketplace = mkValidator $ marketplaceStateMachine marketplace + +type MarketplaceScript = StateMachine MarketplaceDatum MarketplaceRedeemer + +marketplaceInst :: Marketplace -> Scripts.TypedValidator MarketplaceScript +marketplaceInst marketplace = Scripts.mkTypedValidator @MarketplaceScript + ($$(PlutusTx.compile [|| mkMarketplaceValidator ||]) + `PlutusTx.applyCode` PlutusTx.liftCode marketplace) + $$(PlutusTx.compile [|| wrap ||]) + where + wrap = Scripts.wrapValidator @MarketplaceDatum @MarketplaceRedeemer + +marketplaceClient :: Marketplace -> StateMachineClient MarketplaceDatum MarketplaceRedeemer +marketplaceClient marketplace = mkStateMachineClient $ StateMachineInstance (marketplaceStateMachine marketplace) (marketplaceInst marketplace) From b67cc33db2baf8a13563da5d71ac9d17af19092e Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Wed, 21 Jul 2021 16:18:24 +0700 Subject: [PATCH 009/217] add marketplace start simulation --- .../src/Plutus/PAB/Simulation.hs | 139 +++++++++++++++++- 1 file changed, 138 insertions(+), 1 deletion(-) diff --git a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs index 5ab604867..015a56ae0 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs @@ -1,4 +1,141 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} + module Plutus.PAB.Simulation where +import Control.Monad (forM, forM_, void, + when) +import Control.Monad.Freer (Eff, Member, + interpret, + type (~>)) +import Control.Monad.Freer.Error (Error) +import Control.Monad.Freer.Extras.Log (LogMsg) +import Control.Monad.IO.Class (MonadIO (..)) +import Data.Aeson (FromJSON, + Result (..), + ToJSON, encode, + fromJSON) +import qualified Data.ByteString as BS +import qualified Data.Map.Strict as Map +import qualified Data.Monoid as Monoid +import qualified Data.Semigroup as Semigroup +import Data.Text (Text) +import Data.Text.Prettyprint.Doc (Pretty (..), + viaShow) +import GHC.Generics (Generic) +import Ledger +import Ledger.Ada (adaSymbol, + adaToken, + adaValueOf, + lovelaceValueOf) +import Ledger.Constraints +import qualified Ledger.Constraints.OffChain as Constraints +import qualified Ledger.Typed.Scripts as Scripts +import Ledger.Value as Value +import Plutus.Abstract.ContractResponse (ContractResponse (..)) +import Plutus.Contract hiding (when) +import Plutus.Contracts.Currency as Currency +import qualified Plutus.Contracts.NftMarketplace.Endpoints as Marketplace +import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace +import Plutus.PAB.Effects.Contract (ContractEffect (..)) +import Plutus.PAB.Effects.Contract.Builtin (Builtin, + SomeBuiltin (..), + type (.\\)) +import qualified Plutus.PAB.Effects.Contract.Builtin as Builtin +import Plutus.PAB.Monitoring.PABLogMsg (PABMultiAgentMsg) +import Plutus.PAB.Simulator (Simulation, + SimulatorEffectHandlers) +import qualified Plutus.PAB.Simulator as Simulator +import Plutus.PAB.Types (PABError (..)) +import qualified Plutus.PAB.Webserver.Server as PAB.Server +import Plutus.V1.Ledger.Crypto (getPubKeyHash, + pubKeyHash) +import Prelude hiding (init) +import Wallet.Emulator.Types (Wallet (..), + walletPubKey) +import Wallet.Types (ContractInstanceId) + +ownerWallet :: Wallet +ownerWallet = Wallet 1 + +userWallets :: [Wallet] +userWallets = [Wallet i | i <- [2 .. 4]] + +data ContractIDs = ContractIDs { cidUser :: Map.Map Wallet ContractInstanceId, cidInfo :: ContractInstanceId } + +activateContracts :: Simulation (Builtin MarketplaceContracts) ContractIDs +activateContracts = do + cidStart <- Simulator.activateContract ownerWallet MarketplaceStart + _ <- Simulator.callEndpointOnInstance cidStart "start" () + mp <- flip Simulator.waitForState cidStart $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.OwnerContractState)) of + Success (ContractSuccess (Marketplace.Started mp)) -> Just mp + _ -> Nothing + Simulator.logString @(Builtin MarketplaceContracts) $ "Marketplace instance created: " ++ show mp + + -- cidInfo <- Simulator.activateContract ownerWallet $ MarketplaceInfo mp + + -- cidUser <- fmap Map.fromList $ forM userWallets $ \w -> do + -- cid <- Simulator.activateContract w $ MarketplaceUser mp + -- Simulator.logString @(Builtin MarketplaceContracts) $ "Marketplace user contract started for " ++ show w + -- return (w, cid) + + pure $ ContractIDs Map.empty cidStart + runNftMarketplace :: IO () -runNftMarketplace = pure () +runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do + Simulator.logString @(Builtin MarketplaceContracts) "Starting Marketplace PAB webserver on port 8080. Press enter to exit." + shutdown <- PAB.Server.startServerDebug + ContractIDs {..} <- activateContracts + let userCid = cidUser Map.! Wallet 2 + sender = pubKeyHash . walletPubKey $ Wallet 2 + + _ <- liftIO getLine + shutdown + +data MarketplaceContracts = + MarketplaceStart + -- | MarketplaceInfo Marketplace.Marketplace + -- | MarketplaceUser Marketplace.Marketplace + deriving (Eq, Show, Generic) + deriving anyclass (FromJSON, ToJSON) + +instance Pretty MarketplaceContracts where + pretty = viaShow + +handleMarketplaceContract :: + ( Member (Error PABError) effs + , Member (LogMsg (PABMultiAgentMsg (Builtin MarketplaceContracts))) effs + ) + => ContractEffect (Builtin MarketplaceContracts) + ~> Eff effs +handleMarketplaceContract = Builtin.handleBuiltin getSchema getContract where + getSchema = \case + -- MarketplaceUser _ -> Builtin.endpointsToSchemas @Marketplace.MarketplaceUserSchema + -- MarketplaceInfo _ -> Builtin.endpointsToSchemas @Marketplace.MarketplaceInfoSchema + MarketplaceStart -> Builtin.endpointsToSchemas @Marketplace.MarketplaceOwnerSchema + -- DistributeFunds _ _ -> Builtin.endpointsToSchemas @Empty + -- CreateOracles _ -> Builtin.endpointsToSchemas @Empty + getContract = \case + -- MarketplaceInfo marketplace -> SomeBuiltin $ Marketplace.infoEndpoints marketplace + -- MarketplaceUser marketplace -> SomeBuiltin $ Marketplace.userEndpoints marketplace + MarketplaceStart -> SomeBuiltin Marketplace.ownerEndpoints + -- DistributeFunds wallets assets -> SomeBuiltin $ distributeFunds wallets assets + -- CreateOracles assets -> SomeBuiltin $ createOracles assets + +handlers :: SimulatorEffectHandlers (Builtin MarketplaceContracts) +handlers = + Simulator.mkSimulatorHandlers @(Builtin MarketplaceContracts) [] + $ interpret handleMarketplaceContract + +oneAdaInLovelace :: Integer +oneAdaInLovelace = 1000000 From d7e957fef13d5364f703ff990a64a28f1a6890c5 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Wed, 21 Jul 2021 20:22:55 +0700 Subject: [PATCH 010/217] add create nft endpoint --- MetaLamp/nft-marketplace/plutus-starter.cabal | 2 +- .../src/Ext/Plutus/Ledger/Contexts.hs | 59 +++++++++ .../src/Ext/Plutus/Ledger/Value.hs | 10 ++ .../Contracts/NftMarketplace/Endpoints.hs | 1 + .../NftMarketplace/OffChain/Owner.hs | 6 +- .../Contracts/NftMarketplace/OffChain/User.hs | 117 ++++++++++++++++++ .../OnChain/Core/StateMachine.hs | 2 +- .../src/Plutus/PAB/Simulation.hs | 100 +++++++-------- 8 files changed, 242 insertions(+), 55 deletions(-) create mode 100644 MetaLamp/nft-marketplace/src/Ext/Plutus/Ledger/Contexts.hs create mode 100644 MetaLamp/nft-marketplace/src/Ext/Plutus/Ledger/Value.hs create mode 100644 MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs diff --git a/MetaLamp/nft-marketplace/plutus-starter.cabal b/MetaLamp/nft-marketplace/plutus-starter.cabal index 94f17cd9e..8b6554bc4 100644 --- a/MetaLamp/nft-marketplace/plutus-starter.cabal +++ b/MetaLamp/nft-marketplace/plutus-starter.cabal @@ -23,7 +23,7 @@ maintainer: Your email library exposed-modules: - Plutus.Contracts.NftMarketplace.OnChain.Core Plutus.Abstract.ContractResponse Plutus.Abstract.OutputValue Plutus.Abstract.TxUtils Plutus.Contracts.NftMarketplace.Endpoints Plutus.Contracts.NftMarketplace.OffChain.Owner Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine Plutus.PAB.Simulation Plutus.Contracts.NftMarketplace.OnChain.NFT + Plutus.Contracts.NftMarketplace.OnChain.Core Ext.Plutus.Ledger.Contexts Ext.Plutus.Ledger.Value Plutus.Contracts.NftMarketplace.OffChain.User Plutus.Abstract.ContractResponse Plutus.Abstract.OutputValue Plutus.Abstract.TxUtils Plutus.Contracts.NftMarketplace.Endpoints Plutus.Contracts.NftMarketplace.OffChain.Owner Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine Plutus.PAB.Simulation Plutus.Contracts.NftMarketplace.OnChain.NFT build-depends: base >= 4.9 && < 5, aeson, diff --git a/MetaLamp/nft-marketplace/src/Ext/Plutus/Ledger/Contexts.hs b/MetaLamp/nft-marketplace/src/Ext/Plutus/Ledger/Contexts.hs new file mode 100644 index 000000000..6a699ec23 --- /dev/null +++ b/MetaLamp/nft-marketplace/src/Ext/Plutus/Ledger/Contexts.hs @@ -0,0 +1,59 @@ +{-# LANGUAGE NoImplicitPrelude #-} +{-# OPTIONS_GHC -fno-specialise #-} +{-# LANGUAGE NamedFieldPuns #-} + +module Ext.Plutus.Ledger.Contexts where + +import Ledger (Address (Address), + Datum (getDatum), DatumHash, + PubKeyHash, + TxInInfo (txInInfoResolved), + TxInfo (txInfoInputs), + TxOut (TxOut, txOutAddress, txOutDatumHash, txOutValue), + ValidatorHash, Value, findDatum) +import Plutus.V1.Ledger.Credential (Credential (PubKeyCredential, ScriptCredential)) +import qualified PlutusTx +import PlutusTx.Prelude (Eq ((==)), Maybe (..), filter, + find, fst, mapMaybe, mconcat, + otherwise, snd, ($), (.), (<$>), + (>>=)) + +{-# INLINABLE findOnlyOneDatumHashByValue #-} +-- | Find the hash of a datum, if it is part of the script's outputs. +-- Assume search failed if more than one correspondence is found. +findOnlyOneDatumHashByValue :: Value -> [(DatumHash, Value)] -> Maybe DatumHash +findOnlyOneDatumHashByValue val outs = fst <$> case filter f outs of + [res] -> Just res + _ -> Nothing + where + f (_, val') = val' == val + +{-# INLINABLE findValueByDatumHash #-} +-- | Concat value of the script's outputs that have the specified hash of a datum +findValueByDatumHash :: DatumHash -> [(DatumHash, Value)] -> Value +findValueByDatumHash dh outs = mconcat $ mapMaybe f outs + where + f (dh', val) | dh' == dh = Just val + | otherwise = Nothing + +{-# INLINABLE parseDatum #-} +-- | Find datum inside pending transaction and parse it from data +parseDatum :: PlutusTx.IsData a => TxInfo -> DatumHash -> Maybe a +parseDatum txInfo dh = findDatum dh txInfo >>= (PlutusTx.fromData . getDatum) + +{-# INLINABLE valueSpentFrom #-} +-- | Concat value of the inputs belonging to the provided public key inside the pending transaction's inputs +valueSpentFrom :: TxInfo -> PubKeyHash -> Value +valueSpentFrom txInfo pk = + let flt TxOut {txOutAddress = Address (PubKeyCredential pk') _, txOutValue} + | pk == pk' = Just txOutValue + flt _ = Nothing + in mconcat $ mapMaybe flt (txInInfoResolved <$> txInfoInputs txInfo) + +{-# INLINABLE scriptInputsAt #-} +-- | Finds all inputs belonging to a specific script inside the pending transaction's inputs +scriptInputsAt :: ValidatorHash -> TxInfo -> [(DatumHash, Value)] +scriptInputsAt h p = + let flt TxOut{txOutDatumHash=Just ds, txOutAddress=Address (ScriptCredential s) _, txOutValue} | s == h = Just (ds, txOutValue) + flt _ = Nothing + in mapMaybe flt (txInInfoResolved <$> txInfoInputs p) diff --git a/MetaLamp/nft-marketplace/src/Ext/Plutus/Ledger/Value.hs b/MetaLamp/nft-marketplace/src/Ext/Plutus/Ledger/Value.hs new file mode 100644 index 000000000..591245d65 --- /dev/null +++ b/MetaLamp/nft-marketplace/src/Ext/Plutus/Ledger/Value.hs @@ -0,0 +1,10 @@ +module Ext.Plutus.Ledger.Value where + +import qualified Data.Map as Map +import Ledger (TxOut (txOutValue), + TxOutTx (txOutTxOut), Value) +import Ledger.AddressMap (UtxoMap) +import Plutus.V1.Ledger.Value (Value) + +utxoValue :: UtxoMap -> Value +utxoValue = foldMap (txOutValue . txOutTxOut . snd) . Map.toList diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/Endpoints.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/Endpoints.hs index df81fd9cd..8b06421c8 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/Endpoints.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/Endpoints.hs @@ -3,3 +3,4 @@ module Plutus.Contracts.NftMarketplace.Endpoints ) where import Plutus.Contracts.NftMarketplace.OffChain.Owner as Export +import Plutus.Contracts.NftMarketplace.OffChain.User as Export diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Owner.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Owner.hs index 8aaa051dd..0f34459a6 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Owner.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Owner.hs @@ -8,7 +8,9 @@ module Plutus.Contracts.NftMarketplace.OffChain.Owner where +import Control.Monad hiding (fmap) import qualified Data.Aeson as J +import Data.Proxy (Proxy (..)) import Data.Text (Text) import qualified Data.Text as T import qualified GHC.Generics as Haskell @@ -19,6 +21,7 @@ import Plutus.Abstract.ContractResponse (ContractResponse, withContractResponse) import Plutus.Contract import Plutus.Contract.StateMachine +import Plutus.Contracts.Currency as Currency import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Core import qualified PlutusTx import qualified PlutusTx.AssocMap as AssocMap @@ -26,9 +29,6 @@ import PlutusTx.Prelude hiding (Semigroup (..)) import Prelude (Semigroup (..)) import qualified Prelude as Haskell -import Control.Monad hiding (fmap) -import Data.Proxy (Proxy (..)) -import Plutus.Contracts.Currency as Currency import Text.Printf (printf) -- | Starts the NFT Marketplace protocol: minting protocol NFT, creating empty nft storage diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs new file mode 100644 index 000000000..1fe0718b6 --- /dev/null +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs @@ -0,0 +1,117 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeOperators #-} + +module Plutus.Contracts.NftMarketplace.OffChain.User where + +import qualified Control.Lens as Lens +import Control.Monad hiding (fmap) +import qualified Data.Aeson as J +import Data.Proxy (Proxy (..)) +import Data.Text (Text) +import qualified Data.Text as T +import Ext.Plutus.Ledger.Value (utxoValue) +import qualified GHC.Generics as Haskell +import Ledger +import qualified Ledger.Typed.Scripts as Scripts +import Ledger.Typed.Tx +import Ledger.Value +import Plutus.Abstract.ContractResponse (ContractResponse, + withContractResponse) +import Plutus.Contract +import Plutus.Contract.StateMachine +import Plutus.Contracts.Currency as Currency +import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Core +import qualified PlutusTx +import qualified PlutusTx.AssocMap as AssocMap +import PlutusTx.Prelude hiding + (Semigroup (..)) +import Prelude (Semigroup (..)) +import qualified Prelude as Haskell +import Text.Printf (printf) + +data CreateNftParams = + CreateNftParams { + cnpIpfsCid :: ByteString, + cnpNftName :: ByteString, + cnpNftDescription :: ByteString, + cnpRevealIssuer :: Bool + } + deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) + deriving anyclass (J.ToJSON, J.FromJSON) + +PlutusTx.unstableMakeIsData ''CreateNftParams +PlutusTx.makeLift ''CreateNftParams + +-- | The user puts N amount of his asset into a corresponding reserve, in exchange he gets N equivalent aTokens +createNft :: Core.Marketplace -> CreateNftParams -> Contract w s Text () +createNft marketplace CreateNftParams {..} = do + pkh <- getOwnPubKey + let tokenName = TokenName cnpIpfsCid + nft <- + mapError (T.pack . Haskell.show @Currency.CurrencyError) $ + Currency.forgeContract pkh [(tokenName, 1)] + + let client = Core.marketplaceClient marketplace + nftStore <- mapError' (getOnChainState client) >>= getStateDatum + let ipfsCidHash = sha2_256 cnpIpfsCid + let nftEntry = Core.NFT + { nftId = Currency.currencySymbol nft + , nftName = cnpNftName + , nftDescription = cnpNftDescription + , nftIssuer = if cnpRevealIssuer then Just pkh else Nothing + , nftIpfsCid = Nothing + } + void $ mapError' $ runStep client $ Core.CreateNftRedeemer ipfsCidHash nftEntry + + logInfo @Haskell.String $ printf "Created NFT %s with store entry %s" (Haskell.show nft) (Haskell.show nftEntry) + pure () + +-- | Gets all UTxOs belonging to a user and concats them into one Value +fundsAt :: PubKeyHash -> Contract w s Text Value +fundsAt pkh = utxoValue <$> utxoAt (pubKeyHashAddress pkh) + +balanceAt :: PubKeyHash -> AssetClass -> Contract w s Text Integer +balanceAt pkh asset = flip assetClassValueOf asset <$> fundsAt pkh + +getOwnPubKey :: Contract w s Text PubKeyHash +getOwnPubKey = pubKeyHash <$> ownPubKey + +ownPubKeyBalance :: Contract w s Text Value +ownPubKeyBalance = getOwnPubKey >>= fundsAt + +mapError' :: Contract w s SMContractError a -> Contract w s Text a +mapError' = mapError $ T.pack . Haskell.show + +getStateDatum :: + Maybe (OnChainState Core.MarketplaceDatum i, UtxoMap) -> Contract w s Text (AssocMap.Map Core.IpfsCidHash Core.NFT) +getStateDatum = maybe (throwError "Marketplace output not found") (pure . Core.getMarketplaceDatum . tyTxOutData . fst . fst) + +type MarketplaceUserSchema = + Endpoint "createNft" CreateNftParams + .\/ Endpoint "ownPubKey" () + .\/ Endpoint "ownPubKeyBalance" () + +data UserContractState = + NftCreated + | GetPubKey PubKeyHash + | GetPubKeyBalance Value + deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) + deriving anyclass (J.ToJSON, J.FromJSON) + +Lens.makeClassyPrisms ''UserContractState + +userEndpoints :: Core.Marketplace -> Contract (ContractResponse Text UserContractState) MarketplaceUserSchema Void () +userEndpoints marketplace = forever $ + withContractResponse (Proxy @"createNft") (const NftCreated) (createNft marketplace) + `select` withContractResponse (Proxy @"ownPubKey") GetPubKey (const getOwnPubKey) + `select` withContractResponse (Proxy @"ownPubKeyBalance") GetPubKeyBalance (const ownPubKeyBalance) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs index f9bbcac84..24b7fc04e 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs @@ -52,7 +52,7 @@ PlutusTx.makeLift ''NFT data MarketplaceRedeemer = StartRedeemer - | AddNftRedeemer IpfsCidHash NFT + | CreateNftRedeemer IpfsCidHash NFT deriving (Haskell.Show) PlutusTx.unstableMakeIsData ''MarketplaceRedeemer diff --git a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs index 015a56ae0..d9c395a0a 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs @@ -13,57 +13,57 @@ module Plutus.PAB.Simulation where -import Control.Monad (forM, forM_, void, - when) -import Control.Monad.Freer (Eff, Member, - interpret, - type (~>)) -import Control.Monad.Freer.Error (Error) -import Control.Monad.Freer.Extras.Log (LogMsg) -import Control.Monad.IO.Class (MonadIO (..)) -import Data.Aeson (FromJSON, - Result (..), - ToJSON, encode, - fromJSON) -import qualified Data.ByteString as BS -import qualified Data.Map.Strict as Map -import qualified Data.Monoid as Monoid -import qualified Data.Semigroup as Semigroup -import Data.Text (Text) -import Data.Text.Prettyprint.Doc (Pretty (..), - viaShow) -import GHC.Generics (Generic) +import Control.Monad (forM, forM_, + void, when) +import Control.Monad.Freer (Eff, Member, + interpret, + type (~>)) +import Control.Monad.Freer.Error (Error) +import Control.Monad.Freer.Extras.Log (LogMsg) +import Control.Monad.IO.Class (MonadIO (..)) +import Data.Aeson (FromJSON, + Result (..), + ToJSON, encode, + fromJSON) +import qualified Data.ByteString as BS +import qualified Data.Map.Strict as Map +import qualified Data.Monoid as Monoid +import qualified Data.Semigroup as Semigroup +import Data.Text (Text) +import Data.Text.Prettyprint.Doc (Pretty (..), + viaShow) +import GHC.Generics (Generic) import Ledger -import Ledger.Ada (adaSymbol, - adaToken, - adaValueOf, - lovelaceValueOf) +import Ledger.Ada (adaSymbol, + adaToken, + adaValueOf, + lovelaceValueOf) import Ledger.Constraints -import qualified Ledger.Constraints.OffChain as Constraints -import qualified Ledger.Typed.Scripts as Scripts -import Ledger.Value as Value -import Plutus.Abstract.ContractResponse (ContractResponse (..)) -import Plutus.Contract hiding (when) -import Plutus.Contracts.Currency as Currency -import qualified Plutus.Contracts.NftMarketplace.Endpoints as Marketplace -import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace -import Plutus.PAB.Effects.Contract (ContractEffect (..)) -import Plutus.PAB.Effects.Contract.Builtin (Builtin, - SomeBuiltin (..), - type (.\\)) -import qualified Plutus.PAB.Effects.Contract.Builtin as Builtin -import Plutus.PAB.Monitoring.PABLogMsg (PABMultiAgentMsg) -import Plutus.PAB.Simulator (Simulation, - SimulatorEffectHandlers) -import qualified Plutus.PAB.Simulator as Simulator -import Plutus.PAB.Types (PABError (..)) -import qualified Plutus.PAB.Webserver.Server as PAB.Server -import Plutus.V1.Ledger.Crypto (getPubKeyHash, - pubKeyHash) -import Prelude hiding (init) -import Wallet.Emulator.Types (Wallet (..), - walletPubKey) -import Wallet.Types (ContractInstanceId) +import qualified Ledger.Constraints.OffChain as Constraints +import qualified Ledger.Typed.Scripts as Scripts +import Ledger.Value as Value +import Plutus.Abstract.ContractResponse (ContractResponse (..)) +import Plutus.Contract hiding (when) +import Plutus.Contracts.Currency as Currency +import qualified Plutus.Contracts.NftMarketplace.Endpoints as Marketplace +import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace +import Plutus.PAB.Effects.Contract (ContractEffect (..)) +import Plutus.PAB.Effects.Contract.Builtin (Builtin, + SomeBuiltin (..), + type (.\\)) +import qualified Plutus.PAB.Effects.Contract.Builtin as Builtin +import Plutus.PAB.Monitoring.PABLogMsg (PABMultiAgentMsg) +import Plutus.PAB.Simulator (Simulation, + SimulatorEffectHandlers) +import qualified Plutus.PAB.Simulator as Simulator +import Plutus.PAB.Types (PABError (..)) +import qualified Plutus.PAB.Webserver.Server as PAB.Server +import Plutus.V1.Ledger.Crypto (getPubKeyHash, + pubKeyHash) +import Prelude hiding (init) +import Wallet.Emulator.Types (Wallet (..), + walletPubKey) +import Wallet.Types (ContractInstanceId) ownerWallet :: Wallet ownerWallet = Wallet 1 @@ -79,7 +79,7 @@ activateContracts = do _ <- Simulator.callEndpointOnInstance cidStart "start" () mp <- flip Simulator.waitForState cidStart $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.OwnerContractState)) of Success (ContractSuccess (Marketplace.Started mp)) -> Just mp - _ -> Nothing + _ -> Nothing Simulator.logString @(Builtin MarketplaceContracts) $ "Marketplace instance created: " ++ show mp -- cidInfo <- Simulator.activateContract ownerWallet $ MarketplaceInfo mp From b83cdcf23cbd28f7bf016a2f552278bab8eafc8d Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Thu, 22 Jul 2021 14:05:20 +0700 Subject: [PATCH 011/217] add create nft transition validation --- .../OnChain/Core/StateMachine.hs | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs index 24b7fc04e..31f08beee 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs @@ -4,6 +4,7 @@ {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeApplications #-} @@ -14,6 +15,7 @@ import qualified Data.Aeson as J import qualified Data.Text as T import qualified GHC.Generics as Haskell import Ledger +import qualified Ledger.Constraints as Constraints import qualified Ledger.Typed.Scripts as Scripts import Ledger.Value import Plutus.Contract @@ -51,8 +53,7 @@ PlutusTx.unstableMakeIsData ''NFT PlutusTx.makeLift ''NFT data MarketplaceRedeemer - = StartRedeemer - | CreateNftRedeemer IpfsCidHash NFT + = CreateNftRedeemer IpfsCidHash NFT deriving (Haskell.Show) PlutusTx.unstableMakeIsData ''MarketplaceRedeemer @@ -71,17 +72,32 @@ PlutusTx.makeLift ''MarketplaceDatum {-# INLINABLE transition #-} transition :: Marketplace -> State MarketplaceDatum -> MarketplaceRedeemer -> Maybe (TxConstraints Void Void, State MarketplaceDatum) -transition marketplace s r = Nothing +transition marketplace state redeemer = case redeemer of + CreateNftRedeemer ipfsCidHash nftEntry + -> Just ( mustBeSignedByIssuer nftEntry + , State (MarketplaceDatum $ AssocMap.insert ipfsCidHash nftEntry nftStore) mempty + ) + _ -> Nothing where - token :: Value - token = assetClassValue (marketplaceProtocolToken marketplace) 1 + nftStore :: AssocMap.Map IpfsCidHash NFT + nftStore = getMarketplaceDatum $ stateData state + + mustBeSignedByIssuer entry = case nftIssuer entry of + Just pkh -> Constraints.mustBeSignedBy pkh + Nothing -> mempty + +{-# INLINABLE stateTransitionCheck #-} +stateTransitionCheck :: MarketplaceDatum -> MarketplaceRedeemer -> ScriptContext -> Bool +stateTransitionCheck (MarketplaceDatum nftStore) (CreateNftRedeemer ipfsCidHash nftEntry) ctx = + traceIfFalse "Nft entry already exists" $ + isNothing $ AssocMap.lookup ipfsCidHash nftStore {-# INLINABLE marketplaceStateMachine #-} marketplaceStateMachine :: Marketplace -> StateMachine MarketplaceDatum MarketplaceRedeemer marketplaceStateMachine marketplace = StateMachine { smTransition = transition marketplace , smFinal = const False - , smCheck = \d r ctx -> True + , smCheck = stateTransitionCheck , smThreadToken = Just $ marketplaceProtocolToken marketplace } From bfc74aaa23404a97b07a069166c3914b177df5c7 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Thu, 22 Jul 2021 16:19:19 +0700 Subject: [PATCH 012/217] add info endpoints --- MetaLamp/nft-marketplace/plutus-starter.cabal | 2 +- .../Contracts/NftMarketplace/Endpoints.hs | 1 + .../Contracts/NftMarketplace/OffChain/Info.hs | 79 +++++++++++++++++++ .../Contracts/NftMarketplace/OffChain/User.hs | 55 ++++++------- 4 files changed, 104 insertions(+), 33 deletions(-) create mode 100644 MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs diff --git a/MetaLamp/nft-marketplace/plutus-starter.cabal b/MetaLamp/nft-marketplace/plutus-starter.cabal index 8b6554bc4..31a4e25f2 100644 --- a/MetaLamp/nft-marketplace/plutus-starter.cabal +++ b/MetaLamp/nft-marketplace/plutus-starter.cabal @@ -23,7 +23,7 @@ maintainer: Your email library exposed-modules: - Plutus.Contracts.NftMarketplace.OnChain.Core Ext.Plutus.Ledger.Contexts Ext.Plutus.Ledger.Value Plutus.Contracts.NftMarketplace.OffChain.User Plutus.Abstract.ContractResponse Plutus.Abstract.OutputValue Plutus.Abstract.TxUtils Plutus.Contracts.NftMarketplace.Endpoints Plutus.Contracts.NftMarketplace.OffChain.Owner Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine Plutus.PAB.Simulation Plutus.Contracts.NftMarketplace.OnChain.NFT + Plutus.Contracts.NftMarketplace.OnChain.Core Plutus.Contracts.NftMarketplace.OffChain.Info Ext.Plutus.Ledger.Contexts Ext.Plutus.Ledger.Value Plutus.Contracts.NftMarketplace.OffChain.User Plutus.Abstract.ContractResponse Plutus.Abstract.OutputValue Plutus.Abstract.TxUtils Plutus.Contracts.NftMarketplace.Endpoints Plutus.Contracts.NftMarketplace.OffChain.Owner Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine Plutus.PAB.Simulation Plutus.Contracts.NftMarketplace.OnChain.NFT build-depends: base >= 4.9 && < 5, aeson, diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/Endpoints.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/Endpoints.hs index 8b06421c8..b821ae4d3 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/Endpoints.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/Endpoints.hs @@ -2,5 +2,6 @@ module Plutus.Contracts.NftMarketplace.Endpoints ( module Export ) where +import Plutus.Contracts.NftMarketplace.OffChain.Info as Export import Plutus.Contracts.NftMarketplace.OffChain.Owner as Export import Plutus.Contracts.NftMarketplace.OffChain.User as Export diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs new file mode 100644 index 000000000..0131635af --- /dev/null +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs @@ -0,0 +1,79 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeOperators #-} + +module Plutus.Contracts.NftMarketplace.OffChain.Info where + +import qualified Control.Lens as Lens +import Control.Monad hiding (fmap) +import qualified Data.Aeson as J +import Data.Proxy (Proxy (..)) +import Data.Text (Text) +import qualified Data.Text as T +import Ext.Plutus.Ledger.Value (utxoValue) +import qualified GHC.Generics as Haskell +import Ledger +import qualified Ledger.Typed.Scripts as Scripts +import Ledger.Typed.Tx +import Ledger.Value +import Plutus.Abstract.ContractResponse (ContractResponse, + withContractResponse) +import Plutus.Contract +import Plutus.Contract.StateMachine +import Plutus.Contracts.Currency as Currency +import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Core +import qualified PlutusTx +import qualified PlutusTx.AssocMap as AssocMap +import PlutusTx.Prelude hiding + (Semigroup (..)) +import Prelude (Semigroup (..)) +import qualified Prelude as Haskell +import Text.Printf (printf) + +-- | Gets current Marketplace store state +marketplaceStore :: Core.Marketplace -> Contract w s Text (AssocMap.Map Core.IpfsCidHash Core.NFT) +marketplaceStore marketplace = do + let client = Core.marketplaceClient marketplace + mapError' (getOnChainState client) >>= getStateDatum + +getStateDatum :: + Maybe (OnChainState Core.MarketplaceDatum i, UtxoMap) -> Contract w s Text (AssocMap.Map Core.IpfsCidHash Core.NFT) +getStateDatum = maybe (throwError "Marketplace output not found") (pure . Core.getMarketplaceDatum . tyTxOutData . fst . fst) + +-- | Gets all UTxOs belonging to a user and concats them into one Value +fundsAt :: PubKeyHash -> Contract w s Text Value +fundsAt pkh = utxoValue <$> utxoAt (pubKeyHashAddress pkh) + +-- | Gets all UTxOs belonging to the Marketplace script and concats them into one Value +marketplaceFunds :: Core.Marketplace -> Contract w s Text Value +marketplaceFunds marketplace = utxoValue <$> utxoAt (Core.marketplaceAddress marketplace) + +mapError' :: Contract w s SMContractError a -> Contract w s Text a +mapError' = mapError $ T.pack . Haskell.show + +type MarketplaceInfoSchema = + Endpoint "fundsAt" PubKeyHash + .\/ Endpoint "marketplaceFunds" () + .\/ Endpoint "marketplaceStore" () + +data InfoContractState = + FundsAt Value + | MarketplaceFunds Value + | MarketplaceStore (AssocMap.Map Core.IpfsCidHash Core.NFT) + deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) + deriving anyclass (J.ToJSON, J.FromJSON) + +infoEndpoints :: Core.Marketplace -> Contract (ContractResponse Text InfoContractState) MarketplaceInfoSchema Void () +infoEndpoints marketplace = forever $ + withContractResponse (Proxy @"fundsAt") FundsAt fundsAt + `select` withContractResponse (Proxy @"marketplaceFunds") MarketplaceFunds (const $ marketplaceFunds marketplace) + `select` withContractResponse (Proxy @"marketplaceStore") MarketplaceStore (const $ marketplaceStore marketplace) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs index 1fe0718b6..9823c10ef 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs @@ -13,31 +13,33 @@ module Plutus.Contracts.NftMarketplace.OffChain.User where -import qualified Control.Lens as Lens -import Control.Monad hiding (fmap) -import qualified Data.Aeson as J -import Data.Proxy (Proxy (..)) -import Data.Text (Text) -import qualified Data.Text as T -import Ext.Plutus.Ledger.Value (utxoValue) -import qualified GHC.Generics as Haskell +import qualified Control.Lens as Lens +import Control.Monad hiding (fmap) +import qualified Data.Aeson as J +import Data.Proxy (Proxy (..)) +import Data.Text (Text) +import qualified Data.Text as T +import Ext.Plutus.Ledger.Value (utxoValue) +import qualified GHC.Generics as Haskell import Ledger -import qualified Ledger.Typed.Scripts as Scripts +import qualified Ledger.Typed.Scripts as Scripts import Ledger.Typed.Tx import Ledger.Value -import Plutus.Abstract.ContractResponse (ContractResponse, - withContractResponse) +import Plutus.Abstract.ContractResponse (ContractResponse, + withContractResponse) import Plutus.Contract import Plutus.Contract.StateMachine -import Plutus.Contracts.Currency as Currency -import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Core +import Plutus.Contracts.Currency as Currency +import Plutus.Contracts.NftMarketplace.OffChain.Info (fundsAt, + mapError') +import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Core import qualified PlutusTx -import qualified PlutusTx.AssocMap as AssocMap -import PlutusTx.Prelude hiding - (Semigroup (..)) -import Prelude (Semigroup (..)) -import qualified Prelude as Haskell -import Text.Printf (printf) +import qualified PlutusTx.AssocMap as AssocMap +import PlutusTx.Prelude hiding + (Semigroup (..)) +import Prelude (Semigroup (..)) +import qualified Prelude as Haskell +import Text.Printf (printf) data CreateNftParams = CreateNftParams { @@ -52,7 +54,8 @@ data CreateNftParams = PlutusTx.unstableMakeIsData ''CreateNftParams PlutusTx.makeLift ''CreateNftParams --- | The user puts N amount of his asset into a corresponding reserve, in exchange he gets N equivalent aTokens +-- | The user specifizes which NFT to mint and add to marketplace store, +-- he gets it into his wallet and the corresponding store entry is created createNft :: Core.Marketplace -> CreateNftParams -> Contract w s Text () createNft marketplace CreateNftParams {..} = do pkh <- getOwnPubKey @@ -62,7 +65,6 @@ createNft marketplace CreateNftParams {..} = do Currency.forgeContract pkh [(tokenName, 1)] let client = Core.marketplaceClient marketplace - nftStore <- mapError' (getOnChainState client) >>= getStateDatum let ipfsCidHash = sha2_256 cnpIpfsCid let nftEntry = Core.NFT { nftId = Currency.currencySymbol nft @@ -76,10 +78,6 @@ createNft marketplace CreateNftParams {..} = do logInfo @Haskell.String $ printf "Created NFT %s with store entry %s" (Haskell.show nft) (Haskell.show nftEntry) pure () --- | Gets all UTxOs belonging to a user and concats them into one Value -fundsAt :: PubKeyHash -> Contract w s Text Value -fundsAt pkh = utxoValue <$> utxoAt (pubKeyHashAddress pkh) - balanceAt :: PubKeyHash -> AssetClass -> Contract w s Text Integer balanceAt pkh asset = flip assetClassValueOf asset <$> fundsAt pkh @@ -89,13 +87,6 @@ getOwnPubKey = pubKeyHash <$> ownPubKey ownPubKeyBalance :: Contract w s Text Value ownPubKeyBalance = getOwnPubKey >>= fundsAt -mapError' :: Contract w s SMContractError a -> Contract w s Text a -mapError' = mapError $ T.pack . Haskell.show - -getStateDatum :: - Maybe (OnChainState Core.MarketplaceDatum i, UtxoMap) -> Contract w s Text (AssocMap.Map Core.IpfsCidHash Core.NFT) -getStateDatum = maybe (throwError "Marketplace output not found") (pure . Core.getMarketplaceDatum . tyTxOutData . fst . fst) - type MarketplaceUserSchema = Endpoint "createNft" CreateNftParams .\/ Endpoint "ownPubKey" () From 429ca0366d6cfb1a1ebfe9b1ee83fe8bc80817da Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Thu, 22 Jul 2021 17:18:58 +0700 Subject: [PATCH 013/217] add simulation test case --- .../Contracts/NftMarketplace/OffChain/User.hs | 3 +- .../src/Plutus/PAB/Simulation.hs | 59 ++++++++++++++----- 2 files changed, 45 insertions(+), 17 deletions(-) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs index 9823c10ef..e48cf9b5d 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs @@ -39,6 +39,7 @@ import PlutusTx.Prelude hiding (Semigroup (..)) import Prelude (Semigroup (..)) import qualified Prelude as Haskell +import qualified Schema import Text.Printf (printf) data CreateNftParams = @@ -49,7 +50,7 @@ data CreateNftParams = cnpRevealIssuer :: Bool } deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) - deriving anyclass (J.ToJSON, J.FromJSON) + deriving anyclass (J.ToJSON, J.FromJSON, Schema.ToSchema) PlutusTx.unstableMakeIsData ''CreateNftParams PlutusTx.makeLift ''CreateNftParams diff --git a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs index d9c395a0a..8efd65663 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs @@ -82,14 +82,14 @@ activateContracts = do _ -> Nothing Simulator.logString @(Builtin MarketplaceContracts) $ "Marketplace instance created: " ++ show mp - -- cidInfo <- Simulator.activateContract ownerWallet $ MarketplaceInfo mp + cidInfo <- Simulator.activateContract ownerWallet $ MarketplaceInfo mp - -- cidUser <- fmap Map.fromList $ forM userWallets $ \w -> do - -- cid <- Simulator.activateContract w $ MarketplaceUser mp - -- Simulator.logString @(Builtin MarketplaceContracts) $ "Marketplace user contract started for " ++ show w - -- return (w, cid) + users <- fmap Map.fromList $ forM userWallets $ \w -> do + cid <- Simulator.activateContract w $ MarketplaceUser mp + Simulator.logString @(Builtin MarketplaceContracts) $ "Marketplace user contract started for " ++ show w + return (w, cid) - pure $ ContractIDs Map.empty cidStart + pure $ ContractIDs users cidInfo runNftMarketplace :: IO () runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do @@ -99,13 +99,44 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do let userCid = cidUser Map.! Wallet 2 sender = pubKeyHash . walletPubKey $ Wallet 2 + _ <- + Simulator.callEndpointOnInstance userCid "createNft" $ + Marketplace.CreateNftParams { + cnpIpfsCid = "QmPeoJnaDttpFrSySYBY3reRFCzL3qv4Uiqz376EBv9W16", + cnpNftName = "Cat token", + cnpNftDescription = "A picture of a cat on a pogo stick", + cnpRevealIssuer = False + } + flip Simulator.waitForState userCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of + Success (ContractSuccess Marketplace.NftCreated) -> Just () + _ -> Nothing + Simulator.logString @(Builtin MarketplaceContracts) $ "Successful createNft" + + _ <- Simulator.callEndpointOnInstance cidInfo "fundsAt" sender + v <- flip Simulator.waitForState cidInfo $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.InfoContractState)) of + Success (ContractSuccess (Marketplace.FundsAt v)) -> Just v + _ -> Nothing + Simulator.logString @(Builtin MarketplaceContracts) $ "Final user funds: " <> show v + + _ <- Simulator.callEndpointOnInstance cidInfo "marketplaceStore" () + marketplaceStore <- flip Simulator.waitForState cidInfo $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.InfoContractState)) of + Success (ContractSuccess (Marketplace.MarketplaceStore marketplaceStore)) -> Just marketplaceStore + _ -> Nothing + Simulator.logString @(Builtin MarketplaceContracts) $ "Final marketplaceStore: " <> show marketplaceStore + + _ <- Simulator.callEndpointOnInstance cidInfo "marketplaceFunds" () + v <- flip Simulator.waitForState cidInfo $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.InfoContractState)) of + Success (ContractSuccess (Marketplace.MarketplaceFunds v)) -> Just v + _ -> Nothing + Simulator.logString @(Builtin MarketplaceContracts) $ "Final marketplace funds: " <> show v + _ <- liftIO getLine shutdown data MarketplaceContracts = MarketplaceStart - -- | MarketplaceInfo Marketplace.Marketplace - -- | MarketplaceUser Marketplace.Marketplace + | MarketplaceInfo Marketplace.Marketplace + | MarketplaceUser Marketplace.Marketplace deriving (Eq, Show, Generic) deriving anyclass (FromJSON, ToJSON) @@ -120,17 +151,13 @@ handleMarketplaceContract :: ~> Eff effs handleMarketplaceContract = Builtin.handleBuiltin getSchema getContract where getSchema = \case - -- MarketplaceUser _ -> Builtin.endpointsToSchemas @Marketplace.MarketplaceUserSchema - -- MarketplaceInfo _ -> Builtin.endpointsToSchemas @Marketplace.MarketplaceInfoSchema + MarketplaceUser _ -> Builtin.endpointsToSchemas @Marketplace.MarketplaceUserSchema + MarketplaceInfo _ -> Builtin.endpointsToSchemas @Marketplace.MarketplaceInfoSchema MarketplaceStart -> Builtin.endpointsToSchemas @Marketplace.MarketplaceOwnerSchema - -- DistributeFunds _ _ -> Builtin.endpointsToSchemas @Empty - -- CreateOracles _ -> Builtin.endpointsToSchemas @Empty getContract = \case - -- MarketplaceInfo marketplace -> SomeBuiltin $ Marketplace.infoEndpoints marketplace - -- MarketplaceUser marketplace -> SomeBuiltin $ Marketplace.userEndpoints marketplace + MarketplaceInfo marketplace -> SomeBuiltin $ Marketplace.infoEndpoints marketplace + MarketplaceUser marketplace -> SomeBuiltin $ Marketplace.userEndpoints marketplace MarketplaceStart -> SomeBuiltin Marketplace.ownerEndpoints - -- DistributeFunds wallets assets -> SomeBuiltin $ distributeFunds wallets assets - -- CreateOracles assets -> SomeBuiltin $ createOracles assets handlers :: SimulatorEffectHandlers (Builtin MarketplaceContracts) handlers = From 93b88a61eeeca165f0ef0a095d14dceaa91b5289 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Thu, 22 Jul 2021 18:01:00 +0700 Subject: [PATCH 014/217] add existing nft check --- MetaLamp/nft-marketplace/plutus-starter.cabal | 2 +- .../src/Plutus/Contracts/NftMarketplace/OffChain/User.hs | 8 ++++++-- .../src/Plutus/Contracts/NftMarketplace/OnChain/NFT.hs | 1 - 3 files changed, 7 insertions(+), 4 deletions(-) delete mode 100644 MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/NFT.hs diff --git a/MetaLamp/nft-marketplace/plutus-starter.cabal b/MetaLamp/nft-marketplace/plutus-starter.cabal index 31a4e25f2..ac015e431 100644 --- a/MetaLamp/nft-marketplace/plutus-starter.cabal +++ b/MetaLamp/nft-marketplace/plutus-starter.cabal @@ -23,7 +23,7 @@ maintainer: Your email library exposed-modules: - Plutus.Contracts.NftMarketplace.OnChain.Core Plutus.Contracts.NftMarketplace.OffChain.Info Ext.Plutus.Ledger.Contexts Ext.Plutus.Ledger.Value Plutus.Contracts.NftMarketplace.OffChain.User Plutus.Abstract.ContractResponse Plutus.Abstract.OutputValue Plutus.Abstract.TxUtils Plutus.Contracts.NftMarketplace.Endpoints Plutus.Contracts.NftMarketplace.OffChain.Owner Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine Plutus.PAB.Simulation Plutus.Contracts.NftMarketplace.OnChain.NFT + Plutus.Contracts.NftMarketplace.OnChain.Core Plutus.Contracts.NftMarketplace.OffChain.Info Ext.Plutus.Ledger.Contexts Ext.Plutus.Ledger.Value Plutus.Contracts.NftMarketplace.OffChain.User Plutus.Abstract.ContractResponse Plutus.Abstract.OutputValue Plutus.Abstract.TxUtils Plutus.Contracts.NftMarketplace.Endpoints Plutus.Contracts.NftMarketplace.OffChain.Owner Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine Plutus.PAB.Simulation build-depends: base >= 4.9 && < 5, aeson, diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs index e48cf9b5d..016d8459a 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs @@ -31,7 +31,8 @@ import Plutus.Contract import Plutus.Contract.StateMachine import Plutus.Contracts.Currency as Currency import Plutus.Contracts.NftMarketplace.OffChain.Info (fundsAt, - mapError') + mapError', + marketplaceStore) import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Core import qualified PlutusTx import qualified PlutusTx.AssocMap as AssocMap @@ -59,6 +60,10 @@ PlutusTx.makeLift ''CreateNftParams -- he gets it into his wallet and the corresponding store entry is created createNft :: Core.Marketplace -> CreateNftParams -> Contract w s Text () createNft marketplace CreateNftParams {..} = do + let ipfsCidHash = sha2_256 cnpIpfsCid + nftStore <- marketplaceStore marketplace + when (isJust $ AssocMap.lookup ipfsCidHash nftStore) $ throwError "Nft entry already exists" + pkh <- getOwnPubKey let tokenName = TokenName cnpIpfsCid nft <- @@ -66,7 +71,6 @@ createNft marketplace CreateNftParams {..} = do Currency.forgeContract pkh [(tokenName, 1)] let client = Core.marketplaceClient marketplace - let ipfsCidHash = sha2_256 cnpIpfsCid let nftEntry = Core.NFT { nftId = Currency.currencySymbol nft , nftName = cnpNftName diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/NFT.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/NFT.hs deleted file mode 100644 index 1d769aad1..000000000 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/NFT.hs +++ /dev/null @@ -1 +0,0 @@ -module Plutus.Contracts.NftMarketplace.OnChain.NFT where From 28f8c7038c45c96bbc3678716a831b5984bd8380 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Mon, 26 Jul 2021 20:41:56 +0700 Subject: [PATCH 015/217] add todos --- .../src/Plutus/Contracts/NftMarketplace/OffChain/User.hs | 4 ++-- .../Contracts/NftMarketplace/OnChain/Core/StateMachine.hs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs index 016d8459a..76d7d3849 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs @@ -56,7 +56,7 @@ data CreateNftParams = PlutusTx.unstableMakeIsData ''CreateNftParams PlutusTx.makeLift ''CreateNftParams --- | The user specifizes which NFT to mint and add to marketplace store, +-- | The user specifies which NFT to mint and add to marketplace store, -- he gets it into his wallet and the corresponding store entry is created createNft :: Core.Marketplace -> CreateNftParams -> Contract w s Text () createNft marketplace CreateNftParams {..} = do @@ -76,7 +76,7 @@ createNft marketplace CreateNftParams {..} = do , nftName = cnpNftName , nftDescription = cnpNftDescription , nftIssuer = if cnpRevealIssuer then Just pkh else Nothing - , nftIpfsCid = Nothing + , nftIpfsCid = Nothing -- TODO validate that it's Nothing (?) } void $ mapError' $ runStep client $ Core.CreateNftRedeemer ipfsCidHash nftEntry diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs index 31f08beee..e6105444b 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs @@ -74,6 +74,7 @@ PlutusTx.makeLift ''MarketplaceDatum transition :: Marketplace -> State MarketplaceDatum -> MarketplaceRedeemer -> Maybe (TxConstraints Void Void, State MarketplaceDatum) transition marketplace state redeemer = case redeemer of CreateNftRedeemer ipfsCidHash nftEntry + -- TODO check that ipfsCidHash is a hash (?) -> Just ( mustBeSignedByIssuer nftEntry , State (MarketplaceDatum $ AssocMap.insert ipfsCidHash nftEntry nftStore) mempty ) From ef922d6feee278a806c4cc0c1b7cbe776705455a Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Tue, 27 Jul 2021 19:13:30 +0700 Subject: [PATCH 016/217] add sale state machine --- MetaLamp/nft-marketplace/plutus-starter.cabal | 2 +- .../OnChain/Core/StateMachine.hs | 2 +- .../src/Plutus/Contracts/Services/Sale.hs | 3 + .../Contracts/Services/Sale/StateMachine.hs | 114 ++++++++++++++++++ 4 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale.hs create mode 100644 MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/StateMachine.hs diff --git a/MetaLamp/nft-marketplace/plutus-starter.cabal b/MetaLamp/nft-marketplace/plutus-starter.cabal index ac015e431..c7f455b8d 100644 --- a/MetaLamp/nft-marketplace/plutus-starter.cabal +++ b/MetaLamp/nft-marketplace/plutus-starter.cabal @@ -23,7 +23,7 @@ maintainer: Your email library exposed-modules: - Plutus.Contracts.NftMarketplace.OnChain.Core Plutus.Contracts.NftMarketplace.OffChain.Info Ext.Plutus.Ledger.Contexts Ext.Plutus.Ledger.Value Plutus.Contracts.NftMarketplace.OffChain.User Plutus.Abstract.ContractResponse Plutus.Abstract.OutputValue Plutus.Abstract.TxUtils Plutus.Contracts.NftMarketplace.Endpoints Plutus.Contracts.NftMarketplace.OffChain.Owner Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine Plutus.PAB.Simulation + Plutus.Contracts.NftMarketplace.OnChain.Core Plutus.Contracts.Services.Sale Plutus.Contracts.Services.Sale.StateMachine Plutus.Contracts.NftMarketplace.OffChain.Info Ext.Plutus.Ledger.Contexts Ext.Plutus.Ledger.Value Plutus.Contracts.NftMarketplace.OffChain.User Plutus.Abstract.ContractResponse Plutus.Abstract.OutputValue Plutus.Abstract.TxUtils Plutus.Contracts.NftMarketplace.Endpoints Plutus.Contracts.NftMarketplace.OffChain.Owner Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine Plutus.PAB.Simulation build-depends: base >= 4.9 && < 5, aeson, diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs index e6105444b..c1646985c 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs @@ -60,7 +60,7 @@ PlutusTx.unstableMakeIsData ''MarketplaceRedeemer PlutusTx.makeLift ''MarketplaceRedeemer -data MarketplaceDatum = +newtype MarketplaceDatum = MarketplaceDatum { getMarketplaceDatum :: AssocMap.Map IpfsCidHash NFT } diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale.hs new file mode 100644 index 000000000..6336fdd2a --- /dev/null +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale.hs @@ -0,0 +1,3 @@ +module Plutus.Contracts.Services.Sale (module Export) where + +import Plutus.Contracts.Services.Sale.StateMachine as Export diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/StateMachine.hs new file mode 100644 index 000000000..36320e0c1 --- /dev/null +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/StateMachine.hs @@ -0,0 +1,114 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TypeApplications #-} + +module Plutus.Contracts.Services.Sale.StateMachine where + +import qualified Data.Aeson as J +import qualified Data.Text as T +import qualified GHC.Generics as Haskell +import Ledger +import qualified Ledger.Ada as Ada +import qualified Ledger.Constraints as Constraints +import qualified Ledger.Typed.Scripts as Scripts +import Ledger.Value +import Plutus.Contract +import Plutus.Contract.StateMachine +import qualified PlutusTx +import qualified PlutusTx.AssocMap as AssocMap +import PlutusTx.Prelude hiding (Semigroup (..)) +import Prelude (Semigroup (..)) +import qualified Prelude as Haskell + +type Saler = PubKeyHash +type Buyer = PubKeyHash +type LovelacePrice = Integer + +data Sale = + Sale + { saleProtocolToken :: AssetClass, + salePrice :: LovelacePrice, + saleValue :: Value + } + deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) + deriving anyclass (J.ToJSON, J.FromJSON) + +PlutusTx.makeLift ''Sale + +data SaleRedeemer + = Buy Buyer + | Redeem + deriving (Haskell.Show) + +PlutusTx.unstableMakeIsData ''SaleRedeemer + +PlutusTx.makeLift ''SaleRedeemer + +data SaleDatum = + LotInfo Saler + | SaleClosed + deriving (Haskell.Show) + +PlutusTx.unstableMakeIsData ''SaleDatum + +PlutusTx.makeLift ''SaleDatum + +{-# INLINABLE transition #-} +transition :: Sale -> State SaleDatum -> SaleRedeemer -> Maybe (TxConstraints Void Void, State SaleDatum) +transition Sale{..} state redeemer = case (stateData state, redeemer) of + (LotInfo saler, Redeem) | saleValue == val + -> Just ( Constraints.mustBeSignedBy saler <> + Constraints.mustPayToPubKey saler (stateToken <> saleValue) + , State SaleClosed mempty + ) + (LotInfo saler, Buy buyer) | saleValue == val + -> Just ( Constraints.mustBeSignedBy buyer <> + Constraints.mustPayToPubKey saler (stateToken <> Ada.lovelaceValueOf salePrice) <> + Constraints.mustPayToPubKey buyer saleValue + , State SaleClosed mempty + ) + _ -> Nothing + where + stateToken :: Value + stateToken = assetClassValue saleProtocolToken 1 + + val = stateValue state + +{-# INLINABLE isFinal #-} +isFinal :: SaleDatum -> Bool +isFinal SaleClosed = True +isFinal _ = False + +{-# INLINABLE saleStateMachine #-} +saleStateMachine :: Sale -> StateMachine SaleDatum SaleRedeemer +saleStateMachine sale = StateMachine + { smTransition = transition sale + , smFinal = isFinal + , smCheck = \d r ctx -> True + , smThreadToken = Just $ saleProtocolToken sale + } + +{-# INLINABLE mkSaleValidator #-} +mkSaleValidator :: Sale -> SaleDatum -> SaleRedeemer -> ScriptContext -> Bool +mkSaleValidator sale = mkValidator $ saleStateMachine sale + +type SaleScript = StateMachine SaleDatum SaleRedeemer + +saleInst :: Sale -> Scripts.TypedValidator SaleScript +saleInst sale = Scripts.mkTypedValidator @SaleScript + ($$(PlutusTx.compile [|| mkSaleValidator ||]) + `PlutusTx.applyCode` PlutusTx.liftCode sale) + $$(PlutusTx.compile [|| wrap ||]) + where + wrap = Scripts.wrapValidator @SaleDatum @SaleRedeemer + +saleClient :: Sale -> StateMachineClient SaleDatum SaleRedeemer +saleClient sale = mkStateMachineClient $ StateMachineInstance (saleStateMachine sale) (saleInst sale) From 62ff6d813adc8e5cfe4664647ff6d80d3e5e8703 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Wed, 28 Jul 2021 17:23:35 +0700 Subject: [PATCH 017/217] add sale contract endpoints --- MetaLamp/nft-marketplace/plutus-starter.cabal | 2 +- .../src/Plutus/Contracts/Services/Sale.hs | 1 + .../Contracts/Services/Sale/Endpoints.hs | 93 +++++++++++++++++++ .../Contracts/Services/Sale/StateMachine.hs | 13 ++- 4 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/Endpoints.hs diff --git a/MetaLamp/nft-marketplace/plutus-starter.cabal b/MetaLamp/nft-marketplace/plutus-starter.cabal index c7f455b8d..c7d10eda5 100644 --- a/MetaLamp/nft-marketplace/plutus-starter.cabal +++ b/MetaLamp/nft-marketplace/plutus-starter.cabal @@ -23,7 +23,7 @@ maintainer: Your email library exposed-modules: - Plutus.Contracts.NftMarketplace.OnChain.Core Plutus.Contracts.Services.Sale Plutus.Contracts.Services.Sale.StateMachine Plutus.Contracts.NftMarketplace.OffChain.Info Ext.Plutus.Ledger.Contexts Ext.Plutus.Ledger.Value Plutus.Contracts.NftMarketplace.OffChain.User Plutus.Abstract.ContractResponse Plutus.Abstract.OutputValue Plutus.Abstract.TxUtils Plutus.Contracts.NftMarketplace.Endpoints Plutus.Contracts.NftMarketplace.OffChain.Owner Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine Plutus.PAB.Simulation + Plutus.Contracts.NftMarketplace.OnChain.Core Plutus.Contracts.Services.Sale Plutus.Contracts.Services.Sale.Endpoints Plutus.Contracts.Services.Sale.StateMachine Plutus.Contracts.NftMarketplace.OffChain.Info Ext.Plutus.Ledger.Contexts Ext.Plutus.Ledger.Value Plutus.Contracts.NftMarketplace.OffChain.User Plutus.Abstract.ContractResponse Plutus.Abstract.OutputValue Plutus.Abstract.TxUtils Plutus.Contracts.NftMarketplace.Endpoints Plutus.Contracts.NftMarketplace.OffChain.Owner Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine Plutus.PAB.Simulation build-depends: base >= 4.9 && < 5, aeson, diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale.hs index 6336fdd2a..1d26ba469 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale.hs @@ -1,3 +1,4 @@ module Plutus.Contracts.Services.Sale (module Export) where +import Plutus.Contracts.Services.Sale.Endpoints as Export import Plutus.Contracts.Services.Sale.StateMachine as Export diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/Endpoints.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/Endpoints.hs new file mode 100644 index 000000000..280900350 --- /dev/null +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/Endpoints.hs @@ -0,0 +1,93 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeOperators #-} + +module Plutus.Contracts.Services.Sale.Endpoints where + +import Control.Monad hiding (fmap) +import qualified Data.Aeson as J +import Data.Proxy (Proxy (..)) +import Data.Text (Text) +import qualified Data.Text as T +import qualified GHC.Generics as Haskell +import Ledger +import qualified Ledger.Typed.Scripts as Scripts +import Ledger.Value +import Plutus.Abstract.ContractResponse (ContractResponse, + withContractResponse) +import Plutus.Contract +import Plutus.Contract.StateMachine +import Plutus.Contracts.Currency as Currency +import qualified Plutus.Contracts.Services.Sale.StateMachine as Core +import qualified PlutusTx +import qualified PlutusTx.AssocMap as AssocMap +import PlutusTx.Prelude hiding + (Semigroup (..)) +import Prelude (Semigroup (..)) +import qualified Prelude as Haskell +import qualified Schema +import Text.Printf (printf) + +data OpenSaleParams = + OpenSaleParams { + ospSalePrice :: Core.LovelacePrice, + ospSaleValue :: Value + } + deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) + deriving anyclass (J.ToJSON, J.FromJSON, Schema.ToSchema) + +PlutusTx.unstableMakeIsData ''OpenSaleParams +PlutusTx.makeLift ''OpenSaleParams + +-- | Starts the Sale protocol and mints protocol NFT +openSale :: OpenSaleParams -> Contract w s Text Core.Sale +openSale OpenSaleParams {..} = do + pkh <- getOwnPubKey + saleCurrency <- fmap Currency.currencySymbol $ + mapError (T.pack . Haskell.show @Currency.CurrencyError) $ + Currency.forgeContract pkh [(Core.saleProtocolName, 1)] + let sale = Core.Sale + { saleProtocolToken = assetClass saleCurrency Core.saleProtocolName, + salePrice = ospSalePrice, + saleValue = ospSaleValue + } + let client = Core.saleClient sale + void $ mapError (T.pack . Haskell.show @SMContractError) $ runInitialise client (Core.LotInfo pkh) ospSaleValue + + logInfo @Haskell.String $ printf "Opened Sale %s at address %s" (Haskell.show sale) (Haskell.show $ Core.saleAddress sale) + pure sale + +-- | The user buys sale value paying sale price +buyLot :: Core.Sale -> Contract w s Text () +buyLot sale = do + pkh <- getOwnPubKey + let client = Core.saleClient sale + void $ mapError' $ runStep client $ Core.Buy pkh + + logInfo @Haskell.String $ printf "User %s bought lot from sale %s" (Haskell.show pkh) (Haskell.show sale) + pure () + +-- | The user redeems sale value and sale protocol token +redeemLot :: Core.Sale -> Contract w s Text () +redeemLot sale = do + pkh <- getOwnPubKey + let client = Core.saleClient sale + void $ mapError' $ runStep client Core.Redeem + + logInfo @Haskell.String $ printf "User %s redeemed lot from sale %s" (Haskell.show pkh) (Haskell.show sale) + pure () + +getOwnPubKey :: Contract w s Text PubKeyHash +getOwnPubKey = pubKeyHash <$> ownPubKey + +mapError' :: Contract w s SMContractError a -> Contract w s Text a +mapError' = mapError $ T.pack . Haskell.show diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/StateMachine.hs index 36320e0c1..bd363bd45 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/StateMachine.hs @@ -64,9 +64,9 @@ PlutusTx.makeLift ''SaleDatum {-# INLINABLE transition #-} transition :: Sale -> State SaleDatum -> SaleRedeemer -> Maybe (TxConstraints Void Void, State SaleDatum) transition Sale{..} state redeemer = case (stateData state, redeemer) of - (LotInfo saler, Redeem) | saleValue == val + (LotInfo saler, Redeem) -> Just ( Constraints.mustBeSignedBy saler <> - Constraints.mustPayToPubKey saler (stateToken <> saleValue) + Constraints.mustPayToPubKey saler (stateToken <> val) , State SaleClosed mempty ) (LotInfo saler, Buy buyer) | saleValue == val @@ -112,3 +112,12 @@ saleInst sale = Scripts.mkTypedValidator @SaleScript saleClient :: Sale -> StateMachineClient SaleDatum SaleRedeemer saleClient sale = mkStateMachineClient $ StateMachineInstance (saleStateMachine sale) (saleInst sale) + +saleProtocolName :: TokenName +saleProtocolName = "Sale" + +saleValidator :: Sale -> Validator +saleValidator = Scripts.validatorScript . saleInst + +saleAddress :: Sale -> Ledger.Address +saleAddress = scriptAddress . saleValidator From e9f1dca525e3f0f2d6cd788ae09f7dd8cec1d684 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Wed, 28 Jul 2021 19:26:13 +0700 Subject: [PATCH 018/217] fix sale state machine --- .../src/Plutus/Contracts/Services/Sale/StateMachine.hs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/StateMachine.hs index bd363bd45..6c8a33ae9 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/StateMachine.hs @@ -26,6 +26,7 @@ import qualified PlutusTx import qualified PlutusTx.AssocMap as AssocMap import PlutusTx.Prelude hiding (Semigroup (..)) import Prelude (Semigroup (..)) +import qualified Schema import qualified Prelude as Haskell type Saler = PubKeyHash @@ -39,7 +40,7 @@ data Sale = saleValue :: Value } deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) - deriving anyclass (J.ToJSON, J.FromJSON) + deriving anyclass (J.ToJSON, J.FromJSON, Schema.ToSchema) PlutusTx.makeLift ''Sale @@ -66,10 +67,10 @@ transition :: Sale -> State SaleDatum -> SaleRedeemer -> Maybe (TxConstraints Vo transition Sale{..} state redeemer = case (stateData state, redeemer) of (LotInfo saler, Redeem) -> Just ( Constraints.mustBeSignedBy saler <> - Constraints.mustPayToPubKey saler (stateToken <> val) + Constraints.mustPayToPubKey saler val , State SaleClosed mempty ) - (LotInfo saler, Buy buyer) | saleValue == val + (LotInfo saler, Buy buyer) | saleValue == (val - stateToken) -> Just ( Constraints.mustBeSignedBy buyer <> Constraints.mustPayToPubKey saler (stateToken <> Ada.lovelaceValueOf salePrice) <> Constraints.mustPayToPubKey buyer saleValue From 69ead39ab281aa46c944fae5669a3b50bb9b09d9 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Thu, 29 Jul 2021 15:54:09 +0700 Subject: [PATCH 019/217] add open sale endpoint --- .../Contracts/NftMarketplace/OffChain/User.hs | 59 +++++++++++++++--- .../OnChain/Core/StateMachine.hs | 61 +++++++++++++++---- .../Contracts/Services/Sale/StateMachine.hs | 15 ++++- 3 files changed, 115 insertions(+), 20 deletions(-) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs index 76d7d3849..4c2df950d 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs @@ -24,7 +24,7 @@ import qualified GHC.Generics as Haskell import Ledger import qualified Ledger.Typed.Scripts as Scripts import Ledger.Typed.Tx -import Ledger.Value +import qualified Ledger.Value as V import Plutus.Abstract.ContractResponse (ContractResponse, withContractResponse) import Plutus.Contract @@ -34,6 +34,7 @@ import Plutus.Contracts.NftMarketplace.OffChain.Info (fundsAt, mapError', marketplaceStore) import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Core +import qualified Plutus.Contracts.Services.Sale as Sale import qualified PlutusTx import qualified PlutusTx.AssocMap as AssocMap import PlutusTx.Prelude hiding @@ -43,6 +44,9 @@ import qualified Prelude as Haskell import qualified Schema import Text.Printf (printf) +getOwnPubKey :: Contract w s Text PubKeyHash +getOwnPubKey = pubKeyHash <$> ownPubKey + data CreateNftParams = CreateNftParams { cnpIpfsCid :: ByteString, @@ -65,7 +69,7 @@ createNft marketplace CreateNftParams {..} = do when (isJust $ AssocMap.lookup ipfsCidHash nftStore) $ throwError "Nft entry already exists" pkh <- getOwnPubKey - let tokenName = TokenName cnpIpfsCid + let tokenName = V.TokenName cnpIpfsCid nft <- mapError (T.pack . Haskell.show @Currency.CurrencyError) $ Currency.forgeContract pkh [(tokenName, 1)] @@ -76,29 +80,67 @@ createNft marketplace CreateNftParams {..} = do , nftName = cnpNftName , nftDescription = cnpNftDescription , nftIssuer = if cnpRevealIssuer then Just pkh else Nothing - , nftIpfsCid = Nothing -- TODO validate that it's Nothing (?) + , nftSale = Nothing -- TODO validate that it's Nothing } void $ mapError' $ runStep client $ Core.CreateNftRedeemer ipfsCidHash nftEntry logInfo @Haskell.String $ printf "Created NFT %s with store entry %s" (Haskell.show nft) (Haskell.show nftEntry) pure () -balanceAt :: PubKeyHash -> AssetClass -> Contract w s Text Integer -balanceAt pkh asset = flip assetClassValueOf asset <$> fundsAt pkh +data OpenSaleParams = + OpenSaleParams { + ospIpfsCid :: ByteString, + ospSalePrice :: Sale.LovelacePrice + } + deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) + deriving anyclass (J.ToJSON, J.FromJSON, Schema.ToSchema) -getOwnPubKey :: Contract w s Text PubKeyHash -getOwnPubKey = pubKeyHash <$> ownPubKey +PlutusTx.unstableMakeIsData ''OpenSaleParams +PlutusTx.makeLift ''OpenSaleParams + +-- | The user +openSale :: Core.Marketplace -> OpenSaleParams -> Contract w s Text () +openSale marketplace OpenSaleParams {..} = do + let ipfsCidHash = sha2_256 ospIpfsCid + nftStore <- marketplaceStore marketplace + nftEntry <- maybe (throwError "NFT has not been created") pure $ AssocMap.lookup ipfsCidHash nftStore + let tokenName = V.TokenName ospIpfsCid + + sale <- Sale.openSale + Sale.OpenSaleParams { + ospSalePrice = ospSalePrice, + ospSaleValue = V.singleton (Core.nftId nftEntry) tokenName 1 + } + + let client = Core.marketplaceClient marketplace + let lot = Core.Lot + { lotSale = Sale.toTuple sale + , lotIpfsCid = ospIpfsCid + } + void $ mapError' $ runStep client $ Core.OpenSaleRedeemer ipfsCidHash lot + + logInfo @Haskell.String $ printf "Created NFT sale %s" (Haskell.show lot) + pure () + +balanceAt :: PubKeyHash -> AssetClass -> Contract w s Text Integer +balanceAt pkh asset = flip V.assetClassValueOf asset <$> fundsAt pkh ownPubKeyBalance :: Contract w s Text Value ownPubKeyBalance = getOwnPubKey >>= fundsAt type MarketplaceUserSchema = Endpoint "createNft" CreateNftParams + .\/ Endpoint "openSale" OpenSaleParams + .\/ Endpoint "buyLot" Sale.Sale + .\/ Endpoint "redeemLot" Sale.Sale .\/ Endpoint "ownPubKey" () .\/ Endpoint "ownPubKeyBalance" () data UserContractState = NftCreated + | OpenedSale + | Bought + | Redeemed | GetPubKey PubKeyHash | GetPubKeyBalance Value deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) @@ -109,5 +151,8 @@ Lens.makeClassyPrisms ''UserContractState userEndpoints :: Core.Marketplace -> Contract (ContractResponse Text UserContractState) MarketplaceUserSchema Void () userEndpoints marketplace = forever $ withContractResponse (Proxy @"createNft") (const NftCreated) (createNft marketplace) + `select` withContractResponse (Proxy @"openSale") (const OpenedSale) (openSale marketplace) + `select` withContractResponse (Proxy @"buyLot") (const Bought) Sale.buyLot + `select` withContractResponse (Proxy @"redeemLot") (const Redeemed) Sale.redeemLot `select` withContractResponse (Proxy @"ownPubKey") GetPubKey (const getOwnPubKey) `select` withContractResponse (Proxy @"ownPubKeyBalance") GetPubKeyBalance (const ownPubKeyBalance) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs index c1646985c..4a2365ae1 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs @@ -11,20 +11,23 @@ module Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine where -import qualified Data.Aeson as J -import qualified Data.Text as T -import qualified GHC.Generics as Haskell +import Control.Lens ((&), (?~), (^.)) +import qualified Control.Lens as Lens +import qualified Data.Aeson as J +import qualified Data.Text as T +import qualified GHC.Generics as Haskell import Ledger -import qualified Ledger.Constraints as Constraints -import qualified Ledger.Typed.Scripts as Scripts -import Ledger.Value +import qualified Ledger.Constraints as Constraints +import qualified Ledger.Typed.Scripts as Scripts +import qualified Ledger.Value as V import Plutus.Contract import Plutus.Contract.StateMachine +import qualified Plutus.Contracts.Services.Sale as Sale import qualified PlutusTx -import qualified PlutusTx.AssocMap as AssocMap -import PlutusTx.Prelude hiding (Semigroup (..)) -import Prelude (Semigroup (..)) -import qualified Prelude as Haskell +import qualified PlutusTx.AssocMap as AssocMap +import PlutusTx.Prelude hiding (Semigroup (..)) +import Prelude (Semigroup (..)) +import qualified Prelude as Haskell newtype Marketplace = Marketplace @@ -36,6 +39,20 @@ newtype Marketplace = PlutusTx.makeLift ''Marketplace type IpfsCidHash = ByteString +type Sale = (AssetClass, Sale.LovelacePrice, Value) + +data Lot = Lot + { lotSale :: Sale + , lotIpfsCid :: ByteString + } + deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) + deriving anyclass (J.ToJSON, J.FromJSON) + +PlutusTx.unstableMakeIsData ''Lot + +PlutusTx.makeLift ''Lot + +Lens.makeClassy_ ''Lot data NFT = NFT @@ -43,7 +60,7 @@ data NFT = , nftName :: ByteString , nftDescription :: ByteString , nftIssuer :: Maybe PubKeyHash - , nftIpfsCid :: Maybe ByteString + , nftSale :: Maybe Lot } deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON) @@ -52,8 +69,11 @@ PlutusTx.unstableMakeIsData ''NFT PlutusTx.makeLift ''NFT +Lens.makeClassy_ ''NFT + data MarketplaceRedeemer = CreateNftRedeemer IpfsCidHash NFT + | OpenSaleRedeemer IpfsCidHash Lot deriving (Haskell.Show) PlutusTx.unstableMakeIsData ''MarketplaceRedeemer @@ -78,6 +98,12 @@ transition marketplace state redeemer = case redeemer of -> Just ( mustBeSignedByIssuer nftEntry , State (MarketplaceDatum $ AssocMap.insert ipfsCidHash nftEntry nftStore) mempty ) + OpenSaleRedeemer ipfsCidHash lot + -> let newEntry = maybe (traceError "NFT has not been created.") (_nftSale ?~ lot) $ + AssocMap.lookup ipfsCidHash nftStore + in Just ( mempty + , State (MarketplaceDatum $ AssocMap.insert ipfsCidHash newEntry nftStore) mempty + ) _ -> Nothing where nftStore :: AssocMap.Map IpfsCidHash NFT @@ -90,8 +116,19 @@ transition marketplace state redeemer = case redeemer of {-# INLINABLE stateTransitionCheck #-} stateTransitionCheck :: MarketplaceDatum -> MarketplaceRedeemer -> ScriptContext -> Bool stateTransitionCheck (MarketplaceDatum nftStore) (CreateNftRedeemer ipfsCidHash nftEntry) ctx = - traceIfFalse "Nft entry already exists" $ + traceIfFalse "CreateNftRedeemer: " $ + traceIfFalse "NFT entry already exists" $ isNothing $ AssocMap.lookup ipfsCidHash nftStore +stateTransitionCheck (MarketplaceDatum nftStore) (OpenSaleRedeemer ipfsCidHash lot) ctx = + traceIfFalse "OpenSaleRedeemer: " $ + let nftEntry = fromMaybe (traceError "NFT has not been created") $ AssocMap.lookup ipfsCidHash nftStore + nftIpfsCid = lotIpfsCid lot + sale = Sale.fromTuple $ lotSale lot + nftValue = V.singleton (nftId nftEntry) (V.TokenName nftIpfsCid) 1 + hasBeenPutOnSale = Sale.saleValue sale == nftValue + isValidHash = sha2_256 nftIpfsCid == ipfsCidHash + in traceIfFalse "NFT has not been put on sale" hasBeenPutOnSale && + traceIfFalse "Invalid IPFS Cid Hash" isValidHash {-# INLINABLE marketplaceStateMachine #-} marketplaceStateMachine :: Marketplace -> StateMachine MarketplaceDatum MarketplaceRedeemer diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/StateMachine.hs index 6c8a33ae9..4550440eb 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/StateMachine.hs @@ -12,6 +12,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 qualified GHC.Generics as Haskell @@ -26,8 +27,8 @@ import qualified PlutusTx import qualified PlutusTx.AssocMap as AssocMap import PlutusTx.Prelude hiding (Semigroup (..)) import Prelude (Semigroup (..)) -import qualified Schema import qualified Prelude as Haskell +import qualified Schema type Saler = PubKeyHash type Buyer = PubKeyHash @@ -42,8 +43,20 @@ data Sale = deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON, Schema.ToSchema) +PlutusTx.unstableMakeIsData ''Sale + PlutusTx.makeLift ''Sale +Lens.makeClassy_ ''Sale + +{-# INLINABLE toTuple #-} +toTuple :: Sale -> (AssetClass, LovelacePrice, Value) +toTuple Sale{..} = (saleProtocolToken, salePrice, saleValue) + +{-# INLINABLE fromTuple #-} +fromTuple :: (AssetClass, LovelacePrice, Value) -> Sale +fromTuple (saleProtocolToken, salePrice, saleValue) = Sale{..} + data SaleRedeemer = Buy Buyer | Redeem From 97d212bb0e3e20adcf0b836a667ddd053570a5c5 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Thu, 29 Jul 2021 16:37:11 +0700 Subject: [PATCH 020/217] add buy and close sale endpoints --- .../Contracts/NftMarketplace/OffChain/User.hs | 67 +++++++++++++++++-- .../OnChain/Core/StateMachine.hs | 32 ++++++++- 2 files changed, 90 insertions(+), 9 deletions(-) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs index 4c2df950d..143610f64 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs @@ -13,6 +13,7 @@ module Plutus.Contracts.NftMarketplace.OffChain.User where +import Control.Lens ((^.)) import qualified Control.Lens as Lens import Control.Monad hiding (fmap) import qualified Data.Aeson as J @@ -122,6 +123,60 @@ openSale marketplace OpenSaleParams {..} = do logInfo @Haskell.String $ printf "Created NFT sale %s" (Haskell.show lot) pure () +data BuyNftParams = + BuyNftParams { + bnpIpfsCid :: ByteString + } + deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) + deriving anyclass (J.ToJSON, J.FromJSON, Schema.ToSchema) + +PlutusTx.unstableMakeIsData ''BuyNftParams +PlutusTx.makeLift ''BuyNftParams + +-- | The user +buyNft :: Core.Marketplace -> BuyNftParams -> Contract w s Text () +buyNft marketplace BuyNftParams {..} = do + let ipfsCidHash = sha2_256 bnpIpfsCid + nftStore <- marketplaceStore marketplace + nftEntry <- maybe (throwError "NFT has not been created") pure $ AssocMap.lookup ipfsCidHash nftStore + nftLot <- maybe (throwError "NFT has not been put on sale") pure $ nftEntry ^. Core._nftSale + let tokenName = V.TokenName bnpIpfsCid + + _ <- Sale.buyLot $ Sale.fromTuple $ nftLot ^. Core._lotSale + + let client = Core.marketplaceClient marketplace + void $ mapError' $ runStep client $ Core.BuyNftRedeemer ipfsCidHash + + logInfo @Haskell.String $ printf "Bought NFT lot %s" (Haskell.show nftLot) + pure () + +data CloseSaleParams = + CloseSaleParams { + cspIpfsCid :: ByteString + } + deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) + deriving anyclass (J.ToJSON, J.FromJSON, Schema.ToSchema) + +PlutusTx.unstableMakeIsData ''CloseSaleParams +PlutusTx.makeLift ''CloseSaleParams + +-- | The user +closeSale :: Core.Marketplace -> CloseSaleParams -> Contract w s Text () +closeSale marketplace CloseSaleParams {..} = do + let ipfsCidHash = sha2_256 cspIpfsCid + nftStore <- marketplaceStore marketplace + nftEntry <- maybe (throwError "NFT has not been created") pure $ AssocMap.lookup ipfsCidHash nftStore + nftLot <- maybe (throwError "NFT has not been put on sale") pure $ nftEntry ^. Core._nftSale + let tokenName = V.TokenName cspIpfsCid + + _ <- Sale.redeemLot $ Sale.fromTuple $ nftLot ^. Core._lotSale + + let client = Core.marketplaceClient marketplace + void $ mapError' $ runStep client $ Core.CloseSaleRedeemer ipfsCidHash + + logInfo @Haskell.String $ printf "Closed NFT lot sale %s" (Haskell.show nftLot) + pure () + balanceAt :: PubKeyHash -> AssetClass -> Contract w s Text Integer balanceAt pkh asset = flip V.assetClassValueOf asset <$> fundsAt pkh @@ -131,16 +186,16 @@ ownPubKeyBalance = getOwnPubKey >>= fundsAt type MarketplaceUserSchema = Endpoint "createNft" CreateNftParams .\/ Endpoint "openSale" OpenSaleParams - .\/ Endpoint "buyLot" Sale.Sale - .\/ Endpoint "redeemLot" Sale.Sale + .\/ Endpoint "buyNft" BuyNftParams + .\/ Endpoint "closeSale" CloseSaleParams .\/ Endpoint "ownPubKey" () .\/ Endpoint "ownPubKeyBalance" () data UserContractState = NftCreated | OpenedSale - | Bought - | Redeemed + | NftBought + | ClosedSale | GetPubKey PubKeyHash | GetPubKeyBalance Value deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) @@ -152,7 +207,7 @@ userEndpoints :: Core.Marketplace -> Contract (ContractResponse Text UserContrac userEndpoints marketplace = forever $ withContractResponse (Proxy @"createNft") (const NftCreated) (createNft marketplace) `select` withContractResponse (Proxy @"openSale") (const OpenedSale) (openSale marketplace) - `select` withContractResponse (Proxy @"buyLot") (const Bought) Sale.buyLot - `select` withContractResponse (Proxy @"redeemLot") (const Redeemed) Sale.redeemLot + `select` withContractResponse (Proxy @"buyNft") (const NftBought) (buyNft marketplace) + `select` withContractResponse (Proxy @"closeSale") (const ClosedSale) (closeSale marketplace) `select` withContractResponse (Proxy @"ownPubKey") GetPubKey (const getOwnPubKey) `select` withContractResponse (Proxy @"ownPubKeyBalance") GetPubKeyBalance (const ownPubKeyBalance) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs index 4a2365ae1..3aa141137 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs @@ -11,7 +11,7 @@ module Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine where -import Control.Lens ((&), (?~), (^.)) +import Control.Lens ((&), (.~), (?~), (^.)) import qualified Control.Lens as Lens import qualified Data.Aeson as J import qualified Data.Text as T @@ -74,6 +74,8 @@ Lens.makeClassy_ ''NFT data MarketplaceRedeemer = CreateNftRedeemer IpfsCidHash NFT | OpenSaleRedeemer IpfsCidHash Lot + | BuyNftRedeemer IpfsCidHash + | CloseSaleRedeemer IpfsCidHash deriving (Haskell.Show) PlutusTx.unstableMakeIsData ''MarketplaceRedeemer @@ -96,19 +98,33 @@ transition marketplace state redeemer = case redeemer of CreateNftRedeemer ipfsCidHash nftEntry -- TODO check that ipfsCidHash is a hash (?) -> Just ( mustBeSignedByIssuer nftEntry - , State (MarketplaceDatum $ AssocMap.insert ipfsCidHash nftEntry nftStore) mempty + , State (MarketplaceDatum $ AssocMap.insert ipfsCidHash nftEntry nftStore) val ) OpenSaleRedeemer ipfsCidHash lot -> let newEntry = maybe (traceError "NFT has not been created.") (_nftSale ?~ lot) $ AssocMap.lookup ipfsCidHash nftStore in Just ( mempty - , State (MarketplaceDatum $ AssocMap.insert ipfsCidHash newEntry nftStore) mempty + , State (MarketplaceDatum $ AssocMap.insert ipfsCidHash newEntry nftStore) val + ) + BuyNftRedeemer ipfsCidHash + -> let newEntry = maybe (traceError "NFT has not been created.") (_nftSale .~ Nothing) $ + AssocMap.lookup ipfsCidHash nftStore + in Just ( mempty + , State (MarketplaceDatum $ AssocMap.insert ipfsCidHash newEntry nftStore) val + ) + CloseSaleRedeemer ipfsCidHash + -> let newEntry = maybe (traceError "NFT has not been created.") (_nftSale .~ Nothing) $ + AssocMap.lookup ipfsCidHash nftStore + in Just ( mempty + , State (MarketplaceDatum $ AssocMap.insert ipfsCidHash newEntry nftStore) val ) _ -> Nothing where nftStore :: AssocMap.Map IpfsCidHash NFT nftStore = getMarketplaceDatum $ stateData state + val = stateValue state + mustBeSignedByIssuer entry = case nftIssuer entry of Just pkh -> Constraints.mustBeSignedBy pkh Nothing -> mempty @@ -129,6 +145,16 @@ stateTransitionCheck (MarketplaceDatum nftStore) (OpenSaleRedeemer ipfsCidHash l isValidHash = sha2_256 nftIpfsCid == ipfsCidHash in traceIfFalse "NFT has not been put on sale" hasBeenPutOnSale && traceIfFalse "Invalid IPFS Cid Hash" isValidHash +stateTransitionCheck (MarketplaceDatum nftStore) (BuyNftRedeemer ipfsCidHash) ctx = + traceIfFalse "BuyNftRedeemer: " $ + let nftEntry = fromMaybe (traceError "NFT has not been created") $ AssocMap.lookup ipfsCidHash nftStore + hasBeenPutOnSale = isJust $ nftSale nftEntry + in traceIfFalse "NFT has not been put on sale" hasBeenPutOnSale +stateTransitionCheck (MarketplaceDatum nftStore) (CloseSaleRedeemer ipfsCidHash) ctx = + traceIfFalse "CloseSaleRedeemer: " $ + let nftEntry = fromMaybe (traceError "NFT has not been created") $ AssocMap.lookup ipfsCidHash nftStore + hasBeenPutOnSale = isJust $ nftSale nftEntry + in traceIfFalse "NFT has not been put on sale" hasBeenPutOnSale {-# INLINABLE marketplaceStateMachine #-} marketplaceStateMachine :: Marketplace -> StateMachine MarketplaceDatum MarketplaceRedeemer From a314c9a75851cced3524586e30e995c7e688c6b2 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Thu, 29 Jul 2021 17:30:08 +0700 Subject: [PATCH 021/217] add simulation for buying nft --- .../OnChain/Core/StateMachine.hs | 13 ++++--- .../src/Plutus/PAB/Simulation.hs | 37 +++++++++++++++++-- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs index 3aa141137..e6454ec45 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs @@ -98,32 +98,35 @@ transition marketplace state redeemer = case redeemer of CreateNftRedeemer ipfsCidHash nftEntry -- TODO check that ipfsCidHash is a hash (?) -> Just ( mustBeSignedByIssuer nftEntry - , State (MarketplaceDatum $ AssocMap.insert ipfsCidHash nftEntry nftStore) val + , State (MarketplaceDatum $ AssocMap.insert ipfsCidHash nftEntry nftStore) currStateValue ) OpenSaleRedeemer ipfsCidHash lot -> let newEntry = maybe (traceError "NFT has not been created.") (_nftSale ?~ lot) $ AssocMap.lookup ipfsCidHash nftStore in Just ( mempty - , State (MarketplaceDatum $ AssocMap.insert ipfsCidHash newEntry nftStore) val + , State (MarketplaceDatum $ AssocMap.insert ipfsCidHash newEntry nftStore) currStateValue ) BuyNftRedeemer ipfsCidHash -> let newEntry = maybe (traceError "NFT has not been created.") (_nftSale .~ Nothing) $ AssocMap.lookup ipfsCidHash nftStore in Just ( mempty - , State (MarketplaceDatum $ AssocMap.insert ipfsCidHash newEntry nftStore) val + , State (MarketplaceDatum $ AssocMap.insert ipfsCidHash newEntry nftStore) currStateValue ) CloseSaleRedeemer ipfsCidHash -> let newEntry = maybe (traceError "NFT has not been created.") (_nftSale .~ Nothing) $ AssocMap.lookup ipfsCidHash nftStore in Just ( mempty - , State (MarketplaceDatum $ AssocMap.insert ipfsCidHash newEntry nftStore) val + , State (MarketplaceDatum $ AssocMap.insert ipfsCidHash newEntry nftStore) currStateValue ) _ -> Nothing where + stateToken :: Value + stateToken = V.assetClassValue (marketplaceProtocolToken marketplace) 1 + nftStore :: AssocMap.Map IpfsCidHash NFT nftStore = getMarketplaceDatum $ stateData state - val = stateValue state + currStateValue = stateValue state - stateToken mustBeSignedByIssuer entry = case nftIssuer entry of Just pkh -> Constraints.mustBeSignedBy pkh diff --git a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs index 8efd65663..08c972bec 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs @@ -47,6 +47,7 @@ import Plutus.Contract hiding (when) import Plutus.Contracts.Currency as Currency import qualified Plutus.Contracts.NftMarketplace.Endpoints as Marketplace import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace +import qualified Plutus.Contracts.Services.Sale as Sale import Plutus.PAB.Effects.Contract (ContractEffect (..)) import Plutus.PAB.Effects.Contract.Builtin (Builtin, SomeBuiltin (..), @@ -98,11 +99,12 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do ContractIDs {..} <- activateContracts let userCid = cidUser Map.! Wallet 2 sender = pubKeyHash . walletPubKey $ Wallet 2 + let catTokenIpfsCid = "QmPeoJnaDttpFrSySYBY3reRFCzL3qv4Uiqz376EBv9W16" _ <- Simulator.callEndpointOnInstance userCid "createNft" $ Marketplace.CreateNftParams { - cnpIpfsCid = "QmPeoJnaDttpFrSySYBY3reRFCzL3qv4Uiqz376EBv9W16", + cnpIpfsCid = catTokenIpfsCid, cnpNftName = "Cat token", cnpNftDescription = "A picture of a cat on a pogo stick", cnpRevealIssuer = False @@ -112,11 +114,34 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do _ -> Nothing Simulator.logString @(Builtin MarketplaceContracts) $ "Successful createNft" - _ <- Simulator.callEndpointOnInstance cidInfo "fundsAt" sender + _ <- + Simulator.callEndpointOnInstance userCid "openSale" $ + Marketplace.OpenSaleParams { + ospIpfsCid = catTokenIpfsCid, + ospSalePrice = 44*oneAdaInLovelace + } + sale <- flip Simulator.waitForState userCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of + Success (ContractSuccess Marketplace.OpenedSale) -> Just () + _ -> Nothing + Simulator.logString @(Builtin MarketplaceContracts) $ "Successful openSale" + + let buyerCid = cidUser Map.! Wallet 3 + buyer = pubKeyHash . walletPubKey $ Wallet 3 + + _ <- + Simulator.callEndpointOnInstance buyerCid "buyNft" Marketplace.BuyNftParams { + bnpIpfsCid = catTokenIpfsCid + } + _ <- flip Simulator.waitForState buyerCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of + Success (ContractSuccess Marketplace.NftBought) -> Just () + _ -> Nothing + Simulator.logString @(Builtin MarketplaceContracts) $ "Successful buyNft" + + _ <- Simulator.callEndpointOnInstance cidInfo "fundsAt" buyer v <- flip Simulator.waitForState cidInfo $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.InfoContractState)) of Success (ContractSuccess (Marketplace.FundsAt v)) -> Just v _ -> Nothing - Simulator.logString @(Builtin MarketplaceContracts) $ "Final user funds: " <> show v + Simulator.logString @(Builtin MarketplaceContracts) $ "Final buyer funds: " <> show v _ <- Simulator.callEndpointOnInstance cidInfo "marketplaceStore" () marketplaceStore <- flip Simulator.waitForState cidInfo $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.InfoContractState)) of @@ -130,6 +155,12 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do _ -> Nothing Simulator.logString @(Builtin MarketplaceContracts) $ "Final marketplace funds: " <> show v + _ <- Simulator.callEndpointOnInstance cidInfo "fundsAt" sender + v <- flip Simulator.waitForState cidInfo $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.InfoContractState)) of + Success (ContractSuccess (Marketplace.FundsAt v)) -> Just v + _ -> Nothing + Simulator.logString @(Builtin MarketplaceContracts) $ "Final user funds: " <> show v + _ <- liftIO getLine shutdown From 970f0d2f63adb04baf1c8004348f98f0bc7129c8 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Fri, 30 Jul 2021 09:12:56 +0700 Subject: [PATCH 022/217] add close sale simulation --- .../Contracts/NftMarketplace/OffChain/User.hs | 8 ++--- .../src/Plutus/PAB/Simulation.hs | 35 +++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs index 143610f64..8e0e65c55 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs @@ -99,7 +99,7 @@ data OpenSaleParams = PlutusTx.unstableMakeIsData ''OpenSaleParams PlutusTx.makeLift ''OpenSaleParams --- | The user +-- | The user opens sale for his NFT openSale :: Core.Marketplace -> OpenSaleParams -> Contract w s Text () openSale marketplace OpenSaleParams {..} = do let ipfsCidHash = sha2_256 ospIpfsCid @@ -133,14 +133,13 @@ data BuyNftParams = PlutusTx.unstableMakeIsData ''BuyNftParams PlutusTx.makeLift ''BuyNftParams --- | The user +-- | The user buys specified NFT lot buyNft :: Core.Marketplace -> BuyNftParams -> Contract w s Text () buyNft marketplace BuyNftParams {..} = do let ipfsCidHash = sha2_256 bnpIpfsCid nftStore <- marketplaceStore marketplace nftEntry <- maybe (throwError "NFT has not been created") pure $ AssocMap.lookup ipfsCidHash nftStore nftLot <- maybe (throwError "NFT has not been put on sale") pure $ nftEntry ^. Core._nftSale - let tokenName = V.TokenName bnpIpfsCid _ <- Sale.buyLot $ Sale.fromTuple $ nftLot ^. Core._lotSale @@ -160,14 +159,13 @@ data CloseSaleParams = PlutusTx.unstableMakeIsData ''CloseSaleParams PlutusTx.makeLift ''CloseSaleParams --- | The user +-- | The user closes NFT sale and receives his token back closeSale :: Core.Marketplace -> CloseSaleParams -> Contract w s Text () closeSale marketplace CloseSaleParams {..} = do let ipfsCidHash = sha2_256 cspIpfsCid nftStore <- marketplaceStore marketplace nftEntry <- maybe (throwError "NFT has not been created") pure $ AssocMap.lookup ipfsCidHash nftStore nftLot <- maybe (throwError "NFT has not been put on sale") pure $ nftEntry ^. Core._nftSale - let tokenName = V.TokenName cspIpfsCid _ <- Sale.redeemLot $ Sale.fromTuple $ nftLot ^. Core._lotSale diff --git a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs index 08c972bec..8a6070864 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs @@ -100,6 +100,7 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do let userCid = cidUser Map.! Wallet 2 sender = pubKeyHash . walletPubKey $ Wallet 2 let catTokenIpfsCid = "QmPeoJnaDttpFrSySYBY3reRFCzL3qv4Uiqz376EBv9W16" + let photoTokenIpfsCid = "QmeSFBsEZ7XtK7yv5CQ79tqFnH9V2jhFhSSq1LV5W3kuiB" _ <- Simulator.callEndpointOnInstance userCid "createNft" $ @@ -137,6 +138,40 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do _ -> Nothing Simulator.logString @(Builtin MarketplaceContracts) $ "Successful buyNft" + _ <- + Simulator.callEndpointOnInstance userCid "createNft" $ + Marketplace.CreateNftParams { + cnpIpfsCid = photoTokenIpfsCid, + cnpNftName = "Photo token", + cnpNftDescription = "A picture of a sunset", + cnpRevealIssuer = True + } + flip Simulator.waitForState userCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of + Success (ContractSuccess Marketplace.NftCreated) -> Just () + _ -> Nothing + Simulator.logString @(Builtin MarketplaceContracts) $ "Successful createNft" + + _ <- + Simulator.callEndpointOnInstance userCid "openSale" $ + Marketplace.OpenSaleParams { + ospIpfsCid = photoTokenIpfsCid, + ospSalePrice = 12*oneAdaInLovelace + } + sale <- flip Simulator.waitForState userCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of + Success (ContractSuccess Marketplace.OpenedSale) -> Just () + _ -> Nothing + Simulator.logString @(Builtin MarketplaceContracts) $ "Successful openSale" + + _ <- + Simulator.callEndpointOnInstance userCid "closeSale" + Marketplace.CloseSaleParams { + cspIpfsCid = photoTokenIpfsCid + } + sale <- flip Simulator.waitForState userCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of + Success (ContractSuccess Marketplace.ClosedSale) -> Just () + _ -> Nothing + Simulator.logString @(Builtin MarketplaceContracts) $ "Successful closeSale" + _ <- Simulator.callEndpointOnInstance cidInfo "fundsAt" buyer v <- flip Simulator.waitForState cidInfo $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.InfoContractState)) of Success (ContractSuccess (Marketplace.FundsAt v)) -> Just v From a47b08f1390c37acb5c3218bc7ea73192c3ccd1b Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Sat, 31 Jul 2021 12:50:32 +0700 Subject: [PATCH 023/217] cp auction contract --- MetaLamp/nft-marketplace/plutus-starter.cabal | 3 +- .../src/Ext/Plutus/Contracts/Auction.hs | 332 ++++++++++++++++++ 2 files changed, 334 insertions(+), 1 deletion(-) create mode 100644 MetaLamp/nft-marketplace/src/Ext/Plutus/Contracts/Auction.hs diff --git a/MetaLamp/nft-marketplace/plutus-starter.cabal b/MetaLamp/nft-marketplace/plutus-starter.cabal index c7d10eda5..b42a81e8d 100644 --- a/MetaLamp/nft-marketplace/plutus-starter.cabal +++ b/MetaLamp/nft-marketplace/plutus-starter.cabal @@ -23,7 +23,7 @@ maintainer: Your email library exposed-modules: - Plutus.Contracts.NftMarketplace.OnChain.Core Plutus.Contracts.Services.Sale Plutus.Contracts.Services.Sale.Endpoints Plutus.Contracts.Services.Sale.StateMachine Plutus.Contracts.NftMarketplace.OffChain.Info Ext.Plutus.Ledger.Contexts Ext.Plutus.Ledger.Value Plutus.Contracts.NftMarketplace.OffChain.User Plutus.Abstract.ContractResponse Plutus.Abstract.OutputValue Plutus.Abstract.TxUtils Plutus.Contracts.NftMarketplace.Endpoints Plutus.Contracts.NftMarketplace.OffChain.Owner Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine Plutus.PAB.Simulation + Plutus.Contracts.NftMarketplace.OnChain.Core Ext.Plutus.Contracts.Auction Plutus.Contracts.Services.Sale Plutus.Contracts.Services.Sale.Endpoints Plutus.Contracts.Services.Sale.StateMachine Plutus.Contracts.NftMarketplace.OffChain.Info Ext.Plutus.Ledger.Contexts Ext.Plutus.Ledger.Value Plutus.Contracts.NftMarketplace.OffChain.User Plutus.Abstract.ContractResponse Plutus.Abstract.OutputValue Plutus.Abstract.TxUtils Plutus.Contracts.NftMarketplace.Endpoints Plutus.Contracts.NftMarketplace.OffChain.Owner Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine Plutus.PAB.Simulation build-depends: base >= 4.9 && < 5, aeson, @@ -34,6 +34,7 @@ library freer-extras, prettyprinter, lens, + semigroups, -- Plutus: playground-common, plutus-contract, diff --git a/MetaLamp/nft-marketplace/src/Ext/Plutus/Contracts/Auction.hs b/MetaLamp/nft-marketplace/src/Ext/Plutus/Contracts/Auction.hs new file mode 100644 index 000000000..eff0f82f4 --- /dev/null +++ b/MetaLamp/nft-marketplace/src/Ext/Plutus/Contracts/Auction.hs @@ -0,0 +1,332 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE DerivingVia #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE StandaloneDeriving #-} + +module Ext.Plutus.Contracts.Auction where + +import Control.Lens (makeClassyPrisms) +import Data.Aeson (FromJSON, ToJSON) +import Data.Monoid (Last (..)) +import Data.Semigroup.Generic (GenericSemigroupMonoid (..)) +import GHC.Generics (Generic) +import Ledger (Ada, PubKeyHash, Slot, Value) +import qualified Ledger +import qualified Ledger.Ada as Ada +import qualified Ledger.Constraints as Constraints +import Ledger.Constraints.TxConstraints (TxConstraints) +import qualified Ledger.Interval as Interval +import qualified Ledger.Typed.Scripts as Scripts +import Ledger.Typed.Tx (TypedScriptTxOut (..)) +import Ledger.Value (AssetClass) +import Plutus.Contract +import Plutus.Contract.StateMachine (State (..), StateMachine (..), StateMachineClient, Void, + WaitingResult (..)) +import qualified Plutus.Contract.StateMachine as SM +import Plutus.Contract.Util (loopM) +import qualified Plutus.Contracts.Currency as Currency +import qualified PlutusTx +import PlutusTx.Prelude +import qualified Prelude as Haskell + +-- | Definition of an auction +data AuctionParams + = AuctionParams + { apOwner :: PubKeyHash -- ^ Current owner of the asset. This is where the proceeds of the auction will be sent. + , apAsset :: Value -- ^ The asset itself. This value is going to be locked by the auction script output. + , apEndTime :: Slot -- ^ When the time window for bidding ends. + } + deriving stock (Haskell.Eq, Haskell.Show, Generic) + deriving anyclass (ToJSON, FromJSON) + +PlutusTx.makeLift ''AuctionParams + + +data HighestBid = + HighestBid + { highestBid :: Ada + , highestBidder :: PubKeyHash + } + deriving stock (Haskell.Eq, Haskell.Show, Generic) + deriving anyclass (ToJSON, FromJSON) + +PlutusTx.unstableMakeIsData ''HighestBid + +-- | The states of the auction +data AuctionState + = Ongoing HighestBid -- Bids can be submitted. + | Finished HighestBid -- The auction is finished + deriving stock (Generic, Haskell.Show, Haskell.Eq) + deriving anyclass (ToJSON, FromJSON) + +-- | Observable state of the auction app +data AuctionOutput = + AuctionOutput + { auctionState :: Last AuctionState + , auctionThreadToken :: Last AssetClass + } + deriving stock (Generic, Haskell.Show, Haskell.Eq) + deriving anyclass (ToJSON, FromJSON) + +deriving via (GenericSemigroupMonoid AuctionOutput) instance (Haskell.Semigroup AuctionOutput) +deriving via (GenericSemigroupMonoid AuctionOutput) instance (Haskell.Monoid AuctionOutput) + +auctionStateOut :: AuctionState -> AuctionOutput +auctionStateOut s = Haskell.mempty { auctionState = Last (Just s) } + +threadTokenOut :: AssetClass -> AuctionOutput +threadTokenOut t = Haskell.mempty { auctionThreadToken = Last (Just t) } + +-- | Initial 'AuctionState'. In the beginning the highest bid is 0 and the +-- highest bidder is seller of the asset. So if nobody submits +-- any bids, the seller gets the asset back after the auction has ended. +initialState :: PubKeyHash -> AuctionState +initialState self = Ongoing HighestBid{highestBid = 0, highestBidder = self} + +PlutusTx.unstableMakeIsData ''AuctionState + +-- | Transition between auction states +data AuctionInput + = Bid { newBid :: Ada, newBidder :: PubKeyHash } -- Increase the price + | Payout + deriving stock (Generic, Haskell.Show) + deriving anyclass (ToJSON, FromJSON) + +PlutusTx.unstableMakeIsData ''AuctionInput + +{-# INLINABLE auctionTransition #-} +-- | The transitions of the auction state machine. +auctionTransition :: AuctionParams -> State AuctionState -> AuctionInput -> Maybe (TxConstraints Void Void, State AuctionState) + +auctionTransition AuctionParams{apOwner, apAsset, apEndTime} State{stateData=oldState} input = + case (oldState, input) of + + (Ongoing HighestBid{highestBid, highestBidder}, Bid{newBid, newBidder}) | newBid > highestBid -> -- if the new bid is higher, + let constraints = + Constraints.mustPayToPubKey highestBidder (Ada.toValue highestBid) -- we pay back the previous highest bid + <> Constraints.mustValidateIn (Interval.to apEndTime) -- but only if we haven't gone past 'apEndTime' + newState = + State + { stateData = Ongoing HighestBid{highestBid = newBid, highestBidder = newBidder} + , stateValue = apAsset <> Ada.toValue newBid -- and lock the new bid in the script output + } + in Just (constraints, newState) + + (Ongoing h@HighestBid{highestBidder, highestBid}, Payout) -> + let constraints = + Constraints.mustValidateIn (Interval.from (apEndTime + 1)) -- When the auction has ended, + <> Constraints.mustPayToPubKey apOwner (Ada.toValue highestBid) -- the owner receives the payment + <> Constraints.mustPayToPubKey highestBidder apAsset -- and the highest bidder the asset + newState = State { stateData = Finished h, stateValue = mempty } + in Just (constraints, newState) + + -- Any other combination of 'AuctionState' and 'AuctionInput' is disallowed. + -- This rules out new bids that don't go over the current highest bid. + _ -> Nothing + + +{-# INLINABLE auctionStateMachine #-} +auctionStateMachine :: AssetClass -> AuctionParams -> StateMachine AuctionState AuctionInput +auctionStateMachine threadToken auctionParams = SM.mkStateMachine (Just threadToken) (auctionTransition auctionParams) isFinal where + isFinal Finished{} = True + isFinal _ = False + + +-- | The script instance of the auction state machine. It contains the state +-- machine compiled to a Plutus core validator script. +typedValidator :: AssetClass -> AuctionParams -> Scripts.TypedValidator (StateMachine AuctionState AuctionInput) +typedValidator currency auctionParams = + let val = $$(PlutusTx.compile [|| validatorParam ||]) + `PlutusTx.applyCode` + PlutusTx.liftCode currency + `PlutusTx.applyCode` + PlutusTx.liftCode auctionParams + validatorParam c f = SM.mkValidator (auctionStateMachine c f) + wrap = Scripts.wrapValidator @AuctionState @AuctionInput + + in Scripts.mkTypedValidator @(StateMachine AuctionState AuctionInput) + val + $$(PlutusTx.compile [|| wrap ||]) + +-- | The machine client of the auction state machine. It contains the script instance +-- with the on-chain code, and the Haskell definition of the state machine for +-- off-chain use. +machineClient + :: Scripts.TypedValidator (StateMachine AuctionState AuctionInput) + -> AssetClass -- ^ Thread token of the instance + -> AuctionParams + -> StateMachineClient AuctionState AuctionInput +machineClient inst threadToken auctionParams = + let machine = auctionStateMachine threadToken auctionParams + in SM.mkStateMachineClient (SM.StateMachineInstance machine inst) + +type BuyerSchema = Endpoint "bid" Ada +type SellerSchema = EmptySchema -- Don't need any endpoints: the contract runs automatically until the auction is finished. + +data AuctionLog = + AuctionStarted AuctionParams + | AuctionFailed SM.SMContractError + | BidSubmitted HighestBid + | AuctionEnded HighestBid + | CurrentStateNotFound + | TransitionFailed (SM.InvalidTransition AuctionState AuctionInput) + deriving stock (Haskell.Show, Generic) + deriving anyclass (ToJSON, FromJSON) + +data AuctionError = + StateMachineContractError SM.SMContractError -- ^ State machine operation failed + | ThreadTokenError Currency.CurrencyError -- ^ Thread token could not be created + | AuctionContractError ContractError -- ^ Endpoint, coin selection, etc. failed + deriving stock (Haskell.Eq, Haskell.Show, Generic) + deriving anyclass (ToJSON, FromJSON) + +makeClassyPrisms ''AuctionError + +instance AsContractError AuctionError where + _ContractError = _AuctionContractError . _ContractError + +instance SM.AsSMContractError AuctionError where + _SMContractError = _StateMachineContractError . SM._SMContractError + + +-- | Client code for the seller +startAuction :: Value -> Slot -> Contract AuctionOutput SellerSchema AuctionError (AssetClass, AuctionParams) +startAuction value slot = do + threadToken <- mapError ThreadTokenError Currency.createThreadToken + logInfo $ "Obtained thread token: " <> Haskell.show threadToken + tell $ threadTokenOut threadToken + self <- Ledger.pubKeyHash <$> ownPubKey + let params = AuctionParams{apOwner = self, apAsset = value, apEndTime = slot } + inst = typedValidator threadToken params + client = machineClient inst threadToken params + + _ <- handleError + (\e -> do { logError (AuctionFailed e); throwError (StateMachineContractError e) }) + (SM.runInitialise client (initialState self) value) + + logInfo $ AuctionStarted params + pure (threadToken, params) + +-- | Client code for the seller +payoutAuction :: AssetClass -> AuctionParams -> Contract AuctionOutput SellerSchema AuctionError () +payoutAuction threadToken params = do + let inst = typedValidator threadToken params + client = machineClient inst threadToken params + + _ <- awaitSlot $ apEndTime params + + r <- SM.runStep client Payout + case r of + SM.TransitionFailure i -> logError (TransitionFailed i) + SM.TransitionSuccess (Finished h) -> logInfo $ AuctionEnded h + SM.TransitionSuccess s -> logWarn ("Unexpected state after Payout transition: " <> Haskell.show s) + +-- | Get the current state of the contract and log it. +currentState :: StateMachineClient AuctionState AuctionInput -> Contract AuctionOutput BuyerSchema AuctionError (Maybe HighestBid) +currentState client = mapError StateMachineContractError (SM.getOnChainState client) >>= \case + Just ((TypedScriptTxOut{tyTxOutData=Ongoing s}, _), _) -> do + tell $ auctionStateOut $ Ongoing s + pure (Just s) + _ -> do + logWarn CurrentStateNotFound + pure Nothing + +{- Note [Buyer client] + +In the buyer client we want to keep track of the on-chain state of the auction +to give our user a chance to react if they are outbid by somebody else. + +At the same time we want to have the "bid" endpoint active for any bids of our +own, and we want to stop the client when the auction is over. + +To achieve this, we have a loop where we wait for one of several events to +happen and then deal with the event. The waiting is implemented in +@waitForChange@ and the event handling is in @handleEvent@. + +Updates to the user are provided via 'tell'. + +-} + +data BuyerEvent = + AuctionIsOver HighestBid -- ^ The auction has ended with the highest bid + | SubmitOwnBid Ada -- ^ We want to submit a new bid + | OtherBid HighestBid -- ^ Another buyer submitted a higher bid + | NoChange HighestBid -- ^ Nothing has changed + +waitForChange :: AuctionParams -> StateMachineClient AuctionState AuctionInput -> HighestBid -> Contract AuctionOutput BuyerSchema AuctionError BuyerEvent +waitForChange AuctionParams{apEndTime} client lastHighestBid = do + s <- currentSlot + let + auctionOver = awaitSlot apEndTime >> pure (AuctionIsOver lastHighestBid) + submitOwnBid = SubmitOwnBid <$> endpoint @"bid" + otherBid = do + let address = Scripts.validatorAddress (SM.typedValidator (SM.scInstance client)) + targetSlot = Haskell.succ (Haskell.succ s) -- FIXME (jm): There is some off-by-one thing going on that requires us to + -- use succ.succ instead of just a single succ if we want 'addressChangeRequest' + -- to wait for the next slot to begin. + -- I don't have the time to look into that atm though :( + AddressChangeResponse{acrTxns} <- addressChangeRequest + AddressChangeRequest + { acreqSlotRangeFrom = targetSlot + , acreqSlotRangeTo = targetSlot + , acreqAddress = address + } + case acrTxns of + [] -> pure (NoChange lastHighestBid) + _ -> currentState client >>= pure . maybe (AuctionIsOver lastHighestBid) OtherBid + + -- see note [Buyer client] + auctionOver `select` submitOwnBid `select` otherBid + +handleEvent :: StateMachineClient AuctionState AuctionInput -> HighestBid -> BuyerEvent -> Contract AuctionOutput BuyerSchema AuctionError (Either HighestBid ()) +handleEvent client lastHighestBid change = + let continue = pure . Left + stop = pure (Right ()) + -- see note [Buyer client] + in case change of + AuctionIsOver s -> tell (auctionStateOut $ Finished s) >> stop + SubmitOwnBid ada -> do + logInfo @Haskell.String "Submitting bid" + self <- Ledger.pubKeyHash <$> ownPubKey + logInfo @Haskell.String "received pubkey" + r <- SM.runStep client Bid{newBid = ada, newBidder = self} + logInfo @Haskell.String "SM: runStep done" + case r of + SM.TransitionFailure i -> logError (TransitionFailed i) >> continue lastHighestBid + SM.TransitionSuccess (Ongoing newHighestBid) -> logInfo (BidSubmitted newHighestBid) >> continue newHighestBid + + -- the last case shouldn't happen because the "Bid" transition always results in the "Ongoing" + -- but you never know :-) + SM.TransitionSuccess (Finished newHighestBid) -> logError (AuctionEnded newHighestBid) >> stop + OtherBid s -> do + tell (auctionStateOut $ Ongoing s) + continue s + NoChange s -> continue s + +auctionBuyer :: AssetClass -> AuctionParams -> Contract AuctionOutput BuyerSchema AuctionError () +auctionBuyer currency params = do + let inst = typedValidator currency params + client = machineClient inst currency params + + -- the actual loop, see note [Buyer client] + loop = loopM (\h -> waitForChange params client h >>= handleEvent client h) + tell $ threadTokenOut currency + initial <- currentState client + case initial of + Just s -> loop s + + -- If the state can't be found we wait for it to appear. + Nothing -> SM.waitForUpdateUntil client (apEndTime params) >>= \case + WaitingResult (Ongoing s) -> loop s + _ -> logWarn CurrentStateNotFound From b77061b560822bd012806b2f72b5b313a3a6a2a3 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Sat, 31 Jul 2021 16:09:15 +0700 Subject: [PATCH 024/217] add hold auction endpoint --- .../src/Ext/Plutus/Contracts/Auction.hs | 46 ++++++++----- .../Contracts/NftMarketplace/OffChain/User.hs | 69 +++++++++++++++---- .../OnChain/Core/StateMachine.hs | 49 ++++++------- 3 files changed, 105 insertions(+), 59 deletions(-) diff --git a/MetaLamp/nft-marketplace/src/Ext/Plutus/Contracts/Auction.hs b/MetaLamp/nft-marketplace/src/Ext/Plutus/Contracts/Auction.hs index eff0f82f4..29e8ec875 100644 --- a/MetaLamp/nft-marketplace/src/Ext/Plutus/Contracts/Auction.hs +++ b/MetaLamp/nft-marketplace/src/Ext/Plutus/Contracts/Auction.hs @@ -1,18 +1,19 @@ -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE DeriveAnyClass #-} -{-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE DerivingStrategies #-} -{-# LANGUAGE DerivingVia #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE NoImplicitPrelude #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE TypeApplications #-} -{-# LANGUAGE TypeOperators #-} -{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE DerivingVia #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeOperators #-} module Ext.Plutus.Contracts.Auction where @@ -31,7 +32,9 @@ import qualified Ledger.Typed.Scripts as Scripts import Ledger.Typed.Tx (TypedScriptTxOut (..)) import Ledger.Value (AssetClass) import Plutus.Contract -import Plutus.Contract.StateMachine (State (..), StateMachine (..), StateMachineClient, Void, +import Plutus.Contract.StateMachine (State (..), + StateMachine (..), + StateMachineClient, Void, WaitingResult (..)) import qualified Plutus.Contract.StateMachine as SM import Plutus.Contract.Util (loopM) @@ -51,7 +54,15 @@ data AuctionParams deriving anyclass (ToJSON, FromJSON) PlutusTx.makeLift ''AuctionParams +PlutusTx.unstableMakeIsData ''AuctionParams +{-# INLINABLE fromTuple #-} +fromTuple :: (AssetClass, PubKeyHash, Value, Slot) -> AuctionParams +fromTuple (_, apOwner, apAsset, apEndTime) = AuctionParams {..} + +{-# INLINABLE toTuple #-} +toTuple :: AssetClass -> AuctionParams -> (AssetClass, PubKeyHash, Value, Slot) +toTuple threadToken AuctionParams {..} = (threadToken, apOwner, apAsset, apEndTime) data HighestBid = HighestBid @@ -201,11 +212,10 @@ instance SM.AsSMContractError AuctionError where -- | Client code for the seller -startAuction :: Value -> Slot -> Contract AuctionOutput SellerSchema AuctionError (AssetClass, AuctionParams) +startAuction :: Value -> Slot -> Contract w s AuctionError (AssetClass, AuctionParams) startAuction value slot = do threadToken <- mapError ThreadTokenError Currency.createThreadToken logInfo $ "Obtained thread token: " <> Haskell.show threadToken - tell $ threadTokenOut threadToken self <- Ledger.pubKeyHash <$> ownPubKey let params = AuctionParams{apOwner = self, apAsset = value, apEndTime = slot } inst = typedValidator threadToken params @@ -219,7 +229,7 @@ startAuction value slot = do pure (threadToken, params) -- | Client code for the seller -payoutAuction :: AssetClass -> AuctionParams -> Contract AuctionOutput SellerSchema AuctionError () +payoutAuction :: AssetClass -> AuctionParams -> Contract w s AuctionError () payoutAuction threadToken params = do let inst = typedValidator threadToken params client = machineClient inst threadToken params diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs index 8e0e65c55..5bcb45594 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs @@ -13,13 +13,15 @@ module Plutus.Contracts.NftMarketplace.OffChain.User where -import Control.Lens ((^.)) +import Control.Lens (_Left, (^.), + (^?)) import qualified Control.Lens as Lens import Control.Monad hiding (fmap) import qualified Data.Aeson as J import Data.Proxy (Proxy (..)) import Data.Text (Text) import qualified Data.Text as T +import qualified Ext.Plutus.Contracts.Auction as Auction import Ext.Plutus.Ledger.Value (utxoValue) import qualified GHC.Generics as Haskell import Ledger @@ -81,7 +83,7 @@ createNft marketplace CreateNftParams {..} = do , nftName = cnpNftName , nftDescription = cnpNftDescription , nftIssuer = if cnpRevealIssuer then Just pkh else Nothing - , nftSale = Nothing -- TODO validate that it's Nothing + , nftLot = Nothing -- TODO validate that it's Nothing } void $ mapError' $ runStep client $ Core.CreateNftRedeemer ipfsCidHash nftEntry @@ -115,10 +117,10 @@ openSale marketplace OpenSaleParams {..} = do let client = Core.marketplaceClient marketplace let lot = Core.Lot - { lotSale = Sale.toTuple sale + { lotLink = Left $ Sale.toTuple sale , lotIpfsCid = ospIpfsCid } - void $ mapError' $ runStep client $ Core.OpenSaleRedeemer ipfsCidHash lot + void $ mapError' $ runStep client $ Core.PutLotRedeemer ipfsCidHash lot logInfo @Haskell.String $ printf "Created NFT sale %s" (Haskell.show lot) pure () @@ -139,14 +141,15 @@ buyNft marketplace BuyNftParams {..} = do let ipfsCidHash = sha2_256 bnpIpfsCid nftStore <- marketplaceStore marketplace nftEntry <- maybe (throwError "NFT has not been created") pure $ AssocMap.lookup ipfsCidHash nftStore - nftLot <- maybe (throwError "NFT has not been put on sale") pure $ nftEntry ^. Core._nftSale + nftSale <- maybe (throwError "NFT has not been put on sale") pure $ + nftEntry ^. Core._nftLot ^? traverse . Core._lotLink . _Left - _ <- Sale.buyLot $ Sale.fromTuple $ nftLot ^. Core._lotSale + _ <- Sale.buyLot $ Sale.fromTuple nftSale let client = Core.marketplaceClient marketplace - void $ mapError' $ runStep client $ Core.BuyNftRedeemer ipfsCidHash + void $ mapError' $ runStep client $ Core.RemoveLotRedeemer ipfsCidHash - logInfo @Haskell.String $ printf "Bought NFT lot %s" (Haskell.show nftLot) + logInfo @Haskell.String $ printf "Bought NFT from sale %s" (Haskell.show nftSale) pure () data CloseSaleParams = @@ -165,14 +168,53 @@ closeSale marketplace CloseSaleParams {..} = do let ipfsCidHash = sha2_256 cspIpfsCid nftStore <- marketplaceStore marketplace nftEntry <- maybe (throwError "NFT has not been created") pure $ AssocMap.lookup ipfsCidHash nftStore - nftLot <- maybe (throwError "NFT has not been put on sale") pure $ nftEntry ^. Core._nftSale + nftSale <- maybe (throwError "NFT has not been put on sale") pure $ + nftEntry ^. Core._nftLot ^? traverse . Core._lotLink . _Left - _ <- Sale.redeemLot $ Sale.fromTuple $ nftLot ^. Core._lotSale + _ <- Sale.redeemLot $ Sale.fromTuple nftSale let client = Core.marketplaceClient marketplace - void $ mapError' $ runStep client $ Core.CloseSaleRedeemer ipfsCidHash + void $ mapError' $ runStep client $ Core.RemoveLotRedeemer ipfsCidHash - logInfo @Haskell.String $ printf "Closed NFT lot sale %s" (Haskell.show nftLot) + logInfo @Haskell.String $ printf "Closed NFT sale %s" (Haskell.show nftSale) + pure () + +data HoldAnAuctionParams = + HoldAnAuctionParams { + haapIpfsCid :: ByteString, + haapDuration :: Slot + } + deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) + deriving anyclass (J.ToJSON, J.FromJSON, Schema.ToSchema) + +PlutusTx.unstableMakeIsData ''HoldAnAuctionParams +PlutusTx.makeLift ''HoldAnAuctionParams + +-- | The user +holdAnAuction :: Core.Marketplace -> HoldAnAuctionParams -> Contract w s Text () +holdAnAuction marketplace HoldAnAuctionParams {..} = do + let ipfsCidHash = sha2_256 haapIpfsCid + nftStore <- marketplaceStore marketplace + nftEntry <- maybe (throwError "NFT has not been created") pure $ AssocMap.lookup ipfsCidHash nftStore + let tokenName = V.TokenName haapIpfsCid + let nftValue = V.singleton (Core.nftId nftEntry) tokenName 1 + + currSlot <- currentSlot + let endTime = currSlot + haapDuration + (auctionToken, auctionParams) <- mapError (T.pack . Haskell.show) $ Auction.startAuction nftValue endTime + + let client = Core.marketplaceClient marketplace + let lot = Core.Lot + { lotLink = Right $ Auction.toTuple auctionToken auctionParams + , lotIpfsCid = haapIpfsCid + } + void $ mapError' $ runStep client $ Core.PutLotRedeemer ipfsCidHash lot + + _ <- mapError (T.pack . Haskell.show) $ Auction.payoutAuction auctionToken auctionParams + + void $ mapError' $ runStep client $ Core.RemoveLotRedeemer ipfsCidHash + + logInfo @Haskell.String $ printf "Conducted an auction for NFT lot %s" (Haskell.show lot) pure () balanceAt :: PubKeyHash -> AssetClass -> Contract w s Text Integer @@ -186,6 +228,7 @@ type MarketplaceUserSchema = .\/ Endpoint "openSale" OpenSaleParams .\/ Endpoint "buyNft" BuyNftParams .\/ Endpoint "closeSale" CloseSaleParams + .\/ Endpoint "holdAnAuction" HoldAnAuctionParams .\/ Endpoint "ownPubKey" () .\/ Endpoint "ownPubKeyBalance" () @@ -194,6 +237,7 @@ data UserContractState = | OpenedSale | NftBought | ClosedSale + | AuctionComplete | GetPubKey PubKeyHash | GetPubKeyBalance Value deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) @@ -207,5 +251,6 @@ userEndpoints marketplace = forever $ `select` withContractResponse (Proxy @"openSale") (const OpenedSale) (openSale marketplace) `select` withContractResponse (Proxy @"buyNft") (const NftBought) (buyNft marketplace) `select` withContractResponse (Proxy @"closeSale") (const ClosedSale) (closeSale marketplace) + `select` withContractResponse (Proxy @"holdAnAuction") (const AuctionComplete) (holdAnAuction marketplace) `select` withContractResponse (Proxy @"ownPubKey") GetPubKey (const getOwnPubKey) `select` withContractResponse (Proxy @"ownPubKeyBalance") GetPubKeyBalance (const ownPubKeyBalance) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs index e6454ec45..27c1f6c23 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs @@ -15,6 +15,7 @@ import Control.Lens ((&), (.~), (?~), (^.)) import qualified Control.Lens as Lens import qualified Data.Aeson as J import qualified Data.Text as T +import qualified Ext.Plutus.Contracts.Auction as Auction import qualified GHC.Generics as Haskell import Ledger import qualified Ledger.Constraints as Constraints @@ -40,9 +41,10 @@ PlutusTx.makeLift ''Marketplace type IpfsCidHash = ByteString type Sale = (AssetClass, Sale.LovelacePrice, Value) +type Auction = (AssetClass, PubKeyHash, Value, Slot) data Lot = Lot - { lotSale :: Sale + { lotLink :: Either Sale Auction , lotIpfsCid :: ByteString } deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) @@ -60,7 +62,7 @@ data NFT = , nftName :: ByteString , nftDescription :: ByteString , nftIssuer :: Maybe PubKeyHash - , nftSale :: Maybe Lot + , nftLot :: Maybe Lot } deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON) @@ -73,9 +75,8 @@ Lens.makeClassy_ ''NFT data MarketplaceRedeemer = CreateNftRedeemer IpfsCidHash NFT - | OpenSaleRedeemer IpfsCidHash Lot - | BuyNftRedeemer IpfsCidHash - | CloseSaleRedeemer IpfsCidHash + | PutLotRedeemer IpfsCidHash Lot + | RemoveLotRedeemer IpfsCidHash deriving (Haskell.Show) PlutusTx.unstableMakeIsData ''MarketplaceRedeemer @@ -100,20 +101,14 @@ transition marketplace state redeemer = case redeemer of -> Just ( mustBeSignedByIssuer nftEntry , State (MarketplaceDatum $ AssocMap.insert ipfsCidHash nftEntry nftStore) currStateValue ) - OpenSaleRedeemer ipfsCidHash lot - -> let newEntry = maybe (traceError "NFT has not been created.") (_nftSale ?~ lot) $ + PutLotRedeemer ipfsCidHash lot + -> let newEntry = maybe (traceError "NFT has not been created.") (_nftLot ?~ lot) $ AssocMap.lookup ipfsCidHash nftStore in Just ( mempty , State (MarketplaceDatum $ AssocMap.insert ipfsCidHash newEntry nftStore) currStateValue ) - BuyNftRedeemer ipfsCidHash - -> let newEntry = maybe (traceError "NFT has not been created.") (_nftSale .~ Nothing) $ - AssocMap.lookup ipfsCidHash nftStore - in Just ( mempty - , State (MarketplaceDatum $ AssocMap.insert ipfsCidHash newEntry nftStore) currStateValue - ) - CloseSaleRedeemer ipfsCidHash - -> let newEntry = maybe (traceError "NFT has not been created.") (_nftSale .~ Nothing) $ + RemoveLotRedeemer ipfsCidHash + -> let newEntry = maybe (traceError "NFT has not been created.") (_nftLot .~ Nothing) $ AssocMap.lookup ipfsCidHash nftStore in Just ( mempty , State (MarketplaceDatum $ AssocMap.insert ipfsCidHash newEntry nftStore) currStateValue @@ -138,26 +133,22 @@ stateTransitionCheck (MarketplaceDatum nftStore) (CreateNftRedeemer ipfsCidHash traceIfFalse "CreateNftRedeemer: " $ traceIfFalse "NFT entry already exists" $ isNothing $ AssocMap.lookup ipfsCidHash nftStore -stateTransitionCheck (MarketplaceDatum nftStore) (OpenSaleRedeemer ipfsCidHash lot) ctx = - traceIfFalse "OpenSaleRedeemer: " $ +stateTransitionCheck (MarketplaceDatum nftStore) (PutLotRedeemer ipfsCidHash lot) ctx = + traceIfFalse "PutLotRedeemer: " $ let nftEntry = fromMaybe (traceError "NFT has not been created") $ AssocMap.lookup ipfsCidHash nftStore nftIpfsCid = lotIpfsCid lot - sale = Sale.fromTuple $ lotSale lot + lotValue = either (Sale.saleValue . Sale.fromTuple) (Auction.apAsset . Auction.fromTuple) $ lotLink lot nftValue = V.singleton (nftId nftEntry) (V.TokenName nftIpfsCid) 1 - hasBeenPutOnSale = Sale.saleValue sale == nftValue + hasBeenPutOnSale = lotValue == nftValue isValidHash = sha2_256 nftIpfsCid == ipfsCidHash - in traceIfFalse "NFT has not been put on sale" hasBeenPutOnSale && +-- TODO (?) check that there was no previous lot (isNothing $ nftLot nftEntry) + in traceIfFalse "NFT has not been put on sale or auction" hasBeenPutOnSale && traceIfFalse "Invalid IPFS Cid Hash" isValidHash -stateTransitionCheck (MarketplaceDatum nftStore) (BuyNftRedeemer ipfsCidHash) ctx = - traceIfFalse "BuyNftRedeemer: " $ - let nftEntry = fromMaybe (traceError "NFT has not been created") $ AssocMap.lookup ipfsCidHash nftStore - hasBeenPutOnSale = isJust $ nftSale nftEntry - in traceIfFalse "NFT has not been put on sale" hasBeenPutOnSale -stateTransitionCheck (MarketplaceDatum nftStore) (CloseSaleRedeemer ipfsCidHash) ctx = - traceIfFalse "CloseSaleRedeemer: " $ +stateTransitionCheck (MarketplaceDatum nftStore) (RemoveLotRedeemer ipfsCidHash) ctx = + traceIfFalse "RemoveLotRedeemer: " $ let nftEntry = fromMaybe (traceError "NFT has not been created") $ AssocMap.lookup ipfsCidHash nftStore - hasBeenPutOnSale = isJust $ nftSale nftEntry - in traceIfFalse "NFT has not been put on sale" hasBeenPutOnSale + hasBeenPutOnSale = isJust $ nftLot nftEntry + in traceIfFalse "NFT has not been put on sale or auction" hasBeenPutOnSale {-# INLINABLE marketplaceStateMachine #-} marketplaceStateMachine :: Marketplace -> StateMachine MarketplaceDatum MarketplaceRedeemer From 9f7a1fe84cc2a9fd78074b285fc74861ffc0b5e0 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Sat, 31 Jul 2021 18:42:18 +0700 Subject: [PATCH 025/217] add auction buyer endpoints --- .../src/Ext/Plutus/Contracts/Auction.hs | 110 +++--------------- .../Contracts/NftMarketplace/OffChain/Info.hs | 24 ++++ .../Contracts/NftMarketplace/OffChain/User.hs | 34 +++++- .../OnChain/Core/StateMachine.hs | 1 + 4 files changed, 75 insertions(+), 94 deletions(-) diff --git a/MetaLamp/nft-marketplace/src/Ext/Plutus/Contracts/Auction.hs b/MetaLamp/nft-marketplace/src/Ext/Plutus/Contracts/Auction.hs index 29e8ec875..2a08c1c6a 100644 --- a/MetaLamp/nft-marketplace/src/Ext/Plutus/Contracts/Auction.hs +++ b/MetaLamp/nft-marketplace/src/Ext/Plutus/Contracts/Auction.hs @@ -64,6 +64,10 @@ fromTuple (_, apOwner, apAsset, apEndTime) = AuctionParams {..} toTuple :: AssetClass -> AuctionParams -> (AssetClass, PubKeyHash, Value, Slot) toTuple threadToken AuctionParams {..} = (threadToken, apOwner, apAsset, apEndTime) +{-# INLINABLE getStateToken #-} +getStateToken :: (AssetClass, PubKeyHash, Value, Slot) -> AssetClass +getStateToken (token, _, _, _) = token + data HighestBid = HighestBid { highestBid :: Ada @@ -243,100 +247,22 @@ payoutAuction threadToken params = do SM.TransitionSuccess s -> logWarn ("Unexpected state after Payout transition: " <> Haskell.show s) -- | Get the current state of the contract and log it. -currentState :: StateMachineClient AuctionState AuctionInput -> Contract AuctionOutput BuyerSchema AuctionError (Maybe HighestBid) -currentState client = mapError StateMachineContractError (SM.getOnChainState client) >>= \case - Just ((TypedScriptTxOut{tyTxOutData=Ongoing s}, _), _) -> do - tell $ auctionStateOut $ Ongoing s +currentState :: AssetClass -> AuctionParams -> Contract w s AuctionError (Maybe AuctionState) +currentState threadToken params = mapError StateMachineContractError (SM.getOnChainState client) >>= \case + Just ((TypedScriptTxOut{tyTxOutData = s}, _), _) -> do pure (Just s) _ -> do logWarn CurrentStateNotFound pure Nothing + where + inst = typedValidator threadToken params + client = machineClient inst threadToken params -{- Note [Buyer client] - -In the buyer client we want to keep track of the on-chain state of the auction -to give our user a chance to react if they are outbid by somebody else. - -At the same time we want to have the "bid" endpoint active for any bids of our -own, and we want to stop the client when the auction is over. - -To achieve this, we have a loop where we wait for one of several events to -happen and then deal with the event. The waiting is implemented in -@waitForChange@ and the event handling is in @handleEvent@. - -Updates to the user are provided via 'tell'. - --} - -data BuyerEvent = - AuctionIsOver HighestBid -- ^ The auction has ended with the highest bid - | SubmitOwnBid Ada -- ^ We want to submit a new bid - | OtherBid HighestBid -- ^ Another buyer submitted a higher bid - | NoChange HighestBid -- ^ Nothing has changed - -waitForChange :: AuctionParams -> StateMachineClient AuctionState AuctionInput -> HighestBid -> Contract AuctionOutput BuyerSchema AuctionError BuyerEvent -waitForChange AuctionParams{apEndTime} client lastHighestBid = do - s <- currentSlot - let - auctionOver = awaitSlot apEndTime >> pure (AuctionIsOver lastHighestBid) - submitOwnBid = SubmitOwnBid <$> endpoint @"bid" - otherBid = do - let address = Scripts.validatorAddress (SM.typedValidator (SM.scInstance client)) - targetSlot = Haskell.succ (Haskell.succ s) -- FIXME (jm): There is some off-by-one thing going on that requires us to - -- use succ.succ instead of just a single succ if we want 'addressChangeRequest' - -- to wait for the next slot to begin. - -- I don't have the time to look into that atm though :( - AddressChangeResponse{acrTxns} <- addressChangeRequest - AddressChangeRequest - { acreqSlotRangeFrom = targetSlot - , acreqSlotRangeTo = targetSlot - , acreqAddress = address - } - case acrTxns of - [] -> pure (NoChange lastHighestBid) - _ -> currentState client >>= pure . maybe (AuctionIsOver lastHighestBid) OtherBid - - -- see note [Buyer client] - auctionOver `select` submitOwnBid `select` otherBid - -handleEvent :: StateMachineClient AuctionState AuctionInput -> HighestBid -> BuyerEvent -> Contract AuctionOutput BuyerSchema AuctionError (Either HighestBid ()) -handleEvent client lastHighestBid change = - let continue = pure . Left - stop = pure (Right ()) - -- see note [Buyer client] - in case change of - AuctionIsOver s -> tell (auctionStateOut $ Finished s) >> stop - SubmitOwnBid ada -> do - logInfo @Haskell.String "Submitting bid" - self <- Ledger.pubKeyHash <$> ownPubKey - logInfo @Haskell.String "received pubkey" - r <- SM.runStep client Bid{newBid = ada, newBidder = self} - logInfo @Haskell.String "SM: runStep done" - case r of - SM.TransitionFailure i -> logError (TransitionFailed i) >> continue lastHighestBid - SM.TransitionSuccess (Ongoing newHighestBid) -> logInfo (BidSubmitted newHighestBid) >> continue newHighestBid - - -- the last case shouldn't happen because the "Bid" transition always results in the "Ongoing" - -- but you never know :-) - SM.TransitionSuccess (Finished newHighestBid) -> logError (AuctionEnded newHighestBid) >> stop - OtherBid s -> do - tell (auctionStateOut $ Ongoing s) - continue s - NoChange s -> continue s - -auctionBuyer :: AssetClass -> AuctionParams -> Contract AuctionOutput BuyerSchema AuctionError () -auctionBuyer currency params = do - let inst = typedValidator currency params - client = machineClient inst currency params - - -- the actual loop, see note [Buyer client] - loop = loopM (\h -> waitForChange params client h >>= handleEvent client h) - tell $ threadTokenOut currency - initial <- currentState client - case initial of - Just s -> loop s - - -- If the state can't be found we wait for it to appear. - Nothing -> SM.waitForUpdateUntil client (apEndTime params) >>= \case - WaitingResult (Ongoing s) -> loop s - _ -> logWarn CurrentStateNotFound +submitBid :: AssetClass -> AuctionParams -> Ada -> Contract w s AuctionError () +submitBid threadToken params ada = do + let inst = typedValidator threadToken params + client = machineClient inst threadToken params + self <- Ledger.pubKeyHash <$> ownPubKey + let bid = Bid{newBid = ada, newBidder = self} + _ <- SM.runStep client bid + logInfo @Haskell.String $ "Bid submitted" <> Haskell.show bid diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs index 0131635af..f6bd55296 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs @@ -13,12 +13,15 @@ module Plutus.Contracts.NftMarketplace.OffChain.Info where +import Control.Lens (_Left, _Right, + (^.), (^?)) import qualified Control.Lens as Lens import Control.Monad hiding (fmap) import qualified Data.Aeson as J import Data.Proxy (Proxy (..)) import Data.Text (Text) import qualified Data.Text as T +import qualified Ext.Plutus.Contracts.Auction as Auction import Ext.Plutus.Ledger.Value (utxoValue) import qualified GHC.Generics as Haskell import Ledger @@ -57,6 +60,24 @@ fundsAt pkh = utxoValue <$> utxoAt (pubKeyHashAddress pkh) marketplaceFunds :: Core.Marketplace -> Contract w s Text Value marketplaceFunds marketplace = utxoValue <$> utxoAt (Core.marketplaceAddress marketplace) +-- | Gets +getAuctionState :: Core.Marketplace -> Core.IpfsCid -> Contract w s Text Auction.AuctionState +getAuctionState marketplace ipfsCid = do + let ipfsCidHash = sha2_256 ipfsCid + nftStore <- marketplaceStore marketplace + nftEntry <- maybe (throwError "NFT has not been created") pure $ AssocMap.lookup ipfsCidHash nftStore + nftAuction <- maybe (throwError "NFT has not been put on auction") pure $ + nftEntry ^. Core._nftLot ^? traverse . Core._lotLink . _Right + + let auctionToken = Auction.getStateToken nftAuction + let auctionParams = Auction.fromTuple nftAuction + auctionState <- do + st <- mapError (T.pack . Haskell.show) $ Auction.currentState auctionToken auctionParams + maybe (throwError "Auction state not found") pure st + + logInfo @Haskell.String $ printf "Returned auction state %s" (Haskell.show auctionState) + pure auctionState + mapError' :: Contract w s SMContractError a -> Contract w s Text a mapError' = mapError $ T.pack . Haskell.show @@ -64,11 +85,13 @@ type MarketplaceInfoSchema = Endpoint "fundsAt" PubKeyHash .\/ Endpoint "marketplaceFunds" () .\/ Endpoint "marketplaceStore" () + .\/ Endpoint "getAuctionState" Core.IpfsCid data InfoContractState = FundsAt Value | MarketplaceFunds Value | MarketplaceStore (AssocMap.Map Core.IpfsCidHash Core.NFT) + | AuctionState Auction.AuctionState deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON) @@ -77,3 +100,4 @@ infoEndpoints marketplace = forever $ withContractResponse (Proxy @"fundsAt") FundsAt fundsAt `select` withContractResponse (Proxy @"marketplaceFunds") MarketplaceFunds (const $ marketplaceFunds marketplace) `select` withContractResponse (Proxy @"marketplaceStore") MarketplaceStore (const $ marketplaceStore marketplace) + `select` withContractResponse (Proxy @"getAuctionState") AuctionState (getAuctionState marketplace) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs index 5bcb45594..c086fb6df 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs @@ -13,8 +13,8 @@ module Plutus.Contracts.NftMarketplace.OffChain.User where -import Control.Lens (_Left, (^.), - (^?)) +import Control.Lens (_Left, _Right, + (^.), (^?)) import qualified Control.Lens as Lens import Control.Monad hiding (fmap) import qualified Data.Aeson as J @@ -217,6 +217,33 @@ holdAnAuction marketplace HoldAnAuctionParams {..} = do logInfo @Haskell.String $ printf "Conducted an auction for NFT lot %s" (Haskell.show lot) pure () +data BidOnAuctionParams = + BidOnAuctionParams { + japIpfsCid :: ByteString, + japBid :: Ada + } + deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) + deriving anyclass (J.ToJSON, J.FromJSON, Schema.ToSchema) + +PlutusTx.unstableMakeIsData ''BidOnAuctionParams +PlutusTx.makeLift ''BidOnAuctionParams + +-- | The user +bidOnAuction :: Core.Marketplace -> BidOnAuctionParams -> Contract w s Text () +bidOnAuction marketplace BidOnAuctionParams {..} = do + let ipfsCidHash = sha2_256 japIpfsCid + nftStore <- marketplaceStore marketplace + nftEntry <- maybe (throwError "NFT has not been created") pure $ AssocMap.lookup ipfsCidHash nftStore + nftAuction <- maybe (throwError "NFT has not been put on auction") pure $ + nftEntry ^. Core._nftLot ^? traverse . Core._lotLink . _Right + + let auctionToken = Auction.getStateToken nftAuction + let auctionParams = Auction.fromTuple nftAuction + _ <- mapError (T.pack . Haskell.show) $ Auction.submitBid auctionToken auctionParams japBid + + logInfo @Haskell.String $ printf "Submitted bid for NFT auction %s" (Haskell.show nftAuction) + pure () + balanceAt :: PubKeyHash -> AssetClass -> Contract w s Text Integer balanceAt pkh asset = flip V.assetClassValueOf asset <$> fundsAt pkh @@ -229,6 +256,7 @@ type MarketplaceUserSchema = .\/ Endpoint "buyNft" BuyNftParams .\/ Endpoint "closeSale" CloseSaleParams .\/ Endpoint "holdAnAuction" HoldAnAuctionParams + .\/ Endpoint "bidOnAuction" BidOnAuctionParams .\/ Endpoint "ownPubKey" () .\/ Endpoint "ownPubKeyBalance" () @@ -238,6 +266,7 @@ data UserContractState = | NftBought | ClosedSale | AuctionComplete + | BidSubmitted | GetPubKey PubKeyHash | GetPubKeyBalance Value deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) @@ -252,5 +281,6 @@ userEndpoints marketplace = forever $ `select` withContractResponse (Proxy @"buyNft") (const NftBought) (buyNft marketplace) `select` withContractResponse (Proxy @"closeSale") (const ClosedSale) (closeSale marketplace) `select` withContractResponse (Proxy @"holdAnAuction") (const AuctionComplete) (holdAnAuction marketplace) + `select` withContractResponse (Proxy @"bidOnAuction") (const BidSubmitted) (bidOnAuction marketplace) `select` withContractResponse (Proxy @"ownPubKey") GetPubKey (const getOwnPubKey) `select` withContractResponse (Proxy @"ownPubKeyBalance") GetPubKeyBalance (const ownPubKeyBalance) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs index 27c1f6c23..69bdf0c85 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs @@ -39,6 +39,7 @@ newtype Marketplace = PlutusTx.makeLift ''Marketplace +type IpfsCid = ByteString type IpfsCidHash = ByteString type Sale = (AssetClass, Sale.LovelacePrice, Value) type Auction = (AssetClass, PubKeyHash, Value, Slot) From 65049fc5e4845e1c805547a8f594c7f2d89c918e Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Sun, 1 Aug 2021 13:42:35 +0700 Subject: [PATCH 026/217] add auction simulation --- .../src/Plutus/Abstract/ContractResponse.hs | 9 ++--- .../Contracts/NftMarketplace/OffChain/User.hs | 31 +++++++++++++---- .../src/Plutus/PAB/Simulation.hs | 34 +++++++++++++++++++ 3 files changed, 63 insertions(+), 11 deletions(-) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Abstract/ContractResponse.hs b/MetaLamp/nft-marketplace/src/Plutus/Abstract/ContractResponse.hs index 54d37010e..61e32b826 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Abstract/ContractResponse.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Abstract/ContractResponse.hs @@ -70,7 +70,8 @@ withContractResponse _ g c = do tell $ case e of Left err -> ContractError err Right a -> ContractSuccess $ g a - where - errorHandler e = do - logInfo @Text ("Error submiting the transaction: " <> e) - throwError e + +errorHandler :: Text -> Contract w s Text b +errorHandler e = do + logInfo @Text ("Error submiting the transaction: " <> e) + throwError e diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs index c086fb6df..c07259a3b 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs @@ -28,8 +28,7 @@ import Ledger import qualified Ledger.Typed.Scripts as Scripts import Ledger.Typed.Tx import qualified Ledger.Value as V -import Plutus.Abstract.ContractResponse (ContractResponse, - withContractResponse) +import Plutus.Abstract.ContractResponse import Plutus.Contract import Plutus.Contract.StateMachine import Plutus.Contracts.Currency as Currency @@ -191,8 +190,8 @@ PlutusTx.unstableMakeIsData ''HoldAnAuctionParams PlutusTx.makeLift ''HoldAnAuctionParams -- | The user -holdAnAuction :: Core.Marketplace -> HoldAnAuctionParams -> Contract w s Text () -holdAnAuction marketplace HoldAnAuctionParams {..} = do +startAnAuction :: Core.Marketplace -> HoldAnAuctionParams -> Contract w s Text () +startAnAuction marketplace HoldAnAuctionParams {..} = do let ipfsCidHash = sha2_256 haapIpfsCid nftStore <- marketplaceStore marketplace nftEntry <- maybe (throwError "NFT has not been created") pure $ AssocMap.lookup ipfsCidHash nftStore @@ -210,11 +209,26 @@ holdAnAuction marketplace HoldAnAuctionParams {..} = do } void $ mapError' $ runStep client $ Core.PutLotRedeemer ipfsCidHash lot + logInfo @Haskell.String $ printf "Started an auction for NFT lot %s" (Haskell.show lot) + pure () + +-- | The user +completeAnAuction :: Core.Marketplace -> HoldAnAuctionParams -> Contract w s Text () +completeAnAuction marketplace HoldAnAuctionParams {..} = do + let ipfsCidHash = sha2_256 haapIpfsCid + nftStore <- marketplaceStore marketplace + nftEntry <- maybe (throwError "NFT has not been created") pure $ AssocMap.lookup ipfsCidHash nftStore + nftAuction <- maybe (throwError "NFT has not been put on auction") pure $ + nftEntry ^. Core._nftLot ^? traverse . Core._lotLink . _Right + + let auctionToken = Auction.getStateToken nftAuction + let auctionParams = Auction.fromTuple nftAuction _ <- mapError (T.pack . Haskell.show) $ Auction.payoutAuction auctionToken auctionParams + let client = Core.marketplaceClient marketplace void $ mapError' $ runStep client $ Core.RemoveLotRedeemer ipfsCidHash - logInfo @Haskell.String $ printf "Conducted an auction for NFT lot %s" (Haskell.show lot) + logInfo @Haskell.String $ printf "Completed an auction %s" (Haskell.show nftAuction) pure () data BidOnAuctionParams = @@ -255,7 +269,8 @@ type MarketplaceUserSchema = .\/ Endpoint "openSale" OpenSaleParams .\/ Endpoint "buyNft" BuyNftParams .\/ Endpoint "closeSale" CloseSaleParams - .\/ Endpoint "holdAnAuction" HoldAnAuctionParams + .\/ Endpoint "startAnAuction" HoldAnAuctionParams + .\/ Endpoint "completeAnAuction" HoldAnAuctionParams .\/ Endpoint "bidOnAuction" BidOnAuctionParams .\/ Endpoint "ownPubKey" () .\/ Endpoint "ownPubKeyBalance" () @@ -265,6 +280,7 @@ data UserContractState = | OpenedSale | NftBought | ClosedSale + | AuctionStarted | AuctionComplete | BidSubmitted | GetPubKey PubKeyHash @@ -280,7 +296,8 @@ userEndpoints marketplace = forever $ `select` withContractResponse (Proxy @"openSale") (const OpenedSale) (openSale marketplace) `select` withContractResponse (Proxy @"buyNft") (const NftBought) (buyNft marketplace) `select` withContractResponse (Proxy @"closeSale") (const ClosedSale) (closeSale marketplace) - `select` withContractResponse (Proxy @"holdAnAuction") (const AuctionComplete) (holdAnAuction marketplace) + `select` withContractResponse (Proxy @"startAnAuction") (const AuctionStarted) (startAnAuction marketplace) + `select` withContractResponse (Proxy @"completeAnAuction") (const AuctionComplete) (completeAnAuction marketplace) `select` withContractResponse (Proxy @"bidOnAuction") (const BidSubmitted) (bidOnAuction marketplace) `select` withContractResponse (Proxy @"ownPubKey") GetPubKey (const getOwnPubKey) `select` withContractResponse (Proxy @"ownPubKeyBalance") GetPubKeyBalance (const ownPubKeyBalance) diff --git a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs index 8a6070864..fbec3571c 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs @@ -172,6 +172,40 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do _ -> Nothing Simulator.logString @(Builtin MarketplaceContracts) $ "Successful closeSale" + let auction = Marketplace.HoldAnAuctionParams { + haapIpfsCid = photoTokenIpfsCid, + haapDuration = 80 + } + _ <- + Simulator.callEndpointOnInstance userCid "startAnAuction" auction + _ <- flip Simulator.waitForState userCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of + Success (ContractSuccess Marketplace.AuctionStarted) -> Just () + _ -> Nothing + Simulator.logString @(Builtin MarketplaceContracts) $ "Started An Auction" + + _ <- + Simulator.callEndpointOnInstance buyerCid "bidOnAuction" Marketplace.BidOnAuctionParams { + japIpfsCid = photoTokenIpfsCid, + japBid = fromInteger $ 15*oneAdaInLovelace + } + _ <- flip Simulator.waitForState buyerCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of + Success (ContractSuccess Marketplace.BidSubmitted) -> Just () + _ -> Nothing + Simulator.logString @(Builtin MarketplaceContracts) $ "Successful bidOnAuction" + + _ <- Simulator.callEndpointOnInstance cidInfo "getAuctionState" photoTokenIpfsCid + s <- flip Simulator.waitForState cidInfo $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.InfoContractState)) of + Success (ContractSuccess (Marketplace.AuctionState s)) -> Just s + _ -> Nothing + Simulator.logString @(Builtin MarketplaceContracts) $ "Final auction state: " <> show s + + _ <- + Simulator.callEndpointOnInstance buyerCid "completeAnAuction" auction + _ <- flip Simulator.waitForState buyerCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of + Success (ContractSuccess Marketplace.AuctionComplete) -> Just () + _ -> Nothing + Simulator.logString @(Builtin MarketplaceContracts) $ "Successful holdAnAuction" + _ <- Simulator.callEndpointOnInstance cidInfo "fundsAt" buyer v <- flip Simulator.waitForState cidInfo $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.InfoContractState)) of Success (ContractSuccess (Marketplace.FundsAt v)) -> Just v From 261b1e143f912999715ee275b053c26b5f9c21aa Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Sun, 1 Aug 2021 13:57:14 +0700 Subject: [PATCH 027/217] add endpoints description --- .../src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs | 2 +- .../src/Plutus/Contracts/NftMarketplace/OffChain/User.hs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs index f6bd55296..3c9c9695b 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs @@ -60,7 +60,7 @@ fundsAt pkh = utxoValue <$> utxoAt (pubKeyHashAddress pkh) marketplaceFunds :: Core.Marketplace -> Contract w s Text Value marketplaceFunds marketplace = utxoValue <$> utxoAt (Core.marketplaceAddress marketplace) --- | Gets +-- | Gets current auction state for specified NFT getAuctionState :: Core.Marketplace -> Core.IpfsCid -> Contract w s Text Auction.AuctionState getAuctionState marketplace ipfsCid = do let ipfsCidHash = sha2_256 ipfsCid diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs index c07259a3b..188b4b852 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs @@ -189,7 +189,7 @@ data HoldAnAuctionParams = PlutusTx.unstableMakeIsData ''HoldAnAuctionParams PlutusTx.makeLift ''HoldAnAuctionParams --- | The user +-- | The user starts an auction for specified NFT startAnAuction :: Core.Marketplace -> HoldAnAuctionParams -> Contract w s Text () startAnAuction marketplace HoldAnAuctionParams {..} = do let ipfsCidHash = sha2_256 haapIpfsCid @@ -212,7 +212,7 @@ startAnAuction marketplace HoldAnAuctionParams {..} = do logInfo @Haskell.String $ printf "Started an auction for NFT lot %s" (Haskell.show lot) pure () --- | The user +-- | The user completes the auction for specified NFT completeAnAuction :: Core.Marketplace -> HoldAnAuctionParams -> Contract w s Text () completeAnAuction marketplace HoldAnAuctionParams {..} = do let ipfsCidHash = sha2_256 haapIpfsCid @@ -242,7 +242,7 @@ data BidOnAuctionParams = PlutusTx.unstableMakeIsData ''BidOnAuctionParams PlutusTx.makeLift ''BidOnAuctionParams --- | The user +-- | The user submits a bid on the auction for specified NFT bidOnAuction :: Core.Marketplace -> BidOnAuctionParams -> Contract w s Text () bidOnAuction marketplace BidOnAuctionParams {..} = do let ipfsCidHash = sha2_256 japIpfsCid From bb2b92df9cecfb9c91a07c11213a6e6dc28fcb78 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Sun, 1 Aug 2021 15:57:40 +0700 Subject: [PATCH 028/217] mv models to a module --- MetaLamp/nft-marketplace/plutus-starter.cabal | 2 +- .../Contracts/NftMarketplace/OnChain/Core.hs | 1 + .../NftMarketplace/OnChain/Core/NFT.hs | 71 +++++++++++++++++++ .../OnChain/Core/StateMachine.hs | 66 +++++------------ 4 files changed, 90 insertions(+), 50 deletions(-) create mode 100644 MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs diff --git a/MetaLamp/nft-marketplace/plutus-starter.cabal b/MetaLamp/nft-marketplace/plutus-starter.cabal index b42a81e8d..0b1b8351f 100644 --- a/MetaLamp/nft-marketplace/plutus-starter.cabal +++ b/MetaLamp/nft-marketplace/plutus-starter.cabal @@ -23,7 +23,7 @@ maintainer: Your email library exposed-modules: - Plutus.Contracts.NftMarketplace.OnChain.Core Ext.Plutus.Contracts.Auction Plutus.Contracts.Services.Sale Plutus.Contracts.Services.Sale.Endpoints Plutus.Contracts.Services.Sale.StateMachine Plutus.Contracts.NftMarketplace.OffChain.Info Ext.Plutus.Ledger.Contexts Ext.Plutus.Ledger.Value Plutus.Contracts.NftMarketplace.OffChain.User Plutus.Abstract.ContractResponse Plutus.Abstract.OutputValue Plutus.Abstract.TxUtils Plutus.Contracts.NftMarketplace.Endpoints Plutus.Contracts.NftMarketplace.OffChain.Owner Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine Plutus.PAB.Simulation + Plutus.Contracts.NftMarketplace.OnChain.Core Plutus.Contracts.NftMarketplace.OnChain.Core.NFT Ext.Plutus.Contracts.Auction Plutus.Contracts.Services.Sale Plutus.Contracts.Services.Sale.Endpoints Plutus.Contracts.Services.Sale.StateMachine Plutus.Contracts.NftMarketplace.OffChain.Info Ext.Plutus.Ledger.Contexts Ext.Plutus.Ledger.Value Plutus.Contracts.NftMarketplace.OffChain.User Plutus.Abstract.ContractResponse Plutus.Abstract.OutputValue Plutus.Abstract.TxUtils Plutus.Contracts.NftMarketplace.Endpoints Plutus.Contracts.NftMarketplace.OffChain.Owner Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine Plutus.PAB.Simulation build-depends: base >= 4.9 && < 5, aeson, diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core.hs index 117ba0c2c..cfb6a6c0a 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core.hs @@ -15,6 +15,7 @@ import qualified Ledger.Typed.Scripts as Sc import Ledger.Value import Plutus.Contract import Plutus.Contract.StateMachine +import Plutus.Contracts.NftMarketplace.OnChain.Core.NFT as Export import Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine as Export import qualified PlutusTx import qualified PlutusTx.AssocMap as AssocMap diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs new file mode 100644 index 000000000..d60f6a792 --- /dev/null +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs @@ -0,0 +1,71 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TypeApplications #-} +{-# OPTIONS_GHC -fno-specialise #-} +{-# OPTIONS_GHC -fno-strictness #-} +{-# OPTIONS_GHC -fno-ignore-interface-pragmas #-} +{-# OPTIONS_GHC -fno-omit-interface-pragmas #-} +{-# OPTIONS_GHC -fobject-code #-} + +module Plutus.Contracts.NftMarketplace.OnChain.Core.NFT where + +import Control.Lens ((&), (.~), (?~), (^.)) +import qualified Control.Lens as Lens +import qualified Data.Aeson as J +import qualified Data.Text as T +import qualified Ext.Plutus.Contracts.Auction as Auction +import qualified GHC.Generics as Haskell +import Ledger +import qualified Ledger.Constraints as Constraints +import qualified Ledger.Typed.Scripts as Scripts +import qualified Ledger.Value as V +import Plutus.Contract +import Plutus.Contract.StateMachine +import qualified Plutus.Contracts.Services.Sale as Sale +import qualified PlutusTx +import qualified PlutusTx.AssocMap as AssocMap +import PlutusTx.Prelude hiding (Semigroup (..)) +import Prelude (Semigroup (..)) +import qualified Prelude as Haskell + +type IpfsCid = ByteString +type IpfsCidHash = ByteString +type Sale = (AssetClass, Sale.LovelacePrice, Value) +type Auction = (AssetClass, PubKeyHash, Value, Slot) + +data Lot = Lot + { lotLink :: !(Either Sale Auction) + , lotIpfsCid :: !ByteString + } + deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) + deriving anyclass (J.ToJSON, J.FromJSON) + +PlutusTx.unstableMakeIsData ''Lot + +PlutusTx.makeLift ''Lot + +Lens.makeClassy_ ''Lot + +data NFT = + NFT + { nftId :: !CurrencySymbol + , nftName :: !ByteString + , nftDescription :: !ByteString + , nftIssuer :: !(Maybe PubKeyHash) + , nftLot :: !(Maybe Lot) + } + deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) + deriving anyclass (J.ToJSON, J.FromJSON) + +PlutusTx.unstableMakeIsData ''NFT + +PlutusTx.makeLift ''NFT + +Lens.makeClassy_ ''NFT diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs index 69bdf0c85..0f9a87c16 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs @@ -11,24 +11,27 @@ module Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine where -import Control.Lens ((&), (.~), (?~), (^.)) -import qualified Control.Lens as Lens -import qualified Data.Aeson as J -import qualified Data.Text as T -import qualified Ext.Plutus.Contracts.Auction as Auction -import qualified GHC.Generics as Haskell +import Control.Lens ((&), (.~), + (?~), (^.)) +import qualified Control.Lens as Lens +import qualified Data.Aeson as J +import qualified Data.Text as T +import qualified Ext.Plutus.Contracts.Auction as Auction +import qualified GHC.Generics as Haskell import Ledger -import qualified Ledger.Constraints as Constraints -import qualified Ledger.Typed.Scripts as Scripts -import qualified Ledger.Value as V +import qualified Ledger.Constraints as Constraints +import qualified Ledger.Typed.Scripts as Scripts +import qualified Ledger.Value as V import Plutus.Contract import Plutus.Contract.StateMachine -import qualified Plutus.Contracts.Services.Sale as Sale +import Plutus.Contracts.NftMarketplace.OnChain.Core.NFT +import qualified Plutus.Contracts.Services.Sale as Sale import qualified PlutusTx -import qualified PlutusTx.AssocMap as AssocMap -import PlutusTx.Prelude hiding (Semigroup (..)) -import Prelude (Semigroup (..)) -import qualified Prelude as Haskell +import qualified PlutusTx.AssocMap as AssocMap +import PlutusTx.Prelude hiding + (Semigroup (..)) +import Prelude (Semigroup (..)) +import qualified Prelude as Haskell newtype Marketplace = Marketplace @@ -39,41 +42,6 @@ newtype Marketplace = PlutusTx.makeLift ''Marketplace -type IpfsCid = ByteString -type IpfsCidHash = ByteString -type Sale = (AssetClass, Sale.LovelacePrice, Value) -type Auction = (AssetClass, PubKeyHash, Value, Slot) - -data Lot = Lot - { lotLink :: Either Sale Auction - , lotIpfsCid :: ByteString - } - deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) - deriving anyclass (J.ToJSON, J.FromJSON) - -PlutusTx.unstableMakeIsData ''Lot - -PlutusTx.makeLift ''Lot - -Lens.makeClassy_ ''Lot - -data NFT = - NFT - { nftId :: CurrencySymbol - , nftName :: ByteString - , nftDescription :: ByteString - , nftIssuer :: Maybe PubKeyHash - , nftLot :: Maybe Lot - } - deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) - deriving anyclass (J.ToJSON, J.FromJSON) - -PlutusTx.unstableMakeIsData ''NFT - -PlutusTx.makeLift ''NFT - -Lens.makeClassy_ ''NFT - data MarketplaceRedeemer = CreateNftRedeemer IpfsCidHash NFT | PutLotRedeemer IpfsCidHash Lot From b8a67e2d65e94a443da4f366a7d06544537e4c52 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Sun, 1 Aug 2021 16:23:27 +0700 Subject: [PATCH 029/217] use sale model --- MetaLamp/nft-marketplace/plutus-starter.cabal | 2 +- .../Contracts/NftMarketplace/OffChain/User.hs | 6 +- .../NftMarketplace/OnChain/Core/NFT.hs | 3 +- .../OnChain/Core/StateMachine.hs | 2 +- .../src/Plutus/Contracts/Services/Sale.hs | 1 + .../Plutus/Contracts/Services/Sale/Core.hs | 55 +++++++++++++++++++ .../Contracts/Services/Sale/Endpoints.hs | 1 + .../Contracts/Services/Sale/StateMachine.hs | 50 ++++------------- 8 files changed, 75 insertions(+), 45 deletions(-) create mode 100644 MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/Core.hs diff --git a/MetaLamp/nft-marketplace/plutus-starter.cabal b/MetaLamp/nft-marketplace/plutus-starter.cabal index 0b1b8351f..8e024d9fd 100644 --- a/MetaLamp/nft-marketplace/plutus-starter.cabal +++ b/MetaLamp/nft-marketplace/plutus-starter.cabal @@ -23,7 +23,7 @@ maintainer: Your email library exposed-modules: - Plutus.Contracts.NftMarketplace.OnChain.Core Plutus.Contracts.NftMarketplace.OnChain.Core.NFT Ext.Plutus.Contracts.Auction Plutus.Contracts.Services.Sale Plutus.Contracts.Services.Sale.Endpoints Plutus.Contracts.Services.Sale.StateMachine Plutus.Contracts.NftMarketplace.OffChain.Info Ext.Plutus.Ledger.Contexts Ext.Plutus.Ledger.Value Plutus.Contracts.NftMarketplace.OffChain.User Plutus.Abstract.ContractResponse Plutus.Abstract.OutputValue Plutus.Abstract.TxUtils Plutus.Contracts.NftMarketplace.Endpoints Plutus.Contracts.NftMarketplace.OffChain.Owner Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine Plutus.PAB.Simulation + Plutus.Contracts.NftMarketplace.OnChain.Core Plutus.Contracts.Services.Sale.Core Plutus.Contracts.NftMarketplace.OnChain.Core.NFT Ext.Plutus.Contracts.Auction Plutus.Contracts.Services.Sale Plutus.Contracts.Services.Sale.Endpoints Plutus.Contracts.Services.Sale.StateMachine Plutus.Contracts.NftMarketplace.OffChain.Info Ext.Plutus.Ledger.Contexts Ext.Plutus.Ledger.Value Plutus.Contracts.NftMarketplace.OffChain.User Plutus.Abstract.ContractResponse Plutus.Abstract.OutputValue Plutus.Abstract.TxUtils Plutus.Contracts.NftMarketplace.Endpoints Plutus.Contracts.NftMarketplace.OffChain.Owner Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine Plutus.PAB.Simulation build-depends: base >= 4.9 && < 5, aeson, diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs index 188b4b852..20ecd21f6 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs @@ -116,7 +116,7 @@ openSale marketplace OpenSaleParams {..} = do let client = Core.marketplaceClient marketplace let lot = Core.Lot - { lotLink = Left $ Sale.toTuple sale + { lotLink = Left sale , lotIpfsCid = ospIpfsCid } void $ mapError' $ runStep client $ Core.PutLotRedeemer ipfsCidHash lot @@ -143,7 +143,7 @@ buyNft marketplace BuyNftParams {..} = do nftSale <- maybe (throwError "NFT has not been put on sale") pure $ nftEntry ^. Core._nftLot ^? traverse . Core._lotLink . _Left - _ <- Sale.buyLot $ Sale.fromTuple nftSale + _ <- Sale.buyLot nftSale let client = Core.marketplaceClient marketplace void $ mapError' $ runStep client $ Core.RemoveLotRedeemer ipfsCidHash @@ -170,7 +170,7 @@ closeSale marketplace CloseSaleParams {..} = do nftSale <- maybe (throwError "NFT has not been put on sale") pure $ nftEntry ^. Core._nftLot ^? traverse . Core._lotLink . _Left - _ <- Sale.redeemLot $ Sale.fromTuple nftSale + _ <- Sale.redeemLot nftSale let client = Core.marketplaceClient marketplace void $ mapError' $ runStep client $ Core.RemoveLotRedeemer ipfsCidHash diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs index d60f6a792..3faf2139b 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs @@ -37,11 +37,10 @@ import qualified Prelude as Haskell type IpfsCid = ByteString type IpfsCidHash = ByteString -type Sale = (AssetClass, Sale.LovelacePrice, Value) type Auction = (AssetClass, PubKeyHash, Value, Slot) data Lot = Lot - { lotLink :: !(Either Sale Auction) + { lotLink :: !(Either Sale.Sale Auction) , lotIpfsCid :: !ByteString } deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs index 0f9a87c16..99006a43a 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs @@ -106,7 +106,7 @@ stateTransitionCheck (MarketplaceDatum nftStore) (PutLotRedeemer ipfsCidHash lot traceIfFalse "PutLotRedeemer: " $ let nftEntry = fromMaybe (traceError "NFT has not been created") $ AssocMap.lookup ipfsCidHash nftStore nftIpfsCid = lotIpfsCid lot - lotValue = either (Sale.saleValue . Sale.fromTuple) (Auction.apAsset . Auction.fromTuple) $ lotLink lot + lotValue = either (Sale.saleValue) (Auction.apAsset . Auction.fromTuple) $ lotLink lot nftValue = V.singleton (nftId nftEntry) (V.TokenName nftIpfsCid) 1 hasBeenPutOnSale = lotValue == nftValue isValidHash = sha2_256 nftIpfsCid == ipfsCidHash diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale.hs index 1d26ba469..eb3767d04 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale.hs @@ -1,4 +1,5 @@ module Plutus.Contracts.Services.Sale (module Export) where +import Plutus.Contracts.Services.Sale.Core as Export import Plutus.Contracts.Services.Sale.Endpoints as Export import Plutus.Contracts.Services.Sale.StateMachine as Export diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/Core.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/Core.hs new file mode 100644 index 000000000..f83c19fef --- /dev/null +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/Core.hs @@ -0,0 +1,55 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TypeApplications #-} +{-# OPTIONS_GHC -fno-specialise #-} +{-# OPTIONS_GHC -fno-strictness #-} +{-# OPTIONS_GHC -fno-ignore-interface-pragmas #-} +{-# OPTIONS_GHC -fno-omit-interface-pragmas #-} +{-# OPTIONS_GHC -fobject-code #-} + +module Plutus.Contracts.Services.Sale.Core where + +import qualified Control.Lens as Lens +import qualified Data.Aeson as J +import qualified Data.Text as T +import qualified GHC.Generics as Haskell +import Ledger +import qualified Ledger.Ada as Ada +import qualified Ledger.Constraints as Constraints +import qualified Ledger.Typed.Scripts as Scripts +import Ledger.Value +import Plutus.Contract +import Plutus.Contract.StateMachine +import qualified PlutusTx +import qualified PlutusTx.AssocMap as AssocMap +import PlutusTx.Prelude hiding (Semigroup (..)) +import Prelude (Semigroup (..)) +import qualified Prelude as Haskell +import qualified Schema + +type Saler = PubKeyHash +type Buyer = PubKeyHash +type LovelacePrice = Integer + +data Sale = + Sale + { saleProtocolToken :: !AssetClass, + salePrice :: !LovelacePrice, + saleValue :: !Value + } + deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) + deriving anyclass (J.ToJSON, J.FromJSON, Schema.ToSchema) + +PlutusTx.unstableMakeIsData ''Sale + +PlutusTx.makeLift ''Sale + +Lens.makeClassy_ ''Sale diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/Endpoints.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/Endpoints.hs index 280900350..81ba77e6f 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/Endpoints.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/Endpoints.hs @@ -27,6 +27,7 @@ import Plutus.Abstract.ContractResponse (ContractResponse, import Plutus.Contract import Plutus.Contract.StateMachine import Plutus.Contracts.Currency as Currency +import qualified Plutus.Contracts.Services.Sale.Core as Core import qualified Plutus.Contracts.Services.Sale.StateMachine as Core import qualified PlutusTx import qualified PlutusTx.AssocMap as AssocMap diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/StateMachine.hs index 4550440eb..8154aaae2 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/StateMachine.hs @@ -12,51 +12,25 @@ 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 qualified GHC.Generics as Haskell +import qualified Control.Lens as Lens +import qualified Data.Aeson as J +import qualified Data.Text as T +import qualified GHC.Generics as Haskell import Ledger -import qualified Ledger.Ada as Ada -import qualified Ledger.Constraints as Constraints -import qualified Ledger.Typed.Scripts as Scripts +import qualified Ledger.Ada as Ada +import qualified Ledger.Constraints as Constraints +import qualified Ledger.Typed.Scripts as Scripts import Ledger.Value import Plutus.Contract import Plutus.Contract.StateMachine +import Plutus.Contracts.Services.Sale.Core import qualified PlutusTx -import qualified PlutusTx.AssocMap as AssocMap -import PlutusTx.Prelude hiding (Semigroup (..)) -import Prelude (Semigroup (..)) -import qualified Prelude as Haskell +import qualified PlutusTx.AssocMap as AssocMap +import PlutusTx.Prelude hiding (Semigroup (..)) +import Prelude (Semigroup (..)) +import qualified Prelude as Haskell import qualified Schema -type Saler = PubKeyHash -type Buyer = PubKeyHash -type LovelacePrice = Integer - -data Sale = - Sale - { saleProtocolToken :: AssetClass, - salePrice :: LovelacePrice, - saleValue :: Value - } - deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) - deriving anyclass (J.ToJSON, J.FromJSON, Schema.ToSchema) - -PlutusTx.unstableMakeIsData ''Sale - -PlutusTx.makeLift ''Sale - -Lens.makeClassy_ ''Sale - -{-# INLINABLE toTuple #-} -toTuple :: Sale -> (AssetClass, LovelacePrice, Value) -toTuple Sale{..} = (saleProtocolToken, salePrice, saleValue) - -{-# INLINABLE fromTuple #-} -fromTuple :: (AssetClass, LovelacePrice, Value) -> Sale -fromTuple (saleProtocolToken, salePrice, saleValue) = Sale{..} - data SaleRedeemer = Buy Buyer | Redeem From 2424fe7dfb9892205ad28492196b22e3879b5151 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Sun, 1 Aug 2021 22:29:19 +0700 Subject: [PATCH 030/217] add categories --- .../src/Plutus/Contracts/NftMarketplace/OffChain/User.hs | 2 ++ .../src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs | 3 +++ MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs | 2 ++ 3 files changed, 7 insertions(+) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs index 20ecd21f6..c19677021 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs @@ -54,6 +54,7 @@ data CreateNftParams = cnpIpfsCid :: ByteString, cnpNftName :: ByteString, cnpNftDescription :: ByteString, + cnpNftCategory :: Core.Category, cnpRevealIssuer :: Bool } deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) @@ -81,6 +82,7 @@ createNft marketplace CreateNftParams {..} = do { nftId = Currency.currencySymbol nft , nftName = cnpNftName , nftDescription = cnpNftDescription + , nftCategory = cnpNftCategory , nftIssuer = if cnpRevealIssuer then Just pkh else Nothing , nftLot = Nothing -- TODO validate that it's Nothing } diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs index 3faf2139b..94fb6234d 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs @@ -38,6 +38,7 @@ import qualified Prelude as Haskell type IpfsCid = ByteString type IpfsCidHash = ByteString type Auction = (AssetClass, PubKeyHash, Value, Slot) +type Category = [ByteString] data Lot = Lot { lotLink :: !(Either Sale.Sale Auction) @@ -52,11 +53,13 @@ PlutusTx.makeLift ''Lot Lens.makeClassy_ ''Lot +-- TODO (?) add NFT tags data NFT = NFT { nftId :: !CurrencySymbol , nftName :: !ByteString , nftDescription :: !ByteString + , nftCategory :: !Category , nftIssuer :: !(Maybe PubKeyHash) , nftLot :: !(Maybe Lot) } diff --git a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs index fbec3571c..298ef2cd3 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs @@ -108,6 +108,7 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do cnpIpfsCid = catTokenIpfsCid, cnpNftName = "Cat token", cnpNftDescription = "A picture of a cat on a pogo stick", + cnpNftCategory = ["GIFs"], cnpRevealIssuer = False } flip Simulator.waitForState userCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of @@ -144,6 +145,7 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do cnpIpfsCid = photoTokenIpfsCid, cnpNftName = "Photo token", cnpNftDescription = "A picture of a sunset", + cnpNftCategory = ["Photos"], cnpRevealIssuer = True } flip Simulator.waitForState userCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of From f0d6a981ff2b185cd9068927db4170d3a9efd5d0 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Tue, 3 Aug 2021 11:31:41 +0700 Subject: [PATCH 031/217] remodel domain with bundles --- .../NftMarketplace/OnChain/Core/NFT.hs | 72 ++++++++++++++----- .../OnChain/Core/StateMachine.hs | 10 +-- 2 files changed, 59 insertions(+), 23 deletions(-) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs index 94fb6234d..7d48b15cf 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs @@ -35,39 +35,73 @@ import PlutusTx.Prelude hiding (Semigroup (..)) import Prelude (Semigroup (..)) import qualified Prelude as Haskell +-- TODO (?) add tags type IpfsCid = ByteString type IpfsCidHash = ByteString type Auction = (AssetClass, PubKeyHash, Value, Slot) type Category = [ByteString] +type LotLink = Either Sale.Sale Auction -data Lot = Lot - { lotLink :: !(Either Sale.Sale Auction) - , lotIpfsCid :: !ByteString - } +data NFT = + NFT + { nftInfo :: !NftInfo + , nftLot :: !(Maybe (IpfsCid, LotLink)) + } deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON) -PlutusTx.unstableMakeIsData ''Lot +PlutusTx.unstableMakeIsData ''NFT -PlutusTx.makeLift ''Lot +PlutusTx.makeLift ''NFT -Lens.makeClassy_ ''Lot +Lens.makeClassy_ ''NFT --- TODO (?) add NFT tags -data NFT = - NFT - { nftId :: !CurrencySymbol - , nftName :: !ByteString - , nftDescription :: !ByteString - , nftCategory :: !Category - , nftIssuer :: !(Maybe PubKeyHash) - , nftLot :: !(Maybe Lot) +data NftInfo = + NftInfo + { niId :: !CurrencySymbol + , niName :: !ByteString + , niDescription :: !ByteString + , niCategory :: !Category + , niIssuer :: !(Maybe PubKeyHash) } deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON) -PlutusTx.unstableMakeIsData ''NFT +PlutusTx.unstableMakeIsData ''NftInfo -PlutusTx.makeLift ''NFT +PlutusTx.makeLift ''NftInfo -Lens.makeClassy_ ''NFT +Lens.makeClassy_ ''NftInfo + +data NftBundle = + NftBundle + { nbName :: !ByteString + , nbDescription :: !ByteString + , nbCategory :: !Category + , nbTokens :: !Bundle + } + deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) + deriving anyclass (J.ToJSON, J.FromJSON) + +PlutusTx.unstableMakeIsData ''NftBundle + +PlutusTx.makeLift ''NftBundle + +Lens.makeClassy_ ''NftBundle + +data Bundle + = NoLot !(AssocMap.Map IpfsCidHash NftInfo) + | HasLot !(AssocMap.Map IpfsCidHash (IpfsCid, NftInfo)) !LotLink + deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) + deriving anyclass (J.ToJSON, J.FromJSON) + +PlutusTx.unstableMakeIsData ''Bundle + +PlutusTx.makeLift ''Bundle + +Lens.makeClassyPrisms ''Bundle + +-- ???? +type ValueHash = ByteString +type BundleId = [IpfsCidHash] +-- does Crypto.Hash.hashUpdates depend on order? diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs index 99006a43a..c91984c24 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs @@ -52,11 +52,13 @@ PlutusTx.unstableMakeIsData ''MarketplaceRedeemer PlutusTx.makeLift ''MarketplaceRedeemer -newtype MarketplaceDatum = +data MarketplaceDatum = MarketplaceDatum - { getMarketplaceDatum :: AssocMap.Map IpfsCidHash NFT + { + mdSingletons :: AssocMap.Map IpfsCidHash NFT, + mdBundles :: AssocMap.Map BundleId NftBundle } - deriving (Haskell.Show) + deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) PlutusTx.unstableMakeIsData ''MarketplaceDatum @@ -106,7 +108,7 @@ stateTransitionCheck (MarketplaceDatum nftStore) (PutLotRedeemer ipfsCidHash lot traceIfFalse "PutLotRedeemer: " $ let nftEntry = fromMaybe (traceError "NFT has not been created") $ AssocMap.lookup ipfsCidHash nftStore nftIpfsCid = lotIpfsCid lot - lotValue = either (Sale.saleValue) (Auction.apAsset . Auction.fromTuple) $ lotLink lot + lotValue = either Sale.saleValue (Auction.apAsset . Auction.fromTuple) $ lotLink lot nftValue = V.singleton (nftId nftEntry) (V.TokenName nftIpfsCid) 1 hasBeenPutOnSale = lotValue == nftValue isValidHash = sha2_256 nftIpfsCid == ipfsCidHash From 8fca248d59fe4e85bb1c3cbc15716b474edb4245 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Tue, 3 Aug 2021 16:46:57 +0700 Subject: [PATCH 032/217] refactor nft state machine --- MetaLamp/nft-marketplace/plutus-starter.cabal | 2 +- .../src/Ext/PlutusTx/AssocMap.hs | 49 +++++++++++++ .../NftMarketplace/OnChain/Core/NFT.hs | 48 ++++++------- .../OnChain/Core/StateMachine.hs | 68 ++++++++++++------- 4 files changed, 117 insertions(+), 50 deletions(-) create mode 100644 MetaLamp/nft-marketplace/src/Ext/PlutusTx/AssocMap.hs diff --git a/MetaLamp/nft-marketplace/plutus-starter.cabal b/MetaLamp/nft-marketplace/plutus-starter.cabal index 8e024d9fd..801870979 100644 --- a/MetaLamp/nft-marketplace/plutus-starter.cabal +++ b/MetaLamp/nft-marketplace/plutus-starter.cabal @@ -23,7 +23,7 @@ maintainer: Your email library exposed-modules: - Plutus.Contracts.NftMarketplace.OnChain.Core Plutus.Contracts.Services.Sale.Core Plutus.Contracts.NftMarketplace.OnChain.Core.NFT Ext.Plutus.Contracts.Auction Plutus.Contracts.Services.Sale Plutus.Contracts.Services.Sale.Endpoints Plutus.Contracts.Services.Sale.StateMachine Plutus.Contracts.NftMarketplace.OffChain.Info Ext.Plutus.Ledger.Contexts Ext.Plutus.Ledger.Value Plutus.Contracts.NftMarketplace.OffChain.User Plutus.Abstract.ContractResponse Plutus.Abstract.OutputValue Plutus.Abstract.TxUtils Plutus.Contracts.NftMarketplace.Endpoints Plutus.Contracts.NftMarketplace.OffChain.Owner Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine Plutus.PAB.Simulation + Plutus.Contracts.NftMarketplace.OnChain.Core Plutus.Contracts.Services.Sale.Core Ext.PlutusTx.AssocMap Plutus.Contracts.NftMarketplace.OnChain.Core.NFT Ext.Plutus.Contracts.Auction Plutus.Contracts.Services.Sale Plutus.Contracts.Services.Sale.Endpoints Plutus.Contracts.Services.Sale.StateMachine Plutus.Contracts.NftMarketplace.OffChain.Info Ext.Plutus.Ledger.Contexts Ext.Plutus.Ledger.Value Plutus.Contracts.NftMarketplace.OffChain.User Plutus.Abstract.ContractResponse Plutus.Abstract.OutputValue Plutus.Abstract.TxUtils Plutus.Contracts.NftMarketplace.Endpoints Plutus.Contracts.NftMarketplace.OffChain.Owner Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine Plutus.PAB.Simulation build-depends: base >= 4.9 && < 5, aeson, diff --git a/MetaLamp/nft-marketplace/src/Ext/PlutusTx/AssocMap.hs b/MetaLamp/nft-marketplace/src/Ext/PlutusTx/AssocMap.hs new file mode 100644 index 000000000..7831f03a2 --- /dev/null +++ b/MetaLamp/nft-marketplace/src/Ext/PlutusTx/AssocMap.hs @@ -0,0 +1,49 @@ +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE MonoLocalBinds #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE UndecidableInstances #-} +{-# OPTIONS_GHC -Wno-name-shadowing #-} +{-# OPTIONS_GHC -fno-strictness #-} +{-# OPTIONS_GHC -fno-specialise #-} +{-# OPTIONS_GHC -fno-omit-interface-pragmas #-} + +module Ext.PlutusTx.AssocMap where + +import GHC.Generics (Generic) +import PlutusTx.AssocMap +import PlutusTx.IsData +import PlutusTx.Lift (makeLift) +import PlutusTx.Prelude hiding (all, null, toList) +import qualified PlutusTx.Prelude as P +import PlutusTx.These +import qualified Prelude as Haskell + +{-# INLINABLE unionWith #-} +-- | Combine two 'Map's with the given combination function. +unionWith :: + forall k a. (Eq k) + => (a -> a -> a) + -> Map k a + -> Map k a + -> Map k a +unionWith merge mls mrs = + let ls = toList mls + rs = toList mrs + f :: a -> Maybe a -> a + f a b' = + case b' of + Nothing -> a + Just b -> merge a b + ls' :: [(k, a)] + ls' = P.fmap (\(c, i) -> (c, f i (lookup c (fromList rs)))) ls + rs' :: [(k, a)] + rs' = filter (\(c, _) -> not (any (\(c', _) -> c' == c) ls)) rs + in fromList (ls' ++ rs') diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs index 7d48b15cf..48eaf3add 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs @@ -42,23 +42,9 @@ type Auction = (AssetClass, PubKeyHash, Value, Slot) type Category = [ByteString] type LotLink = Either Sale.Sale Auction -data NFT = - NFT - { nftInfo :: !NftInfo - , nftLot :: !(Maybe (IpfsCid, LotLink)) - } - deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) - deriving anyclass (J.ToJSON, J.FromJSON) - -PlutusTx.unstableMakeIsData ''NFT - -PlutusTx.makeLift ''NFT - -Lens.makeClassy_ ''NFT - data NftInfo = NftInfo - { niId :: !CurrencySymbol + { niCurrency :: !CurrencySymbol , niName :: !ByteString , niDescription :: !ByteString , niCategory :: !Category @@ -73,21 +59,19 @@ PlutusTx.makeLift ''NftInfo Lens.makeClassy_ ''NftInfo -data NftBundle = - NftBundle - { nbName :: !ByteString - , nbDescription :: !ByteString - , nbCategory :: !Category - , nbTokens :: !Bundle +data NFT = + NFT + { nftRecord :: !NftInfo + , nftLot :: !(Maybe (IpfsCid, LotLink)) } deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON) -PlutusTx.unstableMakeIsData ''NftBundle +PlutusTx.unstableMakeIsData ''NFT -PlutusTx.makeLift ''NftBundle +PlutusTx.makeLift ''NFT -Lens.makeClassy_ ''NftBundle +Lens.makeClassy_ ''NFT data Bundle = NoLot !(AssocMap.Map IpfsCidHash NftInfo) @@ -101,6 +85,22 @@ PlutusTx.makeLift ''Bundle Lens.makeClassyPrisms ''Bundle +data NftBundle = + NftBundle + { nbName :: !ByteString + , nbDescription :: !ByteString + , nbCategory :: !Category + , nbTokens :: !Bundle + } + deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) + deriving anyclass (J.ToJSON, J.FromJSON) + +PlutusTx.unstableMakeIsData ''NftBundle + +PlutusTx.makeLift ''NftBundle + +Lens.makeClassy_ ''NftBundle + -- ???? type ValueHash = ByteString type BundleId = [IpfsCidHash] diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs index c91984c24..5be80ed00 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs @@ -5,6 +5,7 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeApplications #-} @@ -17,6 +18,7 @@ import qualified Control.Lens as Lens import qualified Data.Aeson as J import qualified Data.Text as T import qualified Ext.Plutus.Contracts.Auction as Auction +import qualified Ext.PlutusTx.AssocMap as AssocMap import qualified GHC.Generics as Haskell import Ledger import qualified Ledger.Constraints as Constraints @@ -42,10 +44,11 @@ newtype Marketplace = PlutusTx.makeLift ''Marketplace +-- TODO make sum types for eithers (?) data MarketplaceRedeemer - = CreateNftRedeemer IpfsCidHash NFT - | PutLotRedeemer IpfsCidHash Lot - | RemoveLotRedeemer IpfsCidHash + = CreateNftRedeemer IpfsCidHash NftInfo + | PutLotRedeemer (Either (IpfsCidHash, IpfsCid) (AssocMap.Map IpfsCidHash IpfsCid)) LotLink + | RemoveLotRedeemer (Either IpfsCidHash BundleId) deriving (Haskell.Show) PlutusTx.unstableMakeIsData ''MarketplaceRedeemer @@ -64,62 +67,77 @@ PlutusTx.unstableMakeIsData ''MarketplaceDatum PlutusTx.makeLift ''MarketplaceDatum +{-# INLINABLE insertNft #-} +insertNft :: IpfsCidHash + -> NFT -> MarketplaceDatum -> MarketplaceDatum +insertNft ipfsCidHash nftEntry store@MarketplaceDatum{..} = + store { mdSingletons = AssocMap.insert ipfsCidHash nftEntry mdSingletons } + +{-# INLINABLE nftUnion #-} +nftUnion :: MarketplaceDatum -> AssocMap.Map IpfsCidHash NFT +nftUnion MarketplaceDatum{..} = foldr union mdSingletons $ fmap getNfts $ toList mdBundles + where + union = AssocMap.unionWith const + getNfts NftBundle{..} = case nbTokens of + NoLot val -> fmap (\info -> NFT info Nothing) val + HasLot val lot -> fmap (\(cid, info) -> NFT info (Just (cid, lot))) val + {-# INLINABLE transition #-} transition :: Marketplace -> State MarketplaceDatum -> MarketplaceRedeemer -> Maybe (TxConstraints Void Void, State MarketplaceDatum) transition marketplace state redeemer = case redeemer of CreateNftRedeemer ipfsCidHash nftEntry -- TODO check that ipfsCidHash is a hash (?) -> Just ( mustBeSignedByIssuer nftEntry - , State (MarketplaceDatum $ AssocMap.insert ipfsCidHash nftEntry nftStore) currStateValue + , State (insertNft ipfsCidHash (NFT nftEntry Nothing) nftStore) currStateValue ) - PutLotRedeemer ipfsCidHash lot - -> let newEntry = maybe (traceError "NFT has not been created.") (_nftLot ?~ lot) $ - AssocMap.lookup ipfsCidHash nftStore + PutLotRedeemer (Left (ipfsCidHash, ipfsCid)) lot + -> let newEntry = maybe (traceError "NFT has not been created.") (_nftLot ?~ (ipfsCid, lot)) $ + AssocMap.lookup ipfsCidHash $ mdSingletons nftStore in Just ( mempty - , State (MarketplaceDatum $ AssocMap.insert ipfsCidHash newEntry nftStore) currStateValue + , State (insertNft ipfsCidHash newEntry nftStore) currStateValue ) - RemoveLotRedeemer ipfsCidHash + RemoveLotRedeemer (Left ipfsCidHash) -> let newEntry = maybe (traceError "NFT has not been created.") (_nftLot .~ Nothing) $ - AssocMap.lookup ipfsCidHash nftStore + AssocMap.lookup ipfsCidHash $ mdSingletons nftStore in Just ( mempty - , State (MarketplaceDatum $ AssocMap.insert ipfsCidHash newEntry nftStore) currStateValue + , State (insertNft ipfsCidHash newEntry nftStore) currStateValue ) - _ -> Nothing + _ -> trace "Invalid transition" Nothing where stateToken :: Value stateToken = V.assetClassValue (marketplaceProtocolToken marketplace) 1 - nftStore :: AssocMap.Map IpfsCidHash NFT - nftStore = getMarketplaceDatum $ stateData state + nftStore :: MarketplaceDatum + nftStore = stateData state currStateValue = stateValue state - stateToken - mustBeSignedByIssuer entry = case nftIssuer entry of + mustBeSignedByIssuer entry = case niIssuer entry of Just pkh -> Constraints.mustBeSignedBy pkh Nothing -> mempty {-# INLINABLE stateTransitionCheck #-} stateTransitionCheck :: MarketplaceDatum -> MarketplaceRedeemer -> ScriptContext -> Bool -stateTransitionCheck (MarketplaceDatum nftStore) (CreateNftRedeemer ipfsCidHash nftEntry) ctx = +stateTransitionCheck nftStore (CreateNftRedeemer ipfsCidHash nftEntry) ctx = traceIfFalse "CreateNftRedeemer: " $ traceIfFalse "NFT entry already exists" $ - isNothing $ AssocMap.lookup ipfsCidHash nftStore -stateTransitionCheck (MarketplaceDatum nftStore) (PutLotRedeemer ipfsCidHash lot) ctx = + isNothing $ AssocMap.lookup ipfsCidHash $ nftUnion nftStore +stateTransitionCheck MarketplaceDatum {..} (PutLotRedeemer (Left (ipfsCidHash, ipfsCid)) lot) ctx = traceIfFalse "PutLotRedeemer: " $ - let nftEntry = fromMaybe (traceError "NFT has not been created") $ AssocMap.lookup ipfsCidHash nftStore - nftIpfsCid = lotIpfsCid lot - lotValue = either Sale.saleValue (Auction.apAsset . Auction.fromTuple) $ lotLink lot - nftValue = V.singleton (nftId nftEntry) (V.TokenName nftIpfsCid) 1 + let nftEntry = fromMaybe (traceError "NFT has not been created") $ AssocMap.lookup ipfsCidHash mdSingletons + lotValue = either Sale.saleValue (Auction.apAsset . Auction.fromTuple) lot + nftValue = V.singleton (niCurrency $ nftRecord nftEntry) (V.TokenName ipfsCid) 1 hasBeenPutOnSale = lotValue == nftValue - isValidHash = sha2_256 nftIpfsCid == ipfsCidHash + isValidHash = sha2_256 ipfsCid == ipfsCidHash -- TODO (?) check that there was no previous lot (isNothing $ nftLot nftEntry) in traceIfFalse "NFT has not been put on sale or auction" hasBeenPutOnSale && traceIfFalse "Invalid IPFS Cid Hash" isValidHash -stateTransitionCheck (MarketplaceDatum nftStore) (RemoveLotRedeemer ipfsCidHash) ctx = +stateTransitionCheck MarketplaceDatum {..} (RemoveLotRedeemer (Left ipfsCidHash)) ctx = traceIfFalse "RemoveLotRedeemer: " $ - let nftEntry = fromMaybe (traceError "NFT has not been created") $ AssocMap.lookup ipfsCidHash nftStore + let nftEntry = fromMaybe (traceError "NFT has not been created") $ AssocMap.lookup ipfsCidHash mdSingletons hasBeenPutOnSale = isJust $ nftLot nftEntry in traceIfFalse "NFT has not been put on sale or auction" hasBeenPutOnSale +stateTransitionCheck _ _ _ = traceError "Transition disallowed" {-# INLINABLE marketplaceStateMachine #-} marketplaceStateMachine :: Marketplace -> StateMachine MarketplaceDatum MarketplaceRedeemer From 63bdb6db3db4f671aa2d6da0c47529aeb00794d9 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Fri, 6 Aug 2021 18:58:30 +0700 Subject: [PATCH 033/217] fix singletons endpoints call --- .../Contracts/NftMarketplace/OffChain/Info.hs | 17 ++--- .../NftMarketplace/OffChain/Owner.hs | 2 +- .../Contracts/NftMarketplace/OffChain/User.hs | 68 +++++++++---------- .../OnChain/Core/StateMachine.hs | 1 + 4 files changed, 42 insertions(+), 46 deletions(-) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs index 3c9c9695b..5f75c8e9d 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs @@ -13,8 +13,9 @@ module Plutus.Contracts.NftMarketplace.OffChain.Info where -import Control.Lens (_Left, _Right, - (^.), (^?)) +import Control.Lens (_2, _Left, + _Right, (^.), + (^?)) import qualified Control.Lens as Lens import Control.Monad hiding (fmap) import qualified Data.Aeson as J @@ -43,14 +44,14 @@ import qualified Prelude as Haskell import Text.Printf (printf) -- | Gets current Marketplace store state -marketplaceStore :: Core.Marketplace -> Contract w s Text (AssocMap.Map Core.IpfsCidHash Core.NFT) +marketplaceStore :: Core.Marketplace -> Contract w s Text Core.MarketplaceDatum marketplaceStore marketplace = do let client = Core.marketplaceClient marketplace mapError' (getOnChainState client) >>= getStateDatum getStateDatum :: - Maybe (OnChainState Core.MarketplaceDatum i, UtxoMap) -> Contract w s Text (AssocMap.Map Core.IpfsCidHash Core.NFT) -getStateDatum = maybe (throwError "Marketplace output not found") (pure . Core.getMarketplaceDatum . tyTxOutData . fst . fst) + Maybe (OnChainState Core.MarketplaceDatum i, UtxoMap) -> Contract w s Text Core.MarketplaceDatum +getStateDatum = maybe (throwError "Marketplace output not found") (pure . tyTxOutData . fst . fst) -- | Gets all UTxOs belonging to a user and concats them into one Value fundsAt :: PubKeyHash -> Contract w s Text Value @@ -64,10 +65,10 @@ marketplaceFunds marketplace = utxoValue <$> utxoAt (Core.marketplaceAddress ma getAuctionState :: Core.Marketplace -> Core.IpfsCid -> Contract w s Text Auction.AuctionState getAuctionState marketplace ipfsCid = do let ipfsCidHash = sha2_256 ipfsCid - nftStore <- marketplaceStore marketplace + nftStore <- Core.mdSingletons <$> marketplaceStore marketplace nftEntry <- maybe (throwError "NFT has not been created") pure $ AssocMap.lookup ipfsCidHash nftStore nftAuction <- maybe (throwError "NFT has not been put on auction") pure $ - nftEntry ^. Core._nftLot ^? traverse . Core._lotLink . _Right + nftEntry ^. Core._nftLot ^? traverse . _2 . _Right let auctionToken = Auction.getStateToken nftAuction let auctionParams = Auction.fromTuple nftAuction @@ -90,7 +91,7 @@ type MarketplaceInfoSchema = data InfoContractState = FundsAt Value | MarketplaceFunds Value - | MarketplaceStore (AssocMap.Map Core.IpfsCidHash Core.NFT) + | MarketplaceStore Core.MarketplaceDatum | AuctionState Auction.AuctionState deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Owner.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Owner.hs index 0f34459a6..b163ba0c4 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Owner.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Owner.hs @@ -45,7 +45,7 @@ start' getMarketplaceToken = do pkh <- pubKeyHash <$> ownPubKey let marketplace = Core.marketplace marketplaceToken let client = Core.marketplaceClient marketplace - void $ mapError (T.pack . Haskell.show @SMContractError) $ runInitialise client (Core.MarketplaceDatum AssocMap.empty) mempty + void $ mapError (T.pack . Haskell.show @SMContractError) $ runInitialise client (Core.MarketplaceDatum AssocMap.empty AssocMap.empty) mempty logInfo @Haskell.String $ printf "started Marketplace %s at address %s" (Haskell.show marketplace) (Haskell.show $ Core.marketplaceAddress marketplace) pure marketplace diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs index c19677021..ada5320e3 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs @@ -13,8 +13,9 @@ module Plutus.Contracts.NftMarketplace.OffChain.User where -import Control.Lens (_Left, _Right, - (^.), (^?)) +import Control.Lens (_2, _Left, + _Right, (^.), + (^?)) import qualified Control.Lens as Lens import Control.Monad hiding (fmap) import qualified Data.Aeson as J @@ -68,7 +69,7 @@ PlutusTx.makeLift ''CreateNftParams createNft :: Core.Marketplace -> CreateNftParams -> Contract w s Text () createNft marketplace CreateNftParams {..} = do let ipfsCidHash = sha2_256 cnpIpfsCid - nftStore <- marketplaceStore marketplace + nftStore <- Core.mdSingletons <$> marketplaceStore marketplace when (isJust $ AssocMap.lookup ipfsCidHash nftStore) $ throwError "Nft entry already exists" pkh <- getOwnPubKey @@ -78,13 +79,12 @@ createNft marketplace CreateNftParams {..} = do Currency.forgeContract pkh [(tokenName, 1)] let client = Core.marketplaceClient marketplace - let nftEntry = Core.NFT - { nftId = Currency.currencySymbol nft - , nftName = cnpNftName - , nftDescription = cnpNftDescription - , nftCategory = cnpNftCategory - , nftIssuer = if cnpRevealIssuer then Just pkh else Nothing - , nftLot = Nothing -- TODO validate that it's Nothing + let nftEntry = Core.NftInfo + { niCurrency = Currency.currencySymbol nft + , niName = cnpNftName + , niDescription = cnpNftDescription + , niCategory = cnpNftCategory + , niIssuer = if cnpRevealIssuer then Just pkh else Nothing } void $ mapError' $ runStep client $ Core.CreateNftRedeemer ipfsCidHash nftEntry @@ -106,24 +106,21 @@ PlutusTx.makeLift ''OpenSaleParams openSale :: Core.Marketplace -> OpenSaleParams -> Contract w s Text () openSale marketplace OpenSaleParams {..} = do let ipfsCidHash = sha2_256 ospIpfsCid - nftStore <- marketplaceStore marketplace + nftStore <- Core.mdSingletons <$> marketplaceStore marketplace nftEntry <- maybe (throwError "NFT has not been created") pure $ AssocMap.lookup ipfsCidHash nftStore let tokenName = V.TokenName ospIpfsCid sale <- Sale.openSale Sale.OpenSaleParams { ospSalePrice = ospSalePrice, - ospSaleValue = V.singleton (Core.nftId nftEntry) tokenName 1 + ospSaleValue = V.singleton (nftEntry ^. Core._nftRecord . Core._niCurrency) tokenName 1 } let client = Core.marketplaceClient marketplace - let lot = Core.Lot - { lotLink = Left sale - , lotIpfsCid = ospIpfsCid - } - void $ mapError' $ runStep client $ Core.PutLotRedeemer ipfsCidHash lot + let lot = Left sale + void $ mapError' $ runStep client $ Core.PutLotRedeemer (Left (ipfsCidHash, ospIpfsCid)) lot - logInfo @Haskell.String $ printf "Created NFT sale %s" (Haskell.show lot) + logInfo @Haskell.String $ printf "Created NFT sale %s" (Haskell.show sale) pure () data BuyNftParams = @@ -140,15 +137,15 @@ PlutusTx.makeLift ''BuyNftParams buyNft :: Core.Marketplace -> BuyNftParams -> Contract w s Text () buyNft marketplace BuyNftParams {..} = do let ipfsCidHash = sha2_256 bnpIpfsCid - nftStore <- marketplaceStore marketplace + nftStore <- Core.mdSingletons <$> marketplaceStore marketplace nftEntry <- maybe (throwError "NFT has not been created") pure $ AssocMap.lookup ipfsCidHash nftStore nftSale <- maybe (throwError "NFT has not been put on sale") pure $ - nftEntry ^. Core._nftLot ^? traverse . Core._lotLink . _Left + nftEntry ^. Core._nftLot ^? traverse . _2 . _Left _ <- Sale.buyLot nftSale let client = Core.marketplaceClient marketplace - void $ mapError' $ runStep client $ Core.RemoveLotRedeemer ipfsCidHash + void $ mapError' $ runStep client $ Core.RemoveLotRedeemer (Left ipfsCidHash) logInfo @Haskell.String $ printf "Bought NFT from sale %s" (Haskell.show nftSale) pure () @@ -167,15 +164,15 @@ PlutusTx.makeLift ''CloseSaleParams closeSale :: Core.Marketplace -> CloseSaleParams -> Contract w s Text () closeSale marketplace CloseSaleParams {..} = do let ipfsCidHash = sha2_256 cspIpfsCid - nftStore <- marketplaceStore marketplace + nftStore <- Core.mdSingletons <$> marketplaceStore marketplace nftEntry <- maybe (throwError "NFT has not been created") pure $ AssocMap.lookup ipfsCidHash nftStore nftSale <- maybe (throwError "NFT has not been put on sale") pure $ - nftEntry ^. Core._nftLot ^? traverse . Core._lotLink . _Left + nftEntry ^. Core._nftLot ^? traverse . _2 . _Left _ <- Sale.redeemLot nftSale let client = Core.marketplaceClient marketplace - void $ mapError' $ runStep client $ Core.RemoveLotRedeemer ipfsCidHash + void $ mapError' $ runStep client $ Core.RemoveLotRedeemer (Left ipfsCidHash) logInfo @Haskell.String $ printf "Closed NFT sale %s" (Haskell.show nftSale) pure () @@ -195,40 +192,37 @@ PlutusTx.makeLift ''HoldAnAuctionParams startAnAuction :: Core.Marketplace -> HoldAnAuctionParams -> Contract w s Text () startAnAuction marketplace HoldAnAuctionParams {..} = do let ipfsCidHash = sha2_256 haapIpfsCid - nftStore <- marketplaceStore marketplace + nftStore <- Core.mdSingletons <$> marketplaceStore marketplace nftEntry <- maybe (throwError "NFT has not been created") pure $ AssocMap.lookup ipfsCidHash nftStore let tokenName = V.TokenName haapIpfsCid - let nftValue = V.singleton (Core.nftId nftEntry) tokenName 1 + let nftValue = V.singleton (nftEntry ^. Core._nftRecord . Core._niCurrency) tokenName 1 currSlot <- currentSlot let endTime = currSlot + haapDuration (auctionToken, auctionParams) <- mapError (T.pack . Haskell.show) $ Auction.startAuction nftValue endTime let client = Core.marketplaceClient marketplace - let lot = Core.Lot - { lotLink = Right $ Auction.toTuple auctionToken auctionParams - , lotIpfsCid = haapIpfsCid - } - void $ mapError' $ runStep client $ Core.PutLotRedeemer ipfsCidHash lot + let lot = Right $ Auction.toTuple auctionToken auctionParams + void $ mapError' $ runStep client $ Core.PutLotRedeemer (Left (ipfsCidHash, haapIpfsCid)) lot - logInfo @Haskell.String $ printf "Started an auction for NFT lot %s" (Haskell.show lot) + logInfo @Haskell.String $ printf "Started an auction %s" (Haskell.show auctionParams) pure () -- | The user completes the auction for specified NFT completeAnAuction :: Core.Marketplace -> HoldAnAuctionParams -> Contract w s Text () completeAnAuction marketplace HoldAnAuctionParams {..} = do let ipfsCidHash = sha2_256 haapIpfsCid - nftStore <- marketplaceStore marketplace + nftStore <- Core.mdSingletons <$> marketplaceStore marketplace nftEntry <- maybe (throwError "NFT has not been created") pure $ AssocMap.lookup ipfsCidHash nftStore nftAuction <- maybe (throwError "NFT has not been put on auction") pure $ - nftEntry ^. Core._nftLot ^? traverse . Core._lotLink . _Right + nftEntry ^. Core._nftLot ^? traverse . _2 . _Right let auctionToken = Auction.getStateToken nftAuction let auctionParams = Auction.fromTuple nftAuction _ <- mapError (T.pack . Haskell.show) $ Auction.payoutAuction auctionToken auctionParams let client = Core.marketplaceClient marketplace - void $ mapError' $ runStep client $ Core.RemoveLotRedeemer ipfsCidHash + void $ mapError' $ runStep client $ Core.RemoveLotRedeemer (Left ipfsCidHash) logInfo @Haskell.String $ printf "Completed an auction %s" (Haskell.show nftAuction) pure () @@ -248,10 +242,10 @@ PlutusTx.makeLift ''BidOnAuctionParams bidOnAuction :: Core.Marketplace -> BidOnAuctionParams -> Contract w s Text () bidOnAuction marketplace BidOnAuctionParams {..} = do let ipfsCidHash = sha2_256 japIpfsCid - nftStore <- marketplaceStore marketplace + nftStore <- Core.mdSingletons <$> marketplaceStore marketplace nftEntry <- maybe (throwError "NFT has not been created") pure $ AssocMap.lookup ipfsCidHash nftStore nftAuction <- maybe (throwError "NFT has not been put on auction") pure $ - nftEntry ^. Core._nftLot ^? traverse . Core._lotLink . _Right + nftEntry ^. Core._nftLot ^? traverse . _2 . _Right let auctionToken = Auction.getStateToken nftAuction let auctionParams = Auction.fromTuple nftAuction diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs index 5be80ed00..869a29215 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs @@ -62,6 +62,7 @@ data MarketplaceDatum = mdBundles :: AssocMap.Map BundleId NftBundle } deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) + deriving anyclass (J.ToJSON, J.FromJSON) PlutusTx.unstableMakeIsData ''MarketplaceDatum From b8f512be715c43998b1535721de3a27126d2a334 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Fri, 6 Aug 2021 20:30:36 +0700 Subject: [PATCH 034/217] calculate bundle id --- MetaLamp/nft-marketplace/plutus-starter.cabal | 2 ++ .../Contracts/NftMarketplace/OnChain/Core/NFT.hs | 14 ++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/MetaLamp/nft-marketplace/plutus-starter.cabal b/MetaLamp/nft-marketplace/plutus-starter.cabal index 801870979..77f7aef2d 100644 --- a/MetaLamp/nft-marketplace/plutus-starter.cabal +++ b/MetaLamp/nft-marketplace/plutus-starter.cabal @@ -35,6 +35,8 @@ library prettyprinter, lens, semigroups, + cryptonite, + memory, -- Plutus: playground-common, plutus-contract, diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs index 48eaf3add..ee75d8c32 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs @@ -18,7 +18,10 @@ module Plutus.Contracts.NftMarketplace.OnChain.Core.NFT where import Control.Lens ((&), (.~), (?~), (^.)) import qualified Control.Lens as Lens +import qualified Crypto.Hash as Hash import qualified Data.Aeson as J +import qualified Data.ByteArray as BA +import qualified Data.List as HL import qualified Data.Text as T import qualified Ext.Plutus.Contracts.Auction as Auction import qualified GHC.Generics as Haskell @@ -41,6 +44,7 @@ type IpfsCidHash = ByteString type Auction = (AssetClass, PubKeyHash, Value, Slot) type Category = [ByteString] type LotLink = Either Sale.Sale Auction +type BundleId = ByteString data NftInfo = NftInfo @@ -101,7 +105,9 @@ PlutusTx.makeLift ''NftBundle Lens.makeClassy_ ''NftBundle --- ???? -type ValueHash = ByteString -type BundleId = [IpfsCidHash] --- does Crypto.Hash.hashUpdates depend on order? +-- Calculates a hash of a list of ByteStrings, +-- the result does not depend on the order of ByteStrings inside a list +calcBundleIdHash :: [IpfsCid] -> BundleId +calcBundleIdHash = BA.convert . Hash.hashUpdates alg . HL.sort + where + alg = Hash.hashInit @Hash.SHA256 From bf0ddf5209a96041c4f9d0e5224310fe4614c15a Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Mon, 9 Aug 2021 15:03:41 +0700 Subject: [PATCH 035/217] add bundle state changes --- .../Contracts/NftMarketplace/OffChain/User.hs | 26 ++++++++++++ .../NftMarketplace/OnChain/Core/NFT.hs | 42 +++++++++++++++++-- .../OnChain/Core/StateMachine.hs | 31 ++++++++++++++ 3 files changed, 95 insertions(+), 4 deletions(-) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs index ada5320e3..e517c403f 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs @@ -254,6 +254,26 @@ bidOnAuction marketplace BidOnAuctionParams {..} = do logInfo @Haskell.String $ printf "Submitted bid for NFT auction %s" (Haskell.show nftAuction) pure () +data BundleParams = + BundleParams { + bpIpfsCids :: [ByteString] + } + deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) + deriving anyclass (J.ToJSON, J.FromJSON, Schema.ToSchema) + +PlutusTx.unstableMakeIsData ''BundleParams +PlutusTx.makeLift ''BundleParams + +-- | The user +bundleUp :: Core.Marketplace -> BundleParams -> Contract w s Text () +bundleUp marketplace BundleParams {..} = do + pure () + +-- | The user +unbundle :: Core.Marketplace -> BundleParams -> Contract w s Text () +unbundle marketplace BundleParams {..} = do + pure () + balanceAt :: PubKeyHash -> AssetClass -> Contract w s Text Integer balanceAt pkh asset = flip V.assetClassValueOf asset <$> fundsAt pkh @@ -268,6 +288,8 @@ type MarketplaceUserSchema = .\/ Endpoint "startAnAuction" HoldAnAuctionParams .\/ Endpoint "completeAnAuction" HoldAnAuctionParams .\/ Endpoint "bidOnAuction" BidOnAuctionParams + .\/ Endpoint "bundleUp" BundleParams + .\/ Endpoint "unbundle" BundleParams .\/ Endpoint "ownPubKey" () .\/ Endpoint "ownPubKeyBalance" () @@ -279,6 +301,8 @@ data UserContractState = | AuctionStarted | AuctionComplete | BidSubmitted + | Bundled + | Unbundled | GetPubKey PubKeyHash | GetPubKeyBalance Value deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) @@ -295,5 +319,7 @@ userEndpoints marketplace = forever $ `select` withContractResponse (Proxy @"startAnAuction") (const AuctionStarted) (startAnAuction marketplace) `select` withContractResponse (Proxy @"completeAnAuction") (const AuctionComplete) (completeAnAuction marketplace) `select` withContractResponse (Proxy @"bidOnAuction") (const BidSubmitted) (bidOnAuction marketplace) + `select` withContractResponse (Proxy @"bundleUp") (const Bundled) (bundleUp marketplace) + `select` withContractResponse (Proxy @"unbundle") (const Unbundled) (unbundle marketplace) `select` withContractResponse (Proxy @"ownPubKey") GetPubKey (const getOwnPubKey) `select` withContractResponse (Proxy @"ownPubKeyBalance") GetPubKeyBalance (const ownPubKeyBalance) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs index ee75d8c32..db1dda7f9 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs @@ -89,12 +89,25 @@ PlutusTx.makeLift ''Bundle Lens.makeClassyPrisms ''Bundle +data BundleInfo = + BundleInfo + { biName :: !ByteString + , biDescription :: !ByteString + , biCategory :: !Category + } + deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) + deriving anyclass (J.ToJSON, J.FromJSON) + +PlutusTx.unstableMakeIsData ''BundleInfo + +PlutusTx.makeLift ''BundleInfo + +Lens.makeClassy_ ''BundleInfo + data NftBundle = NftBundle - { nbName :: !ByteString - , nbDescription :: !ByteString - , nbCategory :: !Category - , nbTokens :: !Bundle + { nbRecord :: !BundleInfo + , nbTokens :: !Bundle } deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON) @@ -111,3 +124,24 @@ calcBundleIdHash :: [IpfsCid] -> BundleId calcBundleIdHash = BA.convert . Hash.hashUpdates alg . HL.sort where alg = Hash.hashInit @Hash.SHA256 + +{-# INLINABLE makeBundle #-} +makeBundle :: AssocMap.Map IpfsCidHash NFT -> [IpfsCidHash] -> BundleInfo -> NftBundle +makeBundle singletons nftIds bundleInfo = + NftBundle + { nbRecord = bundleInfo + , nbTokens = NoLot $ foldr insert AssocMap.empty nftIds + } + where + insert nftId = let entry = nftRecord $ fromMaybe (traceError "NFT singleton entry not found") $ AssocMap.lookup nftId singletons + in AssocMap.insert nftId entry + +{-# INLINABLE hasLotNft #-} +hasLotNft :: NFT -> Bool +hasLotNft = isJust . nftLot + +{-# INLINABLE hasLotBundle #-} +hasLotBundle :: NftBundle -> Bool +hasLotBundle bundle = case nbTokens bundle of + HasLot _ _ -> True + _ -> False diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs index 869a29215..38b1ace21 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs @@ -49,6 +49,8 @@ data MarketplaceRedeemer = CreateNftRedeemer IpfsCidHash NftInfo | PutLotRedeemer (Either (IpfsCidHash, IpfsCid) (AssocMap.Map IpfsCidHash IpfsCid)) LotLink | RemoveLotRedeemer (Either IpfsCidHash BundleId) + | BundleUpRedeemer [IpfsCidHash] BundleId BundleInfo + | UnbundleRedeemer BundleId deriving (Haskell.Show) PlutusTx.unstableMakeIsData ''MarketplaceRedeemer @@ -83,6 +85,27 @@ nftUnion MarketplaceDatum{..} = foldr union mdSingletons $ fmap getNfts $ toList NoLot val -> fmap (\info -> NFT info Nothing) val HasLot val lot -> fmap (\(cid, info) -> NFT info (Just (cid, lot))) val +{-# INLINABLE bundleUp #-} +bundleUp :: [IpfsCidHash] -> BundleId -> BundleInfo -> MarketplaceDatum -> MarketplaceDatum +bundleUp nftIds bundleId bundleInfo store@MarketplaceDatum{..} = + store { mdSingletons = foldr AssocMap.delete mdSingletons nftIds + , mdBundles = AssocMap.insert bundleId (makeBundle mdSingletons nftIds bundleInfo) mdBundles + } + +{-# INLINABLE unbundle #-} +unbundle :: BundleId -> MarketplaceDatum -> MarketplaceDatum +unbundle bundleId store@MarketplaceDatum{..} = + store { mdSingletons = foldr insert mdSingletons $ AssocMap.toList tokens + , mdBundles = AssocMap.delete bundleId mdBundles + } + where + bundle = fromMaybe (traceError "Bundle has not been created.") $ + AssocMap.lookup bundleId mdBundles + tokens = case nbTokens bundle of + NoLot ts -> ts + HasLot _ _ -> traceError "Could not unbundle: bundle has lot." + insert (nftId, record) = AssocMap.insert nftId $ NFT record Nothing + {-# INLINABLE transition #-} transition :: Marketplace -> State MarketplaceDatum -> MarketplaceRedeemer -> Maybe (TxConstraints Void Void, State MarketplaceDatum) transition marketplace state redeemer = case redeemer of @@ -103,6 +126,14 @@ transition marketplace state redeemer = case redeemer of in Just ( mempty , State (insertNft ipfsCidHash newEntry nftStore) currStateValue ) + BundleUpRedeemer nftIds bundleId bundleInfo + -> Just ( mempty + , State (bundleUp nftIds bundleId bundleInfo nftStore) currStateValue + ) + UnbundleRedeemer bundleId + -> Just ( mempty + , State (unbundle bundleId nftStore) currStateValue + ) _ -> trace "Invalid transition" Nothing where stateToken :: Value From a717f9bd8e0e84803a69507aa8368dbce3db4624 Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Mon, 9 Aug 2021 17:07:00 +0700 Subject: [PATCH 036/217] rename bid on auction params prefix --- .../src/Plutus/Contracts/NftMarketplace/OffChain/User.hs | 8 ++++---- MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs index 188b4b852..bc28e1ef5 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs @@ -233,8 +233,8 @@ completeAnAuction marketplace HoldAnAuctionParams {..} = do data BidOnAuctionParams = BidOnAuctionParams { - japIpfsCid :: ByteString, - japBid :: Ada + boapIpfsCid :: ByteString, + boapBid :: Ada } deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON, Schema.ToSchema) @@ -245,7 +245,7 @@ PlutusTx.makeLift ''BidOnAuctionParams -- | The user submits a bid on the auction for specified NFT bidOnAuction :: Core.Marketplace -> BidOnAuctionParams -> Contract w s Text () bidOnAuction marketplace BidOnAuctionParams {..} = do - let ipfsCidHash = sha2_256 japIpfsCid + let ipfsCidHash = sha2_256 boapIpfsCid nftStore <- marketplaceStore marketplace nftEntry <- maybe (throwError "NFT has not been created") pure $ AssocMap.lookup ipfsCidHash nftStore nftAuction <- maybe (throwError "NFT has not been put on auction") pure $ @@ -253,7 +253,7 @@ bidOnAuction marketplace BidOnAuctionParams {..} = do let auctionToken = Auction.getStateToken nftAuction let auctionParams = Auction.fromTuple nftAuction - _ <- mapError (T.pack . Haskell.show) $ Auction.submitBid auctionToken auctionParams japBid + _ <- mapError (T.pack . Haskell.show) $ Auction.submitBid auctionToken auctionParams boapBid logInfo @Haskell.String $ printf "Submitted bid for NFT auction %s" (Haskell.show nftAuction) pure () diff --git a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs index fbec3571c..3c3e38817 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs @@ -185,8 +185,8 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do _ <- Simulator.callEndpointOnInstance buyerCid "bidOnAuction" Marketplace.BidOnAuctionParams { - japIpfsCid = photoTokenIpfsCid, - japBid = fromInteger $ 15*oneAdaInLovelace + boapIpfsCid = photoTokenIpfsCid, + boapBid = fromInteger $ 15*oneAdaInLovelace } _ <- flip Simulator.waitForState buyerCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of Success (ContractSuccess Marketplace.BidSubmitted) -> Just () From 5cfc13f34c9c4d1619fecfb20a059e45b23a163a Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Mon, 9 Aug 2021 17:09:15 +0700 Subject: [PATCH 037/217] add todo --- MetaLamp/nft-marketplace/src/Ext/Plutus/Contracts/Auction.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/MetaLamp/nft-marketplace/src/Ext/Plutus/Contracts/Auction.hs b/MetaLamp/nft-marketplace/src/Ext/Plutus/Contracts/Auction.hs index 2a08c1c6a..265250a13 100644 --- a/MetaLamp/nft-marketplace/src/Ext/Plutus/Contracts/Auction.hs +++ b/MetaLamp/nft-marketplace/src/Ext/Plutus/Contracts/Auction.hs @@ -15,6 +15,7 @@ {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeOperators #-} +-- TODO add initial price option module Ext.Plutus.Contracts.Auction where import Control.Lens (makeClassyPrisms) From 0364b29d65da2556688fcaf2691b9cbdcc5f7ce6 Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Mon, 9 Aug 2021 19:35:18 +0700 Subject: [PATCH 038/217] add constraints for bundle endpoints --- .../NftMarketplace/OnChain/Core/StateMachine.hs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs index 38b1ace21..0dd5a67a0 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs @@ -169,6 +169,20 @@ stateTransitionCheck MarketplaceDatum {..} (RemoveLotRedeemer (Left ipfsCidHash) let nftEntry = fromMaybe (traceError "NFT has not been created") $ AssocMap.lookup ipfsCidHash mdSingletons hasBeenPutOnSale = isJust $ nftLot nftEntry in traceIfFalse "NFT has not been put on sale or auction" hasBeenPutOnSale +stateTransitionCheck MarketplaceDatum {..} (BundleUpRedeemer nftIds bundleId bundleInfo) ctx = + traceIfFalse "BundleUpRedeemer: " $ + let doesNotExist = isNothing $ AssocMap.lookup bundleId mdBundles + notEmty = not $ null nftIds + nfts = fromMaybe (traceError "NFT does not exist or is part of existing bundle") . (`AssocMap.lookup` mdSingletons) <$> nftIds + doesNotHaveLots = all (isNothing . nftLot) nfts + in traceIfFalse "Bundle entry already exists" doesNotExist && + traceIfFalse "Bundle is empty" notEmty && + traceIfFalse "One of NFTs has a lot" doesNotHaveLots +stateTransitionCheck MarketplaceDatum {..} (UnbundleRedeemer bundleId) ctx = + traceIfFalse "UnbundleRedeemer: " $ + let bundle = fromMaybe (traceError "Bundle does not exist") $ AssocMap.lookup bundleId mdBundles + doesNotHaveLot = not $ hasLotBundle bundle + in traceIfFalse "Bundle has a lot" doesNotHaveLot stateTransitionCheck _ _ _ = traceError "Transition disallowed" {-# INLINABLE marketplaceStateMachine #-} From 42d7e431a6b934612e88c4d77e138eb6798ce484 Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Mon, 9 Aug 2021 19:54:19 +0700 Subject: [PATCH 039/217] add bundle endpoints --- .../Contracts/NftMarketplace/OffChain/User.hs | 57 ++++++++++++++----- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs index e517c403f..d64246916 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs @@ -254,24 +254,55 @@ bidOnAuction marketplace BidOnAuctionParams {..} = do logInfo @Haskell.String $ printf "Submitted bid for NFT auction %s" (Haskell.show nftAuction) pure () -data BundleParams = - BundleParams { - bpIpfsCids :: [ByteString] +data BundleUpParams = + BundleUpParams { + bupIpfsCids :: [ByteString], + bupName :: ByteString, + bupDescription :: ByteString, + bupCategory :: Core.Category } deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON, Schema.ToSchema) -PlutusTx.unstableMakeIsData ''BundleParams -PlutusTx.makeLift ''BundleParams +PlutusTx.unstableMakeIsData ''BundleUpParams +PlutusTx.makeLift ''BundleUpParams --- | The user -bundleUp :: Core.Marketplace -> BundleParams -> Contract w s Text () -bundleUp marketplace BundleParams {..} = do +-- | The user creates a bundle from specified NFTs +bundleUp :: Core.Marketplace -> BundleUpParams -> Contract w s Text () +bundleUp marketplace BundleUpParams {..} = do + let bundleId = Core.calcBundleIdHash bupIpfsCids + let nftIds = sha2_256 <$> bupIpfsCids + let bundleInfo = Core.BundleInfo + { biName = bupName + , biDescription = bupDescription + , biCategory = bupCategory + } + + let client = Core.marketplaceClient marketplace + void $ mapError' $ runStep client $ Core.BundleUpRedeemer nftIds bundleId bundleInfo + + logInfo @Haskell.String $ printf "Created a bundle %s" (Haskell.show bundleInfo) pure () --- | The user -unbundle :: Core.Marketplace -> BundleParams -> Contract w s Text () -unbundle marketplace BundleParams {..} = do +data UnbundleParams = + UnbundleParams { + upIpfsCids :: [ByteString] + } + deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) + deriving anyclass (J.ToJSON, J.FromJSON, Schema.ToSchema) + +PlutusTx.unstableMakeIsData ''UnbundleParams +PlutusTx.makeLift ''UnbundleParams + +-- | The user unbundles specified NFTs +unbundle :: Core.Marketplace -> UnbundleParams -> Contract w s Text () +unbundle marketplace UnbundleParams {..} = do + let bundleId = Core.calcBundleIdHash upIpfsCids + + let client = Core.marketplaceClient marketplace + void $ mapError' $ runStep client $ Core.UnbundleRedeemer bundleId + + logInfo @Haskell.String $ printf "Removed bundle by id %s" (Haskell.show bundleId) pure () balanceAt :: PubKeyHash -> AssetClass -> Contract w s Text Integer @@ -288,8 +319,8 @@ type MarketplaceUserSchema = .\/ Endpoint "startAnAuction" HoldAnAuctionParams .\/ Endpoint "completeAnAuction" HoldAnAuctionParams .\/ Endpoint "bidOnAuction" BidOnAuctionParams - .\/ Endpoint "bundleUp" BundleParams - .\/ Endpoint "unbundle" BundleParams + .\/ Endpoint "bundleUp" BundleUpParams + .\/ Endpoint "unbundle" UnbundleParams .\/ Endpoint "ownPubKey" () .\/ Endpoint "ownPubKeyBalance" () From 629c82a9b76429b5a9e152d4b84a6bed4ef42b9c Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Tue, 10 Aug 2021 15:03:48 +0700 Subject: [PATCH 040/217] fix traceError call bug --- .../Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs | 5 +++-- .../Contracts/NftMarketplace/OnChain/Core/StateMachine.hs | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs index db1dda7f9..5ff337ace 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs @@ -133,8 +133,9 @@ makeBundle singletons nftIds bundleInfo = , nbTokens = NoLot $ foldr insert AssocMap.empty nftIds } where - insert nftId = let entry = nftRecord $ fromMaybe (traceError "NFT singleton entry not found") $ AssocMap.lookup nftId singletons - in AssocMap.insert nftId entry + insert nftId store = case AssocMap.lookup nftId singletons of + Just n -> AssocMap.insert nftId (nftRecord n) store + Nothing -> store {-# INLINABLE hasLotNft #-} hasLotNft :: NFT -> Bool diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs index 0dd5a67a0..13a695c0c 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs @@ -87,15 +87,15 @@ nftUnion MarketplaceDatum{..} = foldr union mdSingletons $ fmap getNfts $ toList {-# INLINABLE bundleUp #-} bundleUp :: [IpfsCidHash] -> BundleId -> BundleInfo -> MarketplaceDatum -> MarketplaceDatum -bundleUp nftIds bundleId bundleInfo store@MarketplaceDatum{..} = - store { mdSingletons = foldr AssocMap.delete mdSingletons nftIds +bundleUp nftIds bundleId bundleInfo MarketplaceDatum{..} = + MarketplaceDatum { mdSingletons = foldr AssocMap.delete mdSingletons nftIds , mdBundles = AssocMap.insert bundleId (makeBundle mdSingletons nftIds bundleInfo) mdBundles } {-# INLINABLE unbundle #-} unbundle :: BundleId -> MarketplaceDatum -> MarketplaceDatum -unbundle bundleId store@MarketplaceDatum{..} = - store { mdSingletons = foldr insert mdSingletons $ AssocMap.toList tokens +unbundle bundleId MarketplaceDatum{..} = + MarketplaceDatum { mdSingletons = foldr insert mdSingletons $ AssocMap.toList tokens , mdBundles = AssocMap.delete bundleId mdBundles } where From c590ffafcce964835a7d735c2a711e4f6b4e46a8 Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Tue, 10 Aug 2021 18:29:03 +0700 Subject: [PATCH 041/217] add bundle simulation --- .../OnChain/Core/StateMachine.hs | 1 + .../src/Plutus/PAB/Simulation.hs | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs index 13a695c0c..7f760f1d4 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs @@ -44,6 +44,7 @@ newtype Marketplace = PlutusTx.makeLift ''Marketplace +-- TODO (?) Prohibit for users which don't have bundled NFTs inside wallet to bundle and unbundle -- TODO make sum types for eithers (?) data MarketplaceRedeemer = CreateNftRedeemer IpfsCidHash NftInfo diff --git a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs index 298ef2cd3..cb3a55104 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs @@ -208,6 +208,29 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do _ -> Nothing Simulator.logString @(Builtin MarketplaceContracts) $ "Successful holdAnAuction" + _ <- + Simulator.callEndpointOnInstance userCid "bundleUp" $ + Marketplace.BundleUpParams { + bupIpfsCids = [photoTokenIpfsCid,catTokenIpfsCid], + bupName = "Picture gallery", + bupDescription = "Collection of visual media", + bupCategory = ["User","Stan"] + } + flip Simulator.waitForState userCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of + Success (ContractSuccess Marketplace.Bundled) -> Just () + _ -> Nothing + Simulator.logString @(Builtin MarketplaceContracts) $ "Successful bundleUp" + + _ <- + Simulator.callEndpointOnInstance userCid "unbundle" $ + Marketplace.UnbundleParams { + upIpfsCids = [photoTokenIpfsCid,catTokenIpfsCid] + } + flip Simulator.waitForState userCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of + Success (ContractSuccess Marketplace.Unbundled) -> Just () + _ -> Nothing + Simulator.logString @(Builtin MarketplaceContracts) $ "Successful unbundle" + _ <- Simulator.callEndpointOnInstance cidInfo "fundsAt" buyer v <- flip Simulator.waitForState cidInfo $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.InfoContractState)) of Success (ContractSuccess (Marketplace.FundsAt v)) -> Just v From d86760082e7dc0df6a3d427026cd6d4cda32e266 Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Tue, 10 Aug 2021 19:37:25 +0700 Subject: [PATCH 042/217] add bundle lot state transition --- .../Contracts/NftMarketplace/OffChain/User.hs | 6 +-- .../OnChain/Core/StateMachine.hs | 37 ++++++++++++++++++- .../src/Plutus/PAB/Simulation.hs | 4 +- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs index d64246916..a1d6207ef 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs @@ -256,10 +256,10 @@ bidOnAuction marketplace BidOnAuctionParams {..} = do data BundleUpParams = BundleUpParams { - bupIpfsCids :: [ByteString], - bupName :: ByteString, + bupIpfsCids :: [ByteString], + bupName :: ByteString, bupDescription :: ByteString, - bupCategory :: Core.Category + bupCategory :: Core.Category } deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON, Schema.ToSchema) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs index 7f760f1d4..7cd7a0e45 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs @@ -48,7 +48,7 @@ PlutusTx.makeLift ''Marketplace -- TODO make sum types for eithers (?) data MarketplaceRedeemer = CreateNftRedeemer IpfsCidHash NftInfo - | PutLotRedeemer (Either (IpfsCidHash, IpfsCid) (AssocMap.Map IpfsCidHash IpfsCid)) LotLink + | PutLotRedeemer (Either (IpfsCidHash, IpfsCid) (BundleId, AssocMap.Map IpfsCidHash IpfsCid)) LotLink | RemoveLotRedeemer (Either IpfsCidHash BundleId) | BundleUpRedeemer [IpfsCidHash] BundleId BundleInfo | UnbundleRedeemer BundleId @@ -77,6 +77,12 @@ insertNft :: IpfsCidHash insertNft ipfsCidHash nftEntry store@MarketplaceDatum{..} = store { mdSingletons = AssocMap.insert ipfsCidHash nftEntry mdSingletons } +{-# INLINABLE insertBundle #-} +insertBundle :: BundleId + -> NftBundle -> MarketplaceDatum -> MarketplaceDatum +insertBundle bundleId bundle store@MarketplaceDatum{..} = + store { mdBundles = AssocMap.insert bundleId bundle mdBundles } + {-# INLINABLE nftUnion #-} nftUnion :: MarketplaceDatum -> AssocMap.Map IpfsCidHash NFT nftUnion MarketplaceDatum{..} = foldr union mdSingletons $ fmap getNfts $ toList mdBundles @@ -107,6 +113,23 @@ unbundle bundleId MarketplaceDatum{..} = HasLot _ _ -> traceError "Could not unbundle: bundle has lot." insert (nftId, record) = AssocMap.insert nftId $ NFT record Nothing +{-# INLINABLE addLotToBundle #-} +addLotToBundle + :: AssocMap.Map IpfsCidHash IpfsCid -> LotLink -> NftBundle -> NftBundle +addLotToBundle cids lot NftBundle {..} = case nbTokens of + NoLot tokens -> NftBundle nbRecord $ HasLot (AssocMap.fromList $ fmap addCid $ AssocMap.toList tokens) lot + HasLot _ _ -> traceError "Could not add lot: bundle has one." + where + addCid :: (IpfsCidHash, NftInfo) -> (IpfsCidHash, (IpfsCid, NftInfo)) + addCid (nftId, entry) = (nftId, (fromMaybe (traceError "NFT IPFS Cid not provided") $ AssocMap.lookup nftId cids, entry)) + +removeLotFromBundle :: NftBundle -> NftBundle +removeLotFromBundle NftBundle {..} = NftBundle nbRecord $ NoLot $ snd <$> tokens + where + tokens = case nbTokens of + HasLot tokens _ -> tokens + NoLot tokens -> traceError "Could not remove lot: bundle has none." + {-# INLINABLE transition #-} transition :: Marketplace -> State MarketplaceDatum -> MarketplaceRedeemer -> Maybe (TxConstraints Void Void, State MarketplaceDatum) transition marketplace state redeemer = case redeemer of @@ -121,12 +144,24 @@ transition marketplace state redeemer = case redeemer of in Just ( mempty , State (insertNft ipfsCidHash newEntry nftStore) currStateValue ) + PutLotRedeemer (Right (bundleId, cids)) lot + -> let newEntry = maybe (traceError "Bundle has not been created.") (addLotToBundle cids lot) $ + AssocMap.lookup bundleId $ mdBundles nftStore + in Just ( mempty + , State (insertBundle bundleId newEntry nftStore) currStateValue + ) RemoveLotRedeemer (Left ipfsCidHash) -> let newEntry = maybe (traceError "NFT has not been created.") (_nftLot .~ Nothing) $ AssocMap.lookup ipfsCidHash $ mdSingletons nftStore in Just ( mempty , State (insertNft ipfsCidHash newEntry nftStore) currStateValue ) + RemoveLotRedeemer (Right bundleId) + -> let newEntry = maybe (traceError "NFT has not been created.") removeLotFromBundle $ + AssocMap.lookup bundleId $ mdBundles nftStore + in Just ( mempty + , State (insertBundle bundleId newEntry nftStore) currStateValue + ) BundleUpRedeemer nftIds bundleId bundleInfo -> Just ( mempty , State (bundleUp nftIds bundleId bundleInfo nftStore) currStateValue diff --git a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs index cb3a55104..94cccf93b 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs @@ -218,7 +218,7 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do } flip Simulator.waitForState userCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of Success (ContractSuccess Marketplace.Bundled) -> Just () - _ -> Nothing + _ -> Nothing Simulator.logString @(Builtin MarketplaceContracts) $ "Successful bundleUp" _ <- @@ -228,7 +228,7 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do } flip Simulator.waitForState userCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of Success (ContractSuccess Marketplace.Unbundled) -> Just () - _ -> Nothing + _ -> Nothing Simulator.logString @(Builtin MarketplaceContracts) $ "Successful unbundle" _ <- Simulator.callEndpointOnInstance cidInfo "fundsAt" buyer From 9de86b8c1ccfdd6becc1de2a8a9845d3cc8cd2fe Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Tue, 10 Aug 2021 19:42:02 +0700 Subject: [PATCH 043/217] add singleton existing lot check --- .../Contracts/NftMarketplace/OnChain/Core/StateMachine.hs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs index 7cd7a0e45..531ce7790 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs @@ -123,12 +123,13 @@ addLotToBundle cids lot NftBundle {..} = case nbTokens of addCid :: (IpfsCidHash, NftInfo) -> (IpfsCidHash, (IpfsCid, NftInfo)) addCid (nftId, entry) = (nftId, (fromMaybe (traceError "NFT IPFS Cid not provided") $ AssocMap.lookup nftId cids, entry)) +{-# INLINABLE removeLotFromBundle #-} removeLotFromBundle :: NftBundle -> NftBundle removeLotFromBundle NftBundle {..} = NftBundle nbRecord $ NoLot $ snd <$> tokens where tokens = case nbTokens of HasLot tokens _ -> tokens - NoLot tokens -> traceError "Could not remove lot: bundle has none." + NoLot _ -> traceError "Could not remove lot: bundle has none." {-# INLINABLE transition #-} transition :: Marketplace -> State MarketplaceDatum -> MarketplaceRedeemer -> Maybe (TxConstraints Void Void, State MarketplaceDatum) @@ -197,9 +198,10 @@ stateTransitionCheck MarketplaceDatum {..} (PutLotRedeemer (Left (ipfsCidHash, i nftValue = V.singleton (niCurrency $ nftRecord nftEntry) (V.TokenName ipfsCid) 1 hasBeenPutOnSale = lotValue == nftValue isValidHash = sha2_256 ipfsCid == ipfsCidHash --- TODO (?) check that there was no previous lot (isNothing $ nftLot nftEntry) + hasNoExistingLot = isNothing $ nftLot nftEntry in traceIfFalse "NFT has not been put on sale or auction" hasBeenPutOnSale && - traceIfFalse "Invalid IPFS Cid Hash" isValidHash + traceIfFalse "Invalid IPFS Cid Hash" isValidHash && + traceIfFalse "NFT already has a lot" hasNoExistingLot stateTransitionCheck MarketplaceDatum {..} (RemoveLotRedeemer (Left ipfsCidHash)) ctx = traceIfFalse "RemoveLotRedeemer: " $ let nftEntry = fromMaybe (traceError "NFT has not been created") $ AssocMap.lookup ipfsCidHash mdSingletons From af31d2fa338357601826722096f59f7327c05150 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Tue, 10 Aug 2021 23:23:27 +0700 Subject: [PATCH 044/217] add bundle lot state validation --- .../NftMarketplace/OnChain/Core/NFT.hs | 16 +++++++++++--- .../OnChain/Core/StateMachine.hs | 21 +++++++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs index 5ff337ace..2103114a9 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs @@ -137,9 +137,19 @@ makeBundle singletons nftIds bundleInfo = Just n -> AssocMap.insert nftId (nftRecord n) store Nothing -> store -{-# INLINABLE hasLotNft #-} -hasLotNft :: NFT -> Bool -hasLotNft = isJust . nftLot +{-# INLINABLE bundleValue #-} +bundleValue :: AssocMap.Map IpfsCidHash IpfsCid -> NftBundle -> Value +bundleValue cids bundle = case nbTokens bundle of + NoLot tokens -> foldMap getValueNoLot $ AssocMap.toList tokens + HasLot tokens _ -> foldMap getValueHasLot tokens + where + getValueHasLot :: (IpfsCid, NftInfo) -> Value + getValueHasLot (ipfsCid, nft) = V.singleton (niCurrency nft) (V.TokenName ipfsCid) 1 + + getValueNoLot :: (IpfsCidHash, NftInfo) -> Value + getValueNoLot (ipfsCidHash, nft) = case AssocMap.lookup ipfsCidHash cids of + Just ipfsCid -> V.singleton (niCurrency nft) (V.TokenName ipfsCid) 1 + Nothing -> mempty {-# INLINABLE hasLotBundle #-} hasLotBundle :: NftBundle -> Bool diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs index 531ce7790..cdb2c4937 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs @@ -202,11 +202,32 @@ stateTransitionCheck MarketplaceDatum {..} (PutLotRedeemer (Left (ipfsCidHash, i in traceIfFalse "NFT has not been put on sale or auction" hasBeenPutOnSale && traceIfFalse "Invalid IPFS Cid Hash" isValidHash && traceIfFalse "NFT already has a lot" hasNoExistingLot +stateTransitionCheck MarketplaceDatum {..} (PutLotRedeemer (Right (bundleId, cids)) lot) ctx = + traceIfFalse "PutLotRedeemer: " $ + let bundle = fromMaybe (traceError "Bundle has not been created") $ AssocMap.lookup bundleId mdBundles + lotValue = either Sale.saleValue (Auction.apAsset . Auction.fromTuple) lot + cidHashes = case nbTokens bundle of + NoLot tokens -> AssocMap.keys tokens + HasLot tokens _ -> AssocMap.keys tokens + allCidsProvided = all (isJust . (`AssocMap.lookup` cids)) cidHashes + hasBeenPutOnSale = bundleValue cids bundle == lotValue + isValidHash (ipfsCidHash, ipfsCid) = sha2_256 ipfsCid == ipfsCidHash + hasValidHashes = all isValidHash $ AssocMap.toList cids + hasNoExistingLot = not $ hasLotBundle bundle + in traceIfFalse "Bundle has not been put on sale or auction" hasBeenPutOnSale && + traceIfFalse "Not all IPFS Cids provided" allCidsProvided && + traceIfFalse "Invalid IPFS Cid Hash provided" hasValidHashes && + traceIfFalse "Bundle already has a lot" hasNoExistingLot stateTransitionCheck MarketplaceDatum {..} (RemoveLotRedeemer (Left ipfsCidHash)) ctx = traceIfFalse "RemoveLotRedeemer: " $ let nftEntry = fromMaybe (traceError "NFT has not been created") $ AssocMap.lookup ipfsCidHash mdSingletons hasBeenPutOnSale = isJust $ nftLot nftEntry in traceIfFalse "NFT has not been put on sale or auction" hasBeenPutOnSale +stateTransitionCheck MarketplaceDatum {..} (RemoveLotRedeemer (Right bundleId)) ctx = + traceIfFalse "RemoveLotRedeemer: " $ + let bundle = fromMaybe (traceError "Bundle has not been created") $ AssocMap.lookup bundleId mdBundles + hasLot = hasLotBundle bundle + in traceIfFalse "Bundle has not been put on sale or auction" hasLot stateTransitionCheck MarketplaceDatum {..} (BundleUpRedeemer nftIds bundleId bundleInfo) ctx = traceIfFalse "BundleUpRedeemer: " $ let doesNotExist = isNothing $ AssocMap.lookup bundleId mdBundles From 5b478576351a31c82ce46fec65999b693520fcb2 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Wed, 11 Aug 2021 16:11:09 +0700 Subject: [PATCH 045/217] add id modules --- MetaLamp/nft-marketplace/plutus-starter.cabal | 2 +- .../Contracts/NftMarketplace/Endpoints.hs | 1 + .../Contracts/NftMarketplace/OffChain/ID.hs | 50 +++++++++++++++++ .../Contracts/NftMarketplace/OnChain/Core.hs | 1 + .../NftMarketplace/OnChain/Core/ID.hs | 56 +++++++++++++++++++ 5 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/ID.hs create mode 100644 MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/ID.hs diff --git a/MetaLamp/nft-marketplace/plutus-starter.cabal b/MetaLamp/nft-marketplace/plutus-starter.cabal index 77f7aef2d..0d1ad4c0b 100644 --- a/MetaLamp/nft-marketplace/plutus-starter.cabal +++ b/MetaLamp/nft-marketplace/plutus-starter.cabal @@ -23,7 +23,7 @@ maintainer: Your email library exposed-modules: - Plutus.Contracts.NftMarketplace.OnChain.Core Plutus.Contracts.Services.Sale.Core Ext.PlutusTx.AssocMap Plutus.Contracts.NftMarketplace.OnChain.Core.NFT Ext.Plutus.Contracts.Auction Plutus.Contracts.Services.Sale Plutus.Contracts.Services.Sale.Endpoints Plutus.Contracts.Services.Sale.StateMachine Plutus.Contracts.NftMarketplace.OffChain.Info Ext.Plutus.Ledger.Contexts Ext.Plutus.Ledger.Value Plutus.Contracts.NftMarketplace.OffChain.User Plutus.Abstract.ContractResponse Plutus.Abstract.OutputValue Plutus.Abstract.TxUtils Plutus.Contracts.NftMarketplace.Endpoints Plutus.Contracts.NftMarketplace.OffChain.Owner Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine Plutus.PAB.Simulation + Plutus.Contracts.NftMarketplace.OnChain.Core Plutus.Contracts.NftMarketplace.OffChain.ID Plutus.Contracts.NftMarketplace.OnChain.Core.ID Plutus.Contracts.Services.Sale.Core Ext.PlutusTx.AssocMap Plutus.Contracts.NftMarketplace.OnChain.Core.NFT Ext.Plutus.Contracts.Auction Plutus.Contracts.Services.Sale Plutus.Contracts.Services.Sale.Endpoints Plutus.Contracts.Services.Sale.StateMachine Plutus.Contracts.NftMarketplace.OffChain.Info Ext.Plutus.Ledger.Contexts Ext.Plutus.Ledger.Value Plutus.Contracts.NftMarketplace.OffChain.User Plutus.Abstract.ContractResponse Plutus.Abstract.OutputValue Plutus.Abstract.TxUtils Plutus.Contracts.NftMarketplace.Endpoints Plutus.Contracts.NftMarketplace.OffChain.Owner Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine Plutus.PAB.Simulation build-depends: base >= 4.9 && < 5, aeson, diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/Endpoints.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/Endpoints.hs index b821ae4d3..12987c34e 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/Endpoints.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/Endpoints.hs @@ -2,6 +2,7 @@ module Plutus.Contracts.NftMarketplace.Endpoints ( module Export ) where +import Plutus.Contracts.NftMarketplace.OffChain.ID as Export import Plutus.Contracts.NftMarketplace.OffChain.Info as Export import Plutus.Contracts.NftMarketplace.OffChain.Owner as Export import Plutus.Contracts.NftMarketplace.OffChain.User as Export diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/ID.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/ID.hs new file mode 100644 index 000000000..d899f0390 --- /dev/null +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/ID.hs @@ -0,0 +1,50 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE TypeApplications #-} + +module Plutus.Contracts.NftMarketplace.OffChain.ID where + +import Control.Monad hiding (fmap) +import qualified Data.Aeson as J +import Data.Proxy (Proxy (..)) +import Data.Text (Text) +import qualified Data.Text as T +import qualified GHC.Generics as Haskell +import Ledger +import qualified Ledger.Typed.Scripts as Scripts +import Ledger.Value +import Plutus.Abstract.ContractResponse (ContractResponse, + withContractResponse) +import Plutus.Contract +import Plutus.Contract.StateMachine +import Plutus.Contracts.Currency as Currency +import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Core +import qualified PlutusTx +import qualified PlutusTx.AssocMap as AssocMap +import PlutusTx.Prelude hiding + (Semigroup (..)) +import Prelude (Semigroup (..)) +import qualified Prelude as Haskell +import qualified Schema +import Text.Printf (printf) + +data UserItemId = UserNftId Core.IpfsCid | UserBundleId [Core.IpfsCid] + deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) + deriving anyclass (J.ToJSON, J.FromJSON) + +toInternalId :: UserItemId -> Either Core.InternalNftId Core.InternalBundleId +toInternalId (UserNftId ipfsCid) = Left + Core.InternalNftId { + Core.iniIpfsCidHash = sha2_256 ipfsCid, + Core.iniIpfsCid = ipfsCid + } +toInternalId (UserBundleId cids) = Right + Core.InternalBundleId { + Core.ibiIpfsCids = AssocMap.fromList $ (\cid -> (sha2_256 cid, cid)) <$> cids, + Core.ibiBundleId = Core.calcBundleIdHash cids + } diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core.hs index cfb6a6c0a..fad32e68a 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core.hs @@ -15,6 +15,7 @@ import qualified Ledger.Typed.Scripts as Sc import Ledger.Value import Plutus.Contract import Plutus.Contract.StateMachine +import Plutus.Contracts.NftMarketplace.OnChain.Core.ID as Export import Plutus.Contracts.NftMarketplace.OnChain.Core.NFT as Export import Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine as Export import qualified PlutusTx diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/ID.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/ID.hs new file mode 100644 index 000000000..ec5717dde --- /dev/null +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/ID.hs @@ -0,0 +1,56 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TypeApplications #-} +{-# OPTIONS_GHC -fno-specialise #-} +{-# OPTIONS_GHC -fno-strictness #-} +{-# OPTIONS_GHC -fno-ignore-interface-pragmas #-} +{-# OPTIONS_GHC -fno-omit-interface-pragmas #-} +{-# OPTIONS_GHC -fobject-code #-} + +module Plutus.Contracts.NftMarketplace.OnChain.Core.ID where + +import Control.Lens ((&), (.~), + (?~), (^.)) +import qualified Control.Lens as Lens +import qualified Crypto.Hash as Hash +import qualified Data.Aeson as J +import qualified Data.ByteArray as BA +import qualified Data.List as HL +import qualified Data.Text as T +import qualified Ext.Plutus.Contracts.Auction as Auction +import qualified GHC.Generics as Haskell +import Ledger +import qualified Ledger.Constraints as Constraints +import qualified Ledger.Typed.Scripts as Scripts +import qualified Ledger.Value as V +import Plutus.Contract +import Plutus.Contract.StateMachine +import Plutus.Contracts.NftMarketplace.OnChain.Core.NFT +import qualified Plutus.Contracts.Services.Sale as Sale +import qualified PlutusTx +import qualified PlutusTx.AssocMap as AssocMap +import PlutusTx.Prelude hiding + (Semigroup (..)) +import Prelude (Semigroup (..)) +import qualified Prelude as Haskell + +data InternalNftId = InternalNftId { + iniIpfsCidHash :: !IpfsCidHash, + iniIpfsCid :: !IpfsCid +} + deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) + deriving anyclass (J.ToJSON, J.FromJSON) + +data InternalBundleId = InternalBundleId { + ibiIpfsCids :: !(AssocMap.Map IpfsCidHash IpfsCid), + ibiBundleId :: !BundleId +} + deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) + deriving anyclass (J.ToJSON, J.FromJSON) From ee208929568eaf1402d25732a8a49dce2bcbf2bc Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Fri, 13 Aug 2021 17:16:18 +0700 Subject: [PATCH 046/217] add bundles to lot endpoints --- .../Contracts/NftMarketplace/OffChain/ID.hs | 25 ++- .../Contracts/NftMarketplace/OffChain/Info.hs | 37 +++- .../Contracts/NftMarketplace/OffChain/User.hs | 177 ++++++++++-------- .../NftMarketplace/OnChain/Core/ID.hs | 12 +- .../NftMarketplace/OnChain/Core/NFT.hs | 4 + .../OnChain/Core/StateMachine.hs | 14 +- .../src/Plutus/PAB/Simulation.hs | 18 +- 7 files changed, 173 insertions(+), 114 deletions(-) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/ID.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/ID.hs index d899f0390..ae6dbc3ea 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/ID.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/ID.hs @@ -1,11 +1,13 @@ -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE DeriveAnyClass #-} -{-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE DerivingStrategies #-} -{-# LANGUAGE NoImplicitPrelude #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE StandaloneDeriving #-} -{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TypeApplications #-} module Plutus.Contracts.NftMarketplace.OffChain.ID where @@ -33,10 +35,17 @@ import qualified Prelude as Haskell import qualified Schema import Text.Printf (printf) +-- type UserItemId = Either Core.IpfsCid [Core.IpfsCid] data UserItemId = UserNftId Core.IpfsCid | UserBundleId [Core.IpfsCid] deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON) +instance Schema.ToSchema UserItemId where + toSchema = Schema.FormSchemaUnsupported "TODO how to make these instances for sum types?" + +PlutusTx.unstableMakeIsData ''UserItemId +PlutusTx.makeLift ''UserItemId + toInternalId :: UserItemId -> Either Core.InternalNftId Core.InternalBundleId toInternalId (UserNftId ipfsCid) = Left Core.InternalNftId { diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs index 5f75c8e9d..ab0b28709 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs @@ -34,6 +34,7 @@ import Plutus.Abstract.ContractResponse (ContractResponse, import Plutus.Contract import Plutus.Contract.StateMachine import Plutus.Contracts.Currency as Currency +import Plutus.Contracts.NftMarketplace.OffChain.ID import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Core import qualified PlutusTx import qualified PlutusTx.AssocMap as AssocMap @@ -53,6 +54,16 @@ getStateDatum :: Maybe (OnChainState Core.MarketplaceDatum i, UtxoMap) -> Contract w s Text Core.MarketplaceDatum getStateDatum = maybe (throwError "Marketplace output not found") (pure . tyTxOutData . fst . fst) +getNftEntry :: Core.MarketplaceDatum -> Core.InternalNftId -> Contract w s Text Core.NFT +getNftEntry nftStore (Core.InternalNftId ipfsCidHash ipfsCid) = + maybe (throwError "NFT has not been created") pure $ + AssocMap.lookup ipfsCidHash $ Core.mdSingletons nftStore + +getBundleEntry :: Core.MarketplaceDatum -> Core.InternalBundleId -> Contract w s Text Core.NftBundle +getBundleEntry nftStore (Core.InternalBundleId bundleId cids) = + maybe (throwError "Bundle has not been created") pure $ + AssocMap.lookup bundleId $ Core.mdBundles nftStore + -- | Gets all UTxOs belonging to a user and concats them into one Value fundsAt :: PubKeyHash -> Contract w s Text Value fundsAt pkh = utxoValue <$> utxoAt (pubKeyHashAddress pkh) @@ -62,16 +73,22 @@ marketplaceFunds :: Core.Marketplace -> Contract w s Text Value marketplaceFunds marketplace = utxoValue <$> utxoAt (Core.marketplaceAddress marketplace) -- | Gets current auction state for specified NFT -getAuctionState :: Core.Marketplace -> Core.IpfsCid -> Contract w s Text Auction.AuctionState -getAuctionState marketplace ipfsCid = do - let ipfsCidHash = sha2_256 ipfsCid - nftStore <- Core.mdSingletons <$> marketplaceStore marketplace - nftEntry <- maybe (throwError "NFT has not been created") pure $ AssocMap.lookup ipfsCidHash nftStore - nftAuction <- maybe (throwError "NFT has not been put on auction") pure $ - nftEntry ^. Core._nftLot ^? traverse . _2 . _Right +getAuctionState :: Core.Marketplace -> UserItemId -> Contract w s Text Auction.AuctionState +getAuctionState marketplace itemId = do + let internalId = toInternalId itemId + nftStore <- marketplaceStore marketplace + auction <- case internalId of + Left nftId@(Core.InternalNftId ipfsCidHash ipfsCid) -> do + nftEntry <- getNftEntry nftStore nftId + maybe (throwError "NFT has not been put on auction") pure $ + nftEntry ^. Core._nftLot ^? traverse . _2 . _Right + Right bundleId@(Core.InternalBundleId bundleHash cids) -> do + bundleEntry <- getBundleEntry nftStore bundleId + maybe (throwError "Bundle has not been put on auction") pure $ + bundleEntry ^. Core._nbTokens ^? Core._HasLot . _2 . _Right - let auctionToken = Auction.getStateToken nftAuction - let auctionParams = Auction.fromTuple nftAuction + let auctionToken = Auction.getStateToken auction + let auctionParams = Auction.fromTuple auction auctionState <- do st <- mapError (T.pack . Haskell.show) $ Auction.currentState auctionToken auctionParams maybe (throwError "Auction state not found") pure st @@ -86,7 +103,7 @@ type MarketplaceInfoSchema = Endpoint "fundsAt" PubKeyHash .\/ Endpoint "marketplaceFunds" () .\/ Endpoint "marketplaceStore" () - .\/ Endpoint "getAuctionState" Core.IpfsCid + .\/ Endpoint "getAuctionState" UserItemId data InfoContractState = FundsAt Value diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs index 479972755..d8ac790a9 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs @@ -33,9 +33,8 @@ import Plutus.Abstract.ContractResponse import Plutus.Contract import Plutus.Contract.StateMachine import Plutus.Contracts.Currency as Currency -import Plutus.Contracts.NftMarketplace.OffChain.Info (fundsAt, - mapError', - marketplaceStore) +import Plutus.Contracts.NftMarketplace.OffChain.ID +import Plutus.Contracts.NftMarketplace.OffChain.Info import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Core import qualified Plutus.Contracts.Services.Sale as Sale import qualified PlutusTx @@ -93,7 +92,7 @@ createNft marketplace CreateNftParams {..} = do data OpenSaleParams = OpenSaleParams { - ospIpfsCid :: ByteString, + ospItemId :: UserItemId, ospSalePrice :: Sale.LovelacePrice } deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) @@ -105,81 +104,88 @@ PlutusTx.makeLift ''OpenSaleParams -- | The user opens sale for his NFT openSale :: Core.Marketplace -> OpenSaleParams -> Contract w s Text () openSale marketplace OpenSaleParams {..} = do - let ipfsCidHash = sha2_256 ospIpfsCid - nftStore <- Core.mdSingletons <$> marketplaceStore marketplace - nftEntry <- maybe (throwError "NFT has not been created") pure $ AssocMap.lookup ipfsCidHash nftStore - let tokenName = V.TokenName ospIpfsCid + let internalId = toInternalId ospItemId + nftStore <- marketplaceStore marketplace + saleValue <- case internalId of + Left nftId@(Core.InternalNftId ipfsCidHash ipfsCid) -> + Core.nftValue ipfsCid <$> getNftEntry nftStore nftId + Right bundleId@(Core.InternalBundleId bundleHash cids) -> + Core.bundleValue cids <$> getBundleEntry nftStore bundleId sale <- Sale.openSale Sale.OpenSaleParams { ospSalePrice = ospSalePrice, - ospSaleValue = V.singleton (nftEntry ^. Core._nftRecord . Core._niCurrency) tokenName 1 + ospSaleValue = saleValue } let client = Core.marketplaceClient marketplace let lot = Left sale - void $ mapError' $ runStep client $ Core.PutLotRedeemer (Left (ipfsCidHash, ospIpfsCid)) lot + void $ mapError' $ runStep client $ Core.PutLotRedeemer internalId lot logInfo @Haskell.String $ printf "Created NFT sale %s" (Haskell.show sale) pure () -data BuyNftParams = - BuyNftParams { - bnpIpfsCid :: ByteString +data CompleteSaleParams = + CompleteSaleParams { + cspItemId :: UserItemId } deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON, Schema.ToSchema) -PlutusTx.unstableMakeIsData ''BuyNftParams -PlutusTx.makeLift ''BuyNftParams +PlutusTx.unstableMakeIsData ''CompleteSaleParams +PlutusTx.makeLift ''CompleteSaleParams -- | The user buys specified NFT lot -buyNft :: Core.Marketplace -> BuyNftParams -> Contract w s Text () -buyNft marketplace BuyNftParams {..} = do - let ipfsCidHash = sha2_256 bnpIpfsCid - nftStore <- Core.mdSingletons <$> marketplaceStore marketplace - nftEntry <- maybe (throwError "NFT has not been created") pure $ AssocMap.lookup ipfsCidHash nftStore - nftSale <- maybe (throwError "NFT has not been put on sale") pure $ - nftEntry ^. Core._nftLot ^? traverse . _2 . _Left - - _ <- Sale.buyLot nftSale +buyNft :: Core.Marketplace -> CompleteSaleParams -> Contract w s Text () +buyNft marketplace CompleteSaleParams {..} = do + let internalId = toInternalId cspItemId + nftStore <- marketplaceStore marketplace + sale <- case internalId of + Left nftId@(Core.InternalNftId ipfsCidHash ipfsCid) -> do + nftEntry <- getNftEntry nftStore nftId + maybe (throwError "NFT has not been put on sale") pure $ + nftEntry ^. Core._nftLot ^? traverse . _2 . _Left + Right bundleId@(Core.InternalBundleId bundleHash cids) -> do + bundleEntry <- getBundleEntry nftStore bundleId + maybe (throwError "Bundle has not been put on sale") pure $ + bundleEntry ^. Core._nbTokens ^? Core._HasLot . _2 . _Left + + _ <- Sale.buyLot sale let client = Core.marketplaceClient marketplace - void $ mapError' $ runStep client $ Core.RemoveLotRedeemer (Left ipfsCidHash) + void $ mapError' $ runStep client $ Core.RemoveLotRedeemer $ + Lens.bimap Core.iniIpfsCidHash Core.ibiBundleId internalId - logInfo @Haskell.String $ printf "Bought NFT from sale %s" (Haskell.show nftSale) + logInfo @Haskell.String $ printf "Bought lot from sale %s" (Haskell.show sale) pure () -data CloseSaleParams = - CloseSaleParams { - cspIpfsCid :: ByteString - } - deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) - deriving anyclass (J.ToJSON, J.FromJSON, Schema.ToSchema) - -PlutusTx.unstableMakeIsData ''CloseSaleParams -PlutusTx.makeLift ''CloseSaleParams - -- | The user closes NFT sale and receives his token back -closeSale :: Core.Marketplace -> CloseSaleParams -> Contract w s Text () -closeSale marketplace CloseSaleParams {..} = do - let ipfsCidHash = sha2_256 cspIpfsCid - nftStore <- Core.mdSingletons <$> marketplaceStore marketplace - nftEntry <- maybe (throwError "NFT has not been created") pure $ AssocMap.lookup ipfsCidHash nftStore - nftSale <- maybe (throwError "NFT has not been put on sale") pure $ - nftEntry ^. Core._nftLot ^? traverse . _2 . _Left - - _ <- Sale.redeemLot nftSale +closeSale :: Core.Marketplace -> CompleteSaleParams -> Contract w s Text () +closeSale marketplace CompleteSaleParams {..} = do + let internalId = toInternalId cspItemId + nftStore <- marketplaceStore marketplace + sale <- case internalId of + Left nftId@(Core.InternalNftId ipfsCidHash ipfsCid) -> do + nftEntry <- getNftEntry nftStore nftId + maybe (throwError "NFT has not been put on sale") pure $ + nftEntry ^. Core._nftLot ^? traverse . _2 . _Left + Right bundleId@(Core.InternalBundleId bundleHash cids) -> do + bundleEntry <- getBundleEntry nftStore bundleId + maybe (throwError "Bundle has not been put on sale") pure $ + bundleEntry ^. Core._nbTokens ^? Core._HasLot . _2 . _Left + + _ <- Sale.redeemLot sale let client = Core.marketplaceClient marketplace - void $ mapError' $ runStep client $ Core.RemoveLotRedeemer (Left ipfsCidHash) + void $ mapError' $ runStep client $ Core.RemoveLotRedeemer $ + Lens.bimap Core.iniIpfsCidHash Core.ibiBundleId internalId - logInfo @Haskell.String $ printf "Closed NFT sale %s" (Haskell.show nftSale) + logInfo @Haskell.String $ printf "Closed lot sale %s" (Haskell.show sale) pure () data HoldAnAuctionParams = HoldAnAuctionParams { - haapIpfsCid :: ByteString, + haapItemId :: UserItemId, haapDuration :: Slot } deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) @@ -191,19 +197,21 @@ PlutusTx.makeLift ''HoldAnAuctionParams -- | The user starts an auction for specified NFT startAnAuction :: Core.Marketplace -> HoldAnAuctionParams -> Contract w s Text () startAnAuction marketplace HoldAnAuctionParams {..} = do - let ipfsCidHash = sha2_256 haapIpfsCid - nftStore <- Core.mdSingletons <$> marketplaceStore marketplace - nftEntry <- maybe (throwError "NFT has not been created") pure $ AssocMap.lookup ipfsCidHash nftStore - let tokenName = V.TokenName haapIpfsCid - let nftValue = V.singleton (nftEntry ^. Core._nftRecord . Core._niCurrency) tokenName 1 + let internalId = toInternalId haapItemId + nftStore <- marketplaceStore marketplace + auctionValue <- case internalId of + Left nftId@(Core.InternalNftId ipfsCidHash ipfsCid) -> + Core.nftValue ipfsCid <$> getNftEntry nftStore nftId + Right bundleId@(Core.InternalBundleId bundleHash cids) -> + Core.bundleValue cids <$> getBundleEntry nftStore bundleId currSlot <- currentSlot let endTime = currSlot + haapDuration - (auctionToken, auctionParams) <- mapError (T.pack . Haskell.show) $ Auction.startAuction nftValue endTime + (auctionToken, auctionParams) <- mapError (T.pack . Haskell.show) $ Auction.startAuction auctionValue endTime let client = Core.marketplaceClient marketplace let lot = Right $ Auction.toTuple auctionToken auctionParams - void $ mapError' $ runStep client $ Core.PutLotRedeemer (Left (ipfsCidHash, haapIpfsCid)) lot + void $ mapError' $ runStep client $ Core.PutLotRedeemer internalId lot logInfo @Haskell.String $ printf "Started an auction %s" (Haskell.show auctionParams) pure () @@ -211,26 +219,33 @@ startAnAuction marketplace HoldAnAuctionParams {..} = do -- | The user completes the auction for specified NFT completeAnAuction :: Core.Marketplace -> HoldAnAuctionParams -> Contract w s Text () completeAnAuction marketplace HoldAnAuctionParams {..} = do - let ipfsCidHash = sha2_256 haapIpfsCid - nftStore <- Core.mdSingletons <$> marketplaceStore marketplace - nftEntry <- maybe (throwError "NFT has not been created") pure $ AssocMap.lookup ipfsCidHash nftStore - nftAuction <- maybe (throwError "NFT has not been put on auction") pure $ - nftEntry ^. Core._nftLot ^? traverse . _2 . _Right - - let auctionToken = Auction.getStateToken nftAuction - let auctionParams = Auction.fromTuple nftAuction + let internalId = toInternalId haapItemId + nftStore <- marketplaceStore marketplace + auction <- case internalId of + Left nftId@(Core.InternalNftId ipfsCidHash ipfsCid) -> do + nftEntry <- getNftEntry nftStore nftId + maybe (throwError "NFT has not been put on auction") pure $ + nftEntry ^. Core._nftLot ^? traverse . _2 . _Right + Right bundleId@(Core.InternalBundleId bundleHash cids) -> do + bundleEntry <- getBundleEntry nftStore bundleId + maybe (throwError "Bundle has not been put on auction") pure $ + bundleEntry ^. Core._nbTokens ^? Core._HasLot . _2 . _Right + + let auctionToken = Auction.getStateToken auction + let auctionParams = Auction.fromTuple auction _ <- mapError (T.pack . Haskell.show) $ Auction.payoutAuction auctionToken auctionParams let client = Core.marketplaceClient marketplace - void $ mapError' $ runStep client $ Core.RemoveLotRedeemer (Left ipfsCidHash) + void $ mapError' $ runStep client $ Core.RemoveLotRedeemer $ + Lens.bimap Core.iniIpfsCidHash Core.ibiBundleId internalId - logInfo @Haskell.String $ printf "Completed an auction %s" (Haskell.show nftAuction) + logInfo @Haskell.String $ printf "Completed an auction %s" (Haskell.show auction) pure () data BidOnAuctionParams = BidOnAuctionParams { - boapIpfsCid :: ByteString, - boapBid :: Ada + boapItemId :: UserItemId, + boapBid :: Ada } deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON, Schema.ToSchema) @@ -241,17 +256,23 @@ PlutusTx.makeLift ''BidOnAuctionParams -- | The user submits a bid on the auction for specified NFT bidOnAuction :: Core.Marketplace -> BidOnAuctionParams -> Contract w s Text () bidOnAuction marketplace BidOnAuctionParams {..} = do - let ipfsCidHash = sha2_256 boapIpfsCid - nftStore <- Core.mdSingletons <$> marketplaceStore marketplace - nftEntry <- maybe (throwError "NFT has not been created") pure $ AssocMap.lookup ipfsCidHash nftStore - nftAuction <- maybe (throwError "NFT has not been put on auction") pure $ - nftEntry ^. Core._nftLot ^? traverse . _2 . _Right - - let auctionToken = Auction.getStateToken nftAuction - let auctionParams = Auction.fromTuple nftAuction + let internalId = toInternalId boapItemId + nftStore <- marketplaceStore marketplace + auction <- case internalId of + Left nftId@(Core.InternalNftId ipfsCidHash ipfsCid) -> do + nftEntry <- getNftEntry nftStore nftId + maybe (throwError "NFT has not been put on auction") pure $ + nftEntry ^. Core._nftLot ^? traverse . _2 . _Right + Right bundleId@(Core.InternalBundleId bundleHash cids) -> do + bundleEntry <- getBundleEntry nftStore bundleId + maybe (throwError "Bundle has not been put on auction") pure $ + bundleEntry ^. Core._nbTokens ^? Core._HasLot . _2 . _Right + + let auctionToken = Auction.getStateToken auction + let auctionParams = Auction.fromTuple auction _ <- mapError (T.pack . Haskell.show) $ Auction.submitBid auctionToken auctionParams boapBid - logInfo @Haskell.String $ printf "Submitted bid for NFT auction %s" (Haskell.show nftAuction) + logInfo @Haskell.String $ printf "Submitted bid for auction %s" (Haskell.show auction) pure () data BundleUpParams = @@ -314,8 +335,8 @@ ownPubKeyBalance = getOwnPubKey >>= fundsAt type MarketplaceUserSchema = Endpoint "createNft" CreateNftParams .\/ Endpoint "openSale" OpenSaleParams - .\/ Endpoint "buyNft" BuyNftParams - .\/ Endpoint "closeSale" CloseSaleParams + .\/ Endpoint "buyNft" CompleteSaleParams + .\/ Endpoint "closeSale" CompleteSaleParams .\/ Endpoint "startAnAuction" HoldAnAuctionParams .\/ Endpoint "completeAnAuction" HoldAnAuctionParams .\/ Endpoint "bidOnAuction" BidOnAuctionParams diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/ID.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/ID.hs index ec5717dde..b8e661675 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/ID.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/ID.hs @@ -48,9 +48,17 @@ data InternalNftId = InternalNftId { deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON) +PlutusTx.unstableMakeIsData ''InternalNftId + +PlutusTx.makeLift ''InternalNftId + data InternalBundleId = InternalBundleId { - ibiIpfsCids :: !(AssocMap.Map IpfsCidHash IpfsCid), - ibiBundleId :: !BundleId + ibiBundleId :: !BundleId, + ibiIpfsCids :: !(AssocMap.Map IpfsCidHash IpfsCid) } deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON) + +PlutusTx.unstableMakeIsData ''InternalBundleId + +PlutusTx.makeLift ''InternalBundleId diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs index 2103114a9..05de56a00 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs @@ -156,3 +156,7 @@ hasLotBundle :: NftBundle -> Bool hasLotBundle bundle = case nbTokens bundle of HasLot _ _ -> True _ -> False + +{-# INLINABLE nftValue #-} +nftValue :: IpfsCid -> NFT -> Value +nftValue ipfsCid nft = V.singleton (niCurrency $ nftRecord nft) (V.TokenName ipfsCid) 1 diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs index cdb2c4937..fee7822ec 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs @@ -26,6 +26,7 @@ import qualified Ledger.Typed.Scripts as Scripts import qualified Ledger.Value as V import Plutus.Contract import Plutus.Contract.StateMachine +import Plutus.Contracts.NftMarketplace.OnChain.Core.ID import Plutus.Contracts.NftMarketplace.OnChain.Core.NFT import qualified Plutus.Contracts.Services.Sale as Sale import qualified PlutusTx @@ -48,7 +49,7 @@ PlutusTx.makeLift ''Marketplace -- TODO make sum types for eithers (?) data MarketplaceRedeemer = CreateNftRedeemer IpfsCidHash NftInfo - | PutLotRedeemer (Either (IpfsCidHash, IpfsCid) (BundleId, AssocMap.Map IpfsCidHash IpfsCid)) LotLink + | PutLotRedeemer (Either InternalNftId InternalBundleId) LotLink | RemoveLotRedeemer (Either IpfsCidHash BundleId) | BundleUpRedeemer [IpfsCidHash] BundleId BundleInfo | UnbundleRedeemer BundleId @@ -139,13 +140,13 @@ transition marketplace state redeemer = case redeemer of -> Just ( mustBeSignedByIssuer nftEntry , State (insertNft ipfsCidHash (NFT nftEntry Nothing) nftStore) currStateValue ) - PutLotRedeemer (Left (ipfsCidHash, ipfsCid)) lot + PutLotRedeemer (Left (InternalNftId ipfsCidHash ipfsCid)) lot -> let newEntry = maybe (traceError "NFT has not been created.") (_nftLot ?~ (ipfsCid, lot)) $ AssocMap.lookup ipfsCidHash $ mdSingletons nftStore in Just ( mempty , State (insertNft ipfsCidHash newEntry nftStore) currStateValue ) - PutLotRedeemer (Right (bundleId, cids)) lot + PutLotRedeemer (Right (InternalBundleId bundleId cids)) lot -> let newEntry = maybe (traceError "Bundle has not been created.") (addLotToBundle cids lot) $ AssocMap.lookup bundleId $ mdBundles nftStore in Just ( mempty @@ -191,18 +192,17 @@ stateTransitionCheck nftStore (CreateNftRedeemer ipfsCidHash nftEntry) ctx = traceIfFalse "CreateNftRedeemer: " $ traceIfFalse "NFT entry already exists" $ isNothing $ AssocMap.lookup ipfsCidHash $ nftUnion nftStore -stateTransitionCheck MarketplaceDatum {..} (PutLotRedeemer (Left (ipfsCidHash, ipfsCid)) lot) ctx = +stateTransitionCheck MarketplaceDatum {..} (PutLotRedeemer (Left (InternalNftId ipfsCidHash ipfsCid)) lot) ctx = traceIfFalse "PutLotRedeemer: " $ let nftEntry = fromMaybe (traceError "NFT has not been created") $ AssocMap.lookup ipfsCidHash mdSingletons lotValue = either Sale.saleValue (Auction.apAsset . Auction.fromTuple) lot - nftValue = V.singleton (niCurrency $ nftRecord nftEntry) (V.TokenName ipfsCid) 1 - hasBeenPutOnSale = lotValue == nftValue + hasBeenPutOnSale = lotValue == nftValue ipfsCid nftEntry isValidHash = sha2_256 ipfsCid == ipfsCidHash hasNoExistingLot = isNothing $ nftLot nftEntry in traceIfFalse "NFT has not been put on sale or auction" hasBeenPutOnSale && traceIfFalse "Invalid IPFS Cid Hash" isValidHash && traceIfFalse "NFT already has a lot" hasNoExistingLot -stateTransitionCheck MarketplaceDatum {..} (PutLotRedeemer (Right (bundleId, cids)) lot) ctx = +stateTransitionCheck MarketplaceDatum {..} (PutLotRedeemer (Right (InternalBundleId bundleId cids)) lot) ctx = traceIfFalse "PutLotRedeemer: " $ let bundle = fromMaybe (traceError "Bundle has not been created") $ AssocMap.lookup bundleId mdBundles lotValue = either Sale.saleValue (Auction.apAsset . Auction.fromTuple) lot diff --git a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs index ad591b274..3d490e79a 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs @@ -119,7 +119,7 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do _ <- Simulator.callEndpointOnInstance userCid "openSale" $ Marketplace.OpenSaleParams { - ospIpfsCid = catTokenIpfsCid, + ospItemId = Marketplace.UserNftId catTokenIpfsCid, ospSalePrice = 44*oneAdaInLovelace } sale <- flip Simulator.waitForState userCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of @@ -131,8 +131,8 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do buyer = pubKeyHash . walletPubKey $ Wallet 3 _ <- - Simulator.callEndpointOnInstance buyerCid "buyNft" Marketplace.BuyNftParams { - bnpIpfsCid = catTokenIpfsCid + Simulator.callEndpointOnInstance buyerCid "buyNft" Marketplace.CompleteSaleParams { + cspItemId = Marketplace.UserNftId catTokenIpfsCid } _ <- flip Simulator.waitForState buyerCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of Success (ContractSuccess Marketplace.NftBought) -> Just () @@ -156,7 +156,7 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do _ <- Simulator.callEndpointOnInstance userCid "openSale" $ Marketplace.OpenSaleParams { - ospIpfsCid = photoTokenIpfsCid, + ospItemId = Marketplace.UserNftId photoTokenIpfsCid, ospSalePrice = 12*oneAdaInLovelace } sale <- flip Simulator.waitForState userCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of @@ -166,8 +166,8 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do _ <- Simulator.callEndpointOnInstance userCid "closeSale" - Marketplace.CloseSaleParams { - cspIpfsCid = photoTokenIpfsCid + Marketplace.CompleteSaleParams { + cspItemId = Marketplace.UserNftId photoTokenIpfsCid } sale <- flip Simulator.waitForState userCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of Success (ContractSuccess Marketplace.ClosedSale) -> Just () @@ -175,7 +175,7 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do Simulator.logString @(Builtin MarketplaceContracts) $ "Successful closeSale" let auction = Marketplace.HoldAnAuctionParams { - haapIpfsCid = photoTokenIpfsCid, + haapItemId = Marketplace.UserNftId photoTokenIpfsCid, haapDuration = 80 } _ <- @@ -187,7 +187,7 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do _ <- Simulator.callEndpointOnInstance buyerCid "bidOnAuction" Marketplace.BidOnAuctionParams { - boapIpfsCid = photoTokenIpfsCid, + boapItemId = Marketplace.UserNftId photoTokenIpfsCid, boapBid = fromInteger $ 15*oneAdaInLovelace } _ <- flip Simulator.waitForState buyerCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of @@ -195,7 +195,7 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do _ -> Nothing Simulator.logString @(Builtin MarketplaceContracts) $ "Successful bidOnAuction" - _ <- Simulator.callEndpointOnInstance cidInfo "getAuctionState" photoTokenIpfsCid + _ <- Simulator.callEndpointOnInstance cidInfo "getAuctionState" $ Marketplace.UserNftId photoTokenIpfsCid s <- flip Simulator.waitForState cidInfo $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.InfoContractState)) of Success (ContractSuccess (Marketplace.AuctionState s)) -> Just s _ -> Nothing From 98a529bfe121ce44e29ef81e105fec439dc15833 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Sat, 14 Aug 2021 17:11:15 +0700 Subject: [PATCH 047/217] add test suite --- MetaLamp/nft-marketplace/plutus-starter.cabal | 43 +++++++++++++++++++ MetaLamp/nft-marketplace/test/Main.hs | 14 ++++++ .../test/Marketplace/Spec/Start.hs | 19 ++++++++ 3 files changed, 76 insertions(+) create mode 100644 MetaLamp/nft-marketplace/test/Main.hs create mode 100644 MetaLamp/nft-marketplace/test/Marketplace/Spec/Start.hs diff --git a/MetaLamp/nft-marketplace/plutus-starter.cabal b/MetaLamp/nft-marketplace/plutus-starter.cabal index 0d1ad4c0b..666d271b9 100644 --- a/MetaLamp/nft-marketplace/plutus-starter.cabal +++ b/MetaLamp/nft-marketplace/plutus-starter.cabal @@ -60,3 +60,46 @@ executable pab-simulation build-depends: base >= 4.9 && < 5, plutus-starter + +test-suite test + type: exitcode-stdio-1.0 + main-is: Main.hs + hs-source-dirs: test + other-modules: + Marketplace.Spec.Start + default-language: Haskell2010 + ghc-options: -Wall -Wnoncanonical-monad-instances + -Wincomplete-uni-patterns -Wincomplete-record-updates + -Wredundant-constraints -Widentities -rtsopts + -- See Plutus Tx readme + -fobject-code -fno-ignore-interface-pragmas -fno-omit-interface-pragmas + build-depends: + plutus-core -any, + plutus-tx -any, + plutus-contract -any, + plutus-ledger -any, + plutus-starter, + plutus-ledger-api, + plutus-tx-plugin + build-depends: + base >=4.9 && <5, + aeson -any, + bytestring -any, + containers -any, + data-default -any, + freer-extras -any, + hedgehog -any, + prettyprinter -any, + tasty -any, + tasty-hunit -any, + tasty-hedgehog >=0.2.0.0, + tasty-golden -any, + tasty-quickcheck -any, + text -any, + lens -any, + mtl -any, + row-types -any, + QuickCheck -any, + freer-simple -any, + foldl -any, + streaming -any diff --git a/MetaLamp/nft-marketplace/test/Main.hs b/MetaLamp/nft-marketplace/test/Main.hs new file mode 100644 index 000000000..992722636 --- /dev/null +++ b/MetaLamp/nft-marketplace/test/Main.hs @@ -0,0 +1,14 @@ +module Main + ( main + ) where + +import qualified Marketplace.Spec.Start as Start +import Test.Tasty + +main :: IO () +main = defaultMain tests + +tests :: TestTree +tests = testGroup "NFT Marketplace" + [ Start.tests + ] diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Start.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Start.hs new file mode 100644 index 000000000..7b7f04ec1 --- /dev/null +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Start.hs @@ -0,0 +1,19 @@ +{-# LANGUAGE FlexibleContexts #-} + +module Marketplace.Spec.Start + ( tests + ) where + +import Plutus.Contract.Test +import Test.Tasty + +tests :: TestTree +tests = + testGroup + "start" + [ checkPredicateOptions + options + "Should start a new marketplace with empty store" + datumsCheck + startTrace + ] From 2846c5417483c09fa5a7f23eced3ce6cb79caae3 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Sat, 14 Aug 2021 19:52:02 +0700 Subject: [PATCH 048/217] add test for start --- MetaLamp/nft-marketplace/plutus-starter.cabal | 2 +- .../test/Marketplace/Fixtures.hs | 5 ++ .../test/Marketplace/Fixtures/Wallet.hs | 15 +++++ .../test/Marketplace/Spec/Start.hs | 57 ++++++++++++++++++- 4 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 MetaLamp/nft-marketplace/test/Marketplace/Fixtures.hs create mode 100644 MetaLamp/nft-marketplace/test/Marketplace/Fixtures/Wallet.hs diff --git a/MetaLamp/nft-marketplace/plutus-starter.cabal b/MetaLamp/nft-marketplace/plutus-starter.cabal index 666d271b9..75a6a192f 100644 --- a/MetaLamp/nft-marketplace/plutus-starter.cabal +++ b/MetaLamp/nft-marketplace/plutus-starter.cabal @@ -66,7 +66,7 @@ test-suite test main-is: Main.hs hs-source-dirs: test other-modules: - Marketplace.Spec.Start + Marketplace.Spec.Start Marketplace.Fixtures.Wallet Marketplace.Fixtures default-language: Haskell2010 ghc-options: -Wall -Wnoncanonical-monad-instances -Wincomplete-uni-patterns -Wincomplete-record-updates diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures.hs b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures.hs new file mode 100644 index 000000000..06230de55 --- /dev/null +++ b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures.hs @@ -0,0 +1,5 @@ +module Marketplace.Fixtures + ( module Export + ) where + +import Marketplace.Fixtures.Wallet as Export diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/Wallet.hs b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/Wallet.hs new file mode 100644 index 000000000..c7c0fcee2 --- /dev/null +++ b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/Wallet.hs @@ -0,0 +1,15 @@ +module Marketplace.Fixtures.Wallet where + +import Wallet.Emulator.Wallet (Wallet (..)) + +ownerWallet :: Wallet +ownerWallet = Wallet 1 + +userWallet :: Wallet +userWallet = Wallet 2 + +buyerWallet :: Wallet +buyerWallet = Wallet 3 + +userWallets :: [Wallet] +userWallets = [userWallet, buyerWallet] diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Start.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Start.hs index 7b7f04ec1..5d57bc6db 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Start.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Start.hs @@ -1,11 +1,27 @@ -{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE NumericUnderscores #-} +{-# LANGUAGE OverloadedStrings #-} module Marketplace.Spec.Start ( tests ) where -import Plutus.Contract.Test -import Test.Tasty +import Control.Lens ((&), (.~)) +import qualified Control.Lens as Lens +import Control.Monad (void) +import qualified Data.Map as Map +import Data.Text (Text) +import Ledger +import qualified Ledger.Ada as Ada +import qualified Ledger.Value as V +import qualified Marketplace.Fixtures as Fixtures +import Plutus.Contract +import Plutus.Contract.Test +import qualified Plutus.Contracts.NftMarketplace.Endpoints as Marketplace +import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace +import qualified Plutus.Trace as Trace +import qualified PlutusTx.AssocMap as AssocMap +import Test.Tasty tests :: TestTree tests = @@ -17,3 +33,38 @@ tests = datumsCheck startTrace ] + +startTrace :: Trace.EmulatorTrace () +startTrace = do + _ <- Trace.activateContractWallet Fixtures.ownerWallet $ void startContract + _ <- Trace.waitNSlots 5 + pure () + +startContract :: + Contract () Marketplace.MarketplaceOwnerSchema Text Marketplace.Marketplace +startContract = Marketplace.start' $ pure marketplaceSymbol + +datumsCheck :: TracePredicate +datumsCheck = + dataAtAddress + (Marketplace.marketplaceAddress marketplace) + (== Marketplace.MarketplaceDatum AssocMap.empty AssocMap.empty) + +options :: CheckOptions +options = defaultCheckOptions & emulatorConfig .~ emulatorCfg + where + emulatorCfg :: Trace.EmulatorConfig + emulatorCfg = + Trace.EmulatorConfig $ Left $ Map.singleton Fixtures.ownerWallet v + v :: Value + v = + Ada.lovelaceValueOf 1000 _000_000 <> + V.singleton marketplaceSymbol Marketplace.marketplaceProtocolName 1 + +marketplace :: Marketplace.Marketplace +marketplace = + Marketplace.Marketplace $ + V.assetClass marketplaceSymbol Marketplace.marketplaceProtocolName + +marketplaceSymbol :: CurrencySymbol +marketplaceSymbol = "ff" From a83cfd97ed59667f716f4f976d1adae063e45938 Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Mon, 16 Aug 2021 18:30:57 +0700 Subject: [PATCH 049/217] add marketplace value check --- MetaLamp/nft-marketplace/plutus-starter.cabal | 2 +- .../test/Marketplace/Fixtures.hs | 4 ++- .../test/Marketplace/Fixtures/CheckOptions.hs | 35 +++++++++++++++++++ .../test/Marketplace/Fixtures/Script.hs | 32 +++++++++++++++++ .../test/Marketplace/Spec/Start.hs | 33 ++++++----------- 5 files changed, 81 insertions(+), 25 deletions(-) create mode 100644 MetaLamp/nft-marketplace/test/Marketplace/Fixtures/CheckOptions.hs create mode 100644 MetaLamp/nft-marketplace/test/Marketplace/Fixtures/Script.hs diff --git a/MetaLamp/nft-marketplace/plutus-starter.cabal b/MetaLamp/nft-marketplace/plutus-starter.cabal index 75a6a192f..f831e0820 100644 --- a/MetaLamp/nft-marketplace/plutus-starter.cabal +++ b/MetaLamp/nft-marketplace/plutus-starter.cabal @@ -66,7 +66,7 @@ test-suite test main-is: Main.hs hs-source-dirs: test other-modules: - Marketplace.Spec.Start Marketplace.Fixtures.Wallet Marketplace.Fixtures + Marketplace.Spec.Start Marketplace.Fixtures.Wallet Marketplace.Fixtures Marketplace.Fixtures.CheckOptions Marketplace.Fixtures.Script default-language: Haskell2010 ghc-options: -Wall -Wnoncanonical-monad-instances -Wincomplete-uni-patterns -Wincomplete-record-updates diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures.hs b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures.hs index 06230de55..3563e8ea7 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures.hs @@ -2,4 +2,6 @@ module Marketplace.Fixtures ( module Export ) where -import Marketplace.Fixtures.Wallet as Export +import Marketplace.Fixtures.CheckOptions as Export +import Marketplace.Fixtures.Script as Export +import Marketplace.Fixtures.Wallet as Export diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/CheckOptions.hs b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/CheckOptions.hs new file mode 100644 index 000000000..350fefd82 --- /dev/null +++ b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/CheckOptions.hs @@ -0,0 +1,35 @@ +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE NumericUnderscores #-} +{-# LANGUAGE OverloadedStrings #-} + +module Marketplace.Fixtures.CheckOptions where + +import Control.Lens ((&), (.~)) +import qualified Control.Lens as Lens +import Control.Monad (void) +import qualified Data.Map as Map +import Data.Text (Text) +import Ledger +import qualified Ledger.Ada as Ada +import qualified Ledger.Value as V +import qualified Marketplace.Fixtures.Script as Fixtures +import qualified Marketplace.Fixtures.Wallet as Fixtures +import Plutus.Contract +import Plutus.Contract.Test +import qualified Plutus.Contracts.NftMarketplace.Endpoints as Marketplace +import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace +import qualified Plutus.Trace as Trace +import qualified PlutusTx.AssocMap as AssocMap +import Test.Tasty + +options :: CheckOptions +options = defaultCheckOptions & emulatorConfig .~ emulatorCfg + where + emulatorCfg :: Trace.EmulatorConfig + emulatorCfg = + Trace.EmulatorConfig $ Left $ + Map.singleton Fixtures.ownerWallet v <> Map.fromList ((\w -> (w, Ada.lovelaceValueOf 1_000_000_000)) <$> Fixtures.userWallets) + v :: Value + v = + Ada.lovelaceValueOf 1_000_000_000 <> + V.singleton Fixtures.marketplaceSymbol Marketplace.marketplaceProtocolName 1 diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/Script.hs b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/Script.hs new file mode 100644 index 000000000..851b154e3 --- /dev/null +++ b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/Script.hs @@ -0,0 +1,32 @@ +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE NumericUnderscores #-} +{-# LANGUAGE OverloadedStrings #-} + +module Marketplace.Fixtures.Script where + +import Control.Lens ((&), (.~)) +import qualified Control.Lens as Lens +import Control.Monad (void) +import qualified Data.Map as Map +import Data.Text (Text) +import Ledger +import qualified Ledger.Ada as Ada +import qualified Ledger.Value as V +import Plutus.Contract +import Plutus.Contract.Test +import qualified Plutus.Contracts.NftMarketplace.Endpoints as Marketplace +import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace +import qualified Plutus.Trace as Trace +import qualified PlutusTx.AssocMap as AssocMap +import Test.Tasty + +marketplace :: Marketplace.Marketplace +marketplace = + Marketplace.Marketplace $ + V.assetClass marketplaceSymbol Marketplace.marketplaceProtocolName + +marketplaceSymbol :: CurrencySymbol +marketplaceSymbol = "ff" + +marketplaceAddress :: Address +marketplaceAddress = Marketplace.marketplaceAddress marketplace diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Start.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Start.hs index 5d57bc6db..f998e2516 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Start.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Start.hs @@ -28,43 +28,30 @@ tests = testGroup "start" [ checkPredicateOptions - options + Fixtures.options "Should start a new marketplace with empty store" - datumsCheck + (datumsCheck .&&. valueCheck) startTrace ] startTrace :: Trace.EmulatorTrace () startTrace = do _ <- Trace.activateContractWallet Fixtures.ownerWallet $ void startContract - _ <- Trace.waitNSlots 5 + -- _ <- Trace.waitNSlots 5 pure () startContract :: Contract () Marketplace.MarketplaceOwnerSchema Text Marketplace.Marketplace -startContract = Marketplace.start' $ pure marketplaceSymbol +startContract = Marketplace.start' $ pure Fixtures.marketplaceSymbol datumsCheck :: TracePredicate datumsCheck = dataAtAddress - (Marketplace.marketplaceAddress marketplace) + Fixtures.marketplaceAddress (== Marketplace.MarketplaceDatum AssocMap.empty AssocMap.empty) -options :: CheckOptions -options = defaultCheckOptions & emulatorConfig .~ emulatorCfg - where - emulatorCfg :: Trace.EmulatorConfig - emulatorCfg = - Trace.EmulatorConfig $ Left $ Map.singleton Fixtures.ownerWallet v - v :: Value - v = - Ada.lovelaceValueOf 1000 _000_000 <> - V.singleton marketplaceSymbol Marketplace.marketplaceProtocolName 1 - -marketplace :: Marketplace.Marketplace -marketplace = - Marketplace.Marketplace $ - V.assetClass marketplaceSymbol Marketplace.marketplaceProtocolName - -marketplaceSymbol :: CurrencySymbol -marketplaceSymbol = "ff" +valueCheck :: TracePredicate +valueCheck = + valueAtAddress + Fixtures.marketplaceAddress + (== V.singleton Fixtures.marketplaceSymbol Marketplace.marketplaceProtocolName 1) From 5cad6b835a76cb5fc065916fe2f260e2cdf2c519 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Tue, 17 Aug 2021 14:15:44 +0700 Subject: [PATCH 050/217] add create nft test --- MetaLamp/nft-marketplace/plutus-starter.cabal | 2 +- .../NftMarketplace/OffChain/Owner.hs | 4 + .../OnChain/Core/StateMachine.hs | 2 + MetaLamp/nft-marketplace/test/Main.hs | 4 +- .../test/Marketplace/Spec/CreateNft.hs | 93 +++++++++++++++++++ .../test/Marketplace/Spec/Start.hs | 4 +- MetaLamp/nft-marketplace/test/Utils.hs | 4 + MetaLamp/nft-marketplace/test/Utils/Data.hs | 17 ++++ MetaLamp/nft-marketplace/test/Utils/Trace.hs | 48 ++++++++++ 9 files changed, 174 insertions(+), 4 deletions(-) create mode 100644 MetaLamp/nft-marketplace/test/Marketplace/Spec/CreateNft.hs create mode 100644 MetaLamp/nft-marketplace/test/Utils.hs create mode 100644 MetaLamp/nft-marketplace/test/Utils/Data.hs create mode 100644 MetaLamp/nft-marketplace/test/Utils/Trace.hs diff --git a/MetaLamp/nft-marketplace/plutus-starter.cabal b/MetaLamp/nft-marketplace/plutus-starter.cabal index f831e0820..f379e49f8 100644 --- a/MetaLamp/nft-marketplace/plutus-starter.cabal +++ b/MetaLamp/nft-marketplace/plutus-starter.cabal @@ -66,7 +66,7 @@ test-suite test main-is: Main.hs hs-source-dirs: test other-modules: - Marketplace.Spec.Start Marketplace.Fixtures.Wallet Marketplace.Fixtures Marketplace.Fixtures.CheckOptions Marketplace.Fixtures.Script + Marketplace.Spec.Start Utils.Data Utils.Trace Utils Marketplace.Fixtures.Wallet Marketplace.Fixtures Marketplace.Fixtures.CheckOptions Marketplace.Fixtures.Script Marketplace.Spec.CreateNft default-language: Haskell2010 ghc-options: -Wall -Wnoncanonical-monad-instances -Wincomplete-uni-patterns -Wincomplete-record-updates diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Owner.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Owner.hs index b163ba0c4..bf9ce106d 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Owner.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Owner.hs @@ -4,10 +4,12 @@ {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeApplications #-} module Plutus.Contracts.NftMarketplace.OffChain.Owner where +import qualified Control.Lens as Lens import Control.Monad hiding (fmap) import qualified Data.Aeson as J import Data.Proxy (Proxy (..)) @@ -57,5 +59,7 @@ data OwnerContractState = Started Core.Marketplace deriving stock (Haskell.Eq, Haskell.Ord, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON) +Lens.makeClassyPrisms ''OwnerContractState + ownerEndpoints :: Contract (ContractResponse Text OwnerContractState) MarketplaceOwnerSchema Void () ownerEndpoints = forever $ withContractResponse (Proxy @"start") Started start diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs index fee7822ec..1d83794e2 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs @@ -72,6 +72,8 @@ PlutusTx.unstableMakeIsData ''MarketplaceDatum PlutusTx.makeLift ''MarketplaceDatum +Lens.makeClassy_ ''MarketplaceDatum + {-# INLINABLE insertNft #-} insertNft :: IpfsCidHash -> NFT -> MarketplaceDatum -> MarketplaceDatum diff --git a/MetaLamp/nft-marketplace/test/Main.hs b/MetaLamp/nft-marketplace/test/Main.hs index 992722636..34111f9bd 100644 --- a/MetaLamp/nft-marketplace/test/Main.hs +++ b/MetaLamp/nft-marketplace/test/Main.hs @@ -2,7 +2,8 @@ module Main ( main ) where -import qualified Marketplace.Spec.Start as Start +import qualified Marketplace.Spec.CreateNft as CreateNft +import qualified Marketplace.Spec.Start as Start import Test.Tasty main :: IO () @@ -11,4 +12,5 @@ main = defaultMain tests tests :: TestTree tests = testGroup "NFT Marketplace" [ Start.tests + , CreateNft.tests ] diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/CreateNft.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/CreateNft.hs new file mode 100644 index 000000000..037cee3a6 --- /dev/null +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/CreateNft.hs @@ -0,0 +1,93 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE NumericUnderscores #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE TypeApplications #-} +module Marketplace.Spec.CreateNft + ( tests + ) where + +import Control.Lens (_2, (&), (.~), + (^.), (^?)) +import qualified Control.Lens as Lens +import Control.Monad (void) +import qualified Data.Map as Map +import Data.Maybe (isNothing) +import Data.Text (Text) +import Ledger +import qualified Ledger.Ada as Ada +import qualified Ledger.Value as V +import qualified Marketplace.Fixtures as Fixtures +import qualified Marketplace.Spec.Start as Start +import Plutus.Contract +import Plutus.Contract.Test +import qualified Plutus.Contracts.NftMarketplace.Endpoints as Marketplace +import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace +import qualified Plutus.Trace as Trace +import qualified PlutusTx.AssocMap as AssocMap +import PlutusTx.Builtins (sha2_256) +import PlutusTx.Prelude (ByteString) +import Test.Tasty +import qualified Utils +import Wallet.Emulator.Wallet + +-- TODO add tests on cnpRevealIssuer +tests :: TestTree +tests = + testGroup + "createNft" + [ checkPredicateOptions + Fixtures.options + "Should mint NFT token into the user wallet and create the Marketplace entry" + (datumsCheck .&&. valueCheck) + createNftTrace + ] + +createNftTrace :: Trace.EmulatorTrace () +createNftTrace = do + _ <- Start.startTrace + h <- Trace.activateContractWallet Fixtures.userWallet $ Marketplace.userEndpoints Fixtures.marketplace + _ <- Trace.callEndpoint @"createNft" h Marketplace.CreateNftParams { + Marketplace.cnpIpfsCid = catTokenIpfsCid, + Marketplace.cnpNftName = "Cat token", + Marketplace.cnpNftDescription = "A picture of a cat on a pogo stick", + Marketplace.cnpNftCategory = ["GIFs"], + Marketplace.cnpRevealIssuer = False + } + _ <- Trace.waitNSlots 50 + pure () + +datumsCheck :: TracePredicate +datumsCheck = + dataAtAddress + Fixtures.marketplaceAddress + (containsNft . Marketplace.mdSingletons) + where + containsNft = maybe False (\t -> (t ^. Marketplace._nftLot & isNothing) && (t ^. Marketplace._nftRecord & hasCatTokenRecord)) . + (AssocMap.lookup catTokenIpfsCidHash) + +valueCheck :: TracePredicate +valueCheck = + valueAtAddress + (walletAddress Fixtures.userWallet) + (Utils.one hasNft . V.flattenValue) + where + hasNft v = (v ^. _2 & V.unTokenName) == catTokenIpfsCid + +catTokenIpfsCid :: Marketplace.IpfsCid +catTokenIpfsCid = "QmPeoJnaDttpFrSySYBY3reRFCzL3qv4Uiqz376EBv9W16" + +catTokenIpfsCidHash :: Marketplace.IpfsCidHash +catTokenIpfsCidHash = sha2_256 catTokenIpfsCid +catTokenName :: ByteString +catTokenName = "Cat token" +catTokenDescription :: ByteString +catTokenDescription = "A picture of a cat on a pogo stick" +catTokenCategory :: Marketplace.Category +catTokenCategory = ["GIFs"] +hasCatTokenRecord :: Marketplace.NftInfo -> Bool +hasCatTokenRecord Marketplace.NftInfo {..} = niCategory == catTokenCategory && niName == catTokenName && niDescription == catTokenDescription + +photoTokenIpfsCid :: Marketplace.IpfsCid +photoTokenIpfsCid = "QmeSFBsEZ7XtK7yv5CQ79tqFnH9V2jhFhSSq1LV5W3kuiB" diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Start.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Start.hs index f998e2516..61f15b74e 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Start.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Start.hs @@ -3,7 +3,7 @@ {-# LANGUAGE OverloadedStrings #-} module Marketplace.Spec.Start - ( tests + ( tests, startTrace ) where import Control.Lens ((&), (.~)) @@ -37,7 +37,7 @@ tests = startTrace :: Trace.EmulatorTrace () startTrace = do _ <- Trace.activateContractWallet Fixtures.ownerWallet $ void startContract - -- _ <- Trace.waitNSlots 5 + _ <- Trace.waitNSlots 50 pure () startContract :: diff --git a/MetaLamp/nft-marketplace/test/Utils.hs b/MetaLamp/nft-marketplace/test/Utils.hs new file mode 100644 index 000000000..ee68fe1c3 --- /dev/null +++ b/MetaLamp/nft-marketplace/test/Utils.hs @@ -0,0 +1,4 @@ +module Utils (module Export) where + +import Utils.Data as Export +import Utils.Trace as Export diff --git a/MetaLamp/nft-marketplace/test/Utils/Data.hs b/MetaLamp/nft-marketplace/test/Utils/Data.hs new file mode 100644 index 000000000..bde720418 --- /dev/null +++ b/MetaLamp/nft-marketplace/test/Utils/Data.hs @@ -0,0 +1,17 @@ +module Utils.Data where + +import Data.Function ((&)) +import Plutus.Abstract.ContractResponse (ContractResponse (..)) +import Plutus.V1.Ledger.Crypto (PubKeyHash, pubKeyHash) +import qualified PlutusTx.AssocMap as AssocMap +import qualified PlutusTx.Prelude as PlutusTx +import Wallet.Emulator.Wallet (Wallet, walletPubKey) + +allSatisfy :: [a -> Bool] -> a -> Bool +allSatisfy fs a = and . fmap (a &) $ fs + +one :: (a -> Bool) -> [a] -> Bool +one f = foldr reducer False + where + reducer cur acc = if acc then not . f $ cur else f cur + diff --git a/MetaLamp/nft-marketplace/test/Utils/Trace.hs b/MetaLamp/nft-marketplace/test/Utils/Trace.hs new file mode 100644 index 000000000..b7d75d49c --- /dev/null +++ b/MetaLamp/nft-marketplace/test/Utils/Trace.hs @@ -0,0 +1,48 @@ +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE MonoLocalBinds #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeApplications #-} + +module Utils.Trace where + +import qualified Control.Foldl as L +import Control.Monad (unless) +import Control.Monad.Freer.Error (throwError) +import Control.Monad.Freer.Writer (tell) +import qualified Data.Aeson as JSON + +import qualified Data.Map as Map +import Data.Maybe (mapMaybe) +import Data.String (fromString) +import Data.Text.Prettyprint.Doc (Doc) +import Data.Void (Void) +import Ledger (Address) +import qualified Ledger +import Ledger.AddressMap (UtxoMap) +import Plutus.Abstract.ContractResponse (ContractResponse (..)) +import Plutus.Contract.Test (TracePredicate) +import qualified Plutus.Trace.Emulator as Trace +import Plutus.Trace.Emulator.Types (EmulatorRuntimeError (..)) +import PlutusTx (IsData, fromData) +import qualified Wallet.Emulator.Folds as Folds +import Wallet.Emulator.MultiAgent (EmulatorEvent) + +waitForState :: + (Show a + , Show e + , Trace.ContractConstraints s + , JSON.FromJSON e + , JSON.FromJSON a + , JSON.ToJSON e + , JSON.ToJSON a + , JSON.FromJSON e' + ) + => (a -> Maybe b) -> + Trace.ContractHandle (ContractResponse e a) s e' -> + Trace.EmulatorTrace b +waitForState pick userHandle = do + res <- Trace.observableState userHandle + case res of + ContractSuccess s -> maybe (throwError . GenericError $ "Unexpected state: " <> show s) pure (pick s) + ContractError e -> throwError . GenericError . show $ e + ContractPending -> Trace.waitNSlots 1 >> waitForState pick userHandle From 0b2408530b444bdb1288067a7dc61a1867e0b207 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Tue, 17 Aug 2021 15:18:39 +0700 Subject: [PATCH 051/217] add reveal issuer tests --- MetaLamp/nft-marketplace/plutus-starter.cabal | 2 +- .../Contracts/NftMarketplace/OffChain/User.hs | 7 ++ .../test/Marketplace/Fixtures.hs | 1 + .../test/Marketplace/Fixtures/NFT.hs | 54 ++++++++++++++ .../test/Marketplace/Spec/CreateNft.hs | 71 +++++++++++-------- MetaLamp/nft-marketplace/test/Utils/Data.hs | 2 + 6 files changed, 106 insertions(+), 31 deletions(-) create mode 100644 MetaLamp/nft-marketplace/test/Marketplace/Fixtures/NFT.hs diff --git a/MetaLamp/nft-marketplace/plutus-starter.cabal b/MetaLamp/nft-marketplace/plutus-starter.cabal index f379e49f8..910941b80 100644 --- a/MetaLamp/nft-marketplace/plutus-starter.cabal +++ b/MetaLamp/nft-marketplace/plutus-starter.cabal @@ -66,7 +66,7 @@ test-suite test main-is: Main.hs hs-source-dirs: test other-modules: - Marketplace.Spec.Start Utils.Data Utils.Trace Utils Marketplace.Fixtures.Wallet Marketplace.Fixtures Marketplace.Fixtures.CheckOptions Marketplace.Fixtures.Script Marketplace.Spec.CreateNft + Marketplace.Spec.Start Utils.Data Marketplace.Fixtures.NFT Utils.Trace Utils Marketplace.Fixtures.Wallet Marketplace.Fixtures Marketplace.Fixtures.CheckOptions Marketplace.Fixtures.Script Marketplace.Spec.CreateNft default-language: Haskell2010 ghc-options: -Wall -Wnoncanonical-monad-instances -Wincomplete-uni-patterns -Wincomplete-record-updates diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs index d8ac790a9..0cc2fec98 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs @@ -62,6 +62,7 @@ data CreateNftParams = PlutusTx.unstableMakeIsData ''CreateNftParams PlutusTx.makeLift ''CreateNftParams +Lens.makeClassy_ ''CreateNftParams -- | The user specifies which NFT to mint and add to marketplace store, -- he gets it into his wallet and the corresponding store entry is created @@ -100,6 +101,7 @@ data OpenSaleParams = PlutusTx.unstableMakeIsData ''OpenSaleParams PlutusTx.makeLift ''OpenSaleParams +Lens.makeClassy_ ''OpenSaleParams -- | The user opens sale for his NFT openSale :: Core.Marketplace -> OpenSaleParams -> Contract w s Text () @@ -134,6 +136,7 @@ data CompleteSaleParams = PlutusTx.unstableMakeIsData ''CompleteSaleParams PlutusTx.makeLift ''CompleteSaleParams +Lens.makeClassy_ ''CompleteSaleParams -- | The user buys specified NFT lot buyNft :: Core.Marketplace -> CompleteSaleParams -> Contract w s Text () @@ -193,6 +196,7 @@ data HoldAnAuctionParams = PlutusTx.unstableMakeIsData ''HoldAnAuctionParams PlutusTx.makeLift ''HoldAnAuctionParams +Lens.makeClassy_ ''HoldAnAuctionParams -- | The user starts an auction for specified NFT startAnAuction :: Core.Marketplace -> HoldAnAuctionParams -> Contract w s Text () @@ -252,6 +256,7 @@ data BidOnAuctionParams = PlutusTx.unstableMakeIsData ''BidOnAuctionParams PlutusTx.makeLift ''BidOnAuctionParams +Lens.makeClassy_ ''BidOnAuctionParams -- | The user submits a bid on the auction for specified NFT bidOnAuction :: Core.Marketplace -> BidOnAuctionParams -> Contract w s Text () @@ -287,6 +292,7 @@ data BundleUpParams = PlutusTx.unstableMakeIsData ''BundleUpParams PlutusTx.makeLift ''BundleUpParams +Lens.makeClassy_ ''BundleUpParams -- | The user creates a bundle from specified NFTs bundleUp :: Core.Marketplace -> BundleUpParams -> Contract w s Text () @@ -314,6 +320,7 @@ data UnbundleParams = PlutusTx.unstableMakeIsData ''UnbundleParams PlutusTx.makeLift ''UnbundleParams +Lens.makeClassy_ ''UnbundleParams -- | The user unbundles specified NFTs unbundle :: Core.Marketplace -> UnbundleParams -> Contract w s Text () diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures.hs b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures.hs index 3563e8ea7..a36c83962 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures.hs @@ -3,5 +3,6 @@ module Marketplace.Fixtures ) where import Marketplace.Fixtures.CheckOptions as Export +import Marketplace.Fixtures.NFT as Export import Marketplace.Fixtures.Script as Export import Marketplace.Fixtures.Wallet as Export diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/NFT.hs b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/NFT.hs new file mode 100644 index 000000000..292a5c747 --- /dev/null +++ b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/NFT.hs @@ -0,0 +1,54 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE NumericUnderscores #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE TypeApplications #-} + +module Marketplace.Fixtures.NFT where + +import Control.Lens (_2, (&), (.~), + (^.), (^?)) +import qualified Control.Lens as Lens +import Control.Monad (void) +import qualified Data.Map as Map +import Data.Maybe (isNothing) +import Data.Text (Text) +import Ledger +import qualified Ledger.Ada as Ada +import qualified Ledger.Value as V +import Plutus.Contract +import Plutus.Contract.Test +import qualified Plutus.Contracts.NftMarketplace.Endpoints as Marketplace +import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace +import qualified Plutus.Trace as Trace +import qualified PlutusTx.AssocMap as AssocMap +import PlutusTx.Builtins (sha2_256) +import PlutusTx.Prelude (ByteString) +import Test.Tasty +import qualified Utils +import Wallet.Emulator.Wallet + +catTokenIpfsCid :: Marketplace.IpfsCid +catTokenIpfsCid = "QmPeoJnaDttpFrSySYBY3reRFCzL3qv4Uiqz376EBv9W16" + +catTokenIpfsCidHash :: Marketplace.IpfsCidHash +catTokenIpfsCidHash = sha2_256 catTokenIpfsCid + +catTokenName :: ByteString +catTokenName = "Cat token" + +catTokenDescription :: ByteString +catTokenDescription = "A picture of a cat on a pogo stick" + +catTokenCategory :: Marketplace.Category +catTokenCategory = ["GIFs"] + +hasCatTokenRecord :: Marketplace.NftInfo -> Bool +hasCatTokenRecord Marketplace.NftInfo {..} = + niCategory == catTokenCategory && + niName == catTokenName && + niDescription == catTokenDescription + +photoTokenIpfsCid :: Marketplace.IpfsCid +photoTokenIpfsCid = "QmeSFBsEZ7XtK7yv5CQ79tqFnH9V2jhFhSSq1LV5W3kuiB" diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/CreateNft.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/CreateNft.hs index 037cee3a6..1fe23aa6b 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Spec/CreateNft.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/CreateNft.hs @@ -32,29 +32,44 @@ import Test.Tasty import qualified Utils import Wallet.Emulator.Wallet --- TODO add tests on cnpRevealIssuer tests :: TestTree tests = testGroup "createNft" [ checkPredicateOptions Fixtures.options - "Should mint NFT token into the user wallet and create the Marketplace entry" + "Should mint NFT token into the user wallet and create the Marketplace entry hiding issuer" (datumsCheck .&&. valueCheck) - createNftTrace + createNftTrace, + checkPredicateOptions + Fixtures.options + "Should mint NFT token into the user wallet and create the Marketplace entry revealing issuer" + (datumsCheck' .&&. valueCheck) + createNftTrace' ] +createNftParams :: Marketplace.CreateNftParams +createNftParams = Marketplace.CreateNftParams { + Marketplace.cnpIpfsCid = Fixtures.catTokenIpfsCid, + Marketplace.cnpNftName = Fixtures.catTokenName, + Marketplace.cnpNftDescription = Fixtures.catTokenDescription, + Marketplace.cnpNftCategory = Fixtures.catTokenCategory, + Marketplace.cnpRevealIssuer = False + } + createNftTrace :: Trace.EmulatorTrace () createNftTrace = do _ <- Start.startTrace h <- Trace.activateContractWallet Fixtures.userWallet $ Marketplace.userEndpoints Fixtures.marketplace - _ <- Trace.callEndpoint @"createNft" h Marketplace.CreateNftParams { - Marketplace.cnpIpfsCid = catTokenIpfsCid, - Marketplace.cnpNftName = "Cat token", - Marketplace.cnpNftDescription = "A picture of a cat on a pogo stick", - Marketplace.cnpNftCategory = ["GIFs"], - Marketplace.cnpRevealIssuer = False - } + _ <- Trace.callEndpoint @"createNft" h createNftParams + _ <- Trace.waitNSlots 50 + pure () + +createNftTrace' :: Trace.EmulatorTrace () +createNftTrace' = do + _ <- Start.startTrace + h <- Trace.activateContractWallet Fixtures.userWallet $ Marketplace.userEndpoints Fixtures.marketplace + _ <- Trace.callEndpoint @"createNft" h $ createNftParams & Marketplace._cnpRevealIssuer .~ True _ <- Trace.waitNSlots 50 pure () @@ -64,8 +79,21 @@ datumsCheck = Fixtures.marketplaceAddress (containsNft . Marketplace.mdSingletons) where - containsNft = maybe False (\t -> (t ^. Marketplace._nftLot & isNothing) && (t ^. Marketplace._nftRecord & hasCatTokenRecord)) . - (AssocMap.lookup catTokenIpfsCidHash) + containsNft = maybe False (\t -> (t ^. Marketplace._nftLot & isNothing) && + (t ^. Marketplace._nftRecord . Marketplace._niIssuer & isNothing) && + (t ^. Marketplace._nftRecord & Fixtures.hasCatTokenRecord)) . + (AssocMap.lookup Fixtures.catTokenIpfsCidHash) + +datumsCheck' :: TracePredicate +datumsCheck' = + dataAtAddress + Fixtures.marketplaceAddress + (containsNft . Marketplace.mdSingletons) + where + containsNft = maybe False (\t -> (t ^. Marketplace._nftLot & isNothing) && + (t ^. Marketplace._nftRecord . Marketplace._niIssuer == Just (Utils.walletPkh Fixtures.userWallet)) && + (t ^. Marketplace._nftRecord & Fixtures.hasCatTokenRecord)) . + (AssocMap.lookup Fixtures.catTokenIpfsCidHash) valueCheck :: TracePredicate valueCheck = @@ -73,21 +101,4 @@ valueCheck = (walletAddress Fixtures.userWallet) (Utils.one hasNft . V.flattenValue) where - hasNft v = (v ^. _2 & V.unTokenName) == catTokenIpfsCid - -catTokenIpfsCid :: Marketplace.IpfsCid -catTokenIpfsCid = "QmPeoJnaDttpFrSySYBY3reRFCzL3qv4Uiqz376EBv9W16" - -catTokenIpfsCidHash :: Marketplace.IpfsCidHash -catTokenIpfsCidHash = sha2_256 catTokenIpfsCid -catTokenName :: ByteString -catTokenName = "Cat token" -catTokenDescription :: ByteString -catTokenDescription = "A picture of a cat on a pogo stick" -catTokenCategory :: Marketplace.Category -catTokenCategory = ["GIFs"] -hasCatTokenRecord :: Marketplace.NftInfo -> Bool -hasCatTokenRecord Marketplace.NftInfo {..} = niCategory == catTokenCategory && niName == catTokenName && niDescription == catTokenDescription - -photoTokenIpfsCid :: Marketplace.IpfsCid -photoTokenIpfsCid = "QmeSFBsEZ7XtK7yv5CQ79tqFnH9V2jhFhSSq1LV5W3kuiB" + hasNft v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCid diff --git a/MetaLamp/nft-marketplace/test/Utils/Data.hs b/MetaLamp/nft-marketplace/test/Utils/Data.hs index bde720418..4f54e18aa 100644 --- a/MetaLamp/nft-marketplace/test/Utils/Data.hs +++ b/MetaLamp/nft-marketplace/test/Utils/Data.hs @@ -15,3 +15,5 @@ one f = foldr reducer False where reducer cur acc = if acc then not . f $ cur else f cur +walletPkh :: Wallet -> PubKeyHash +walletPkh = pubKeyHash . walletPubKey From 6c7882502e9e3cb34a5346792d3718cdfacd86ce Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Tue, 17 Aug 2021 17:14:28 +0700 Subject: [PATCH 052/217] add bundle up test --- MetaLamp/nft-marketplace/plutus-starter.cabal | 2 +- MetaLamp/nft-marketplace/test/Main.hs | 2 + .../test/Marketplace/Fixtures/CheckOptions.hs | 1 + .../test/Marketplace/Fixtures/NFT.hs | 40 ++++++++ .../test/Marketplace/Spec/Bundles.hs | 97 +++++++++++++++++++ 5 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 MetaLamp/nft-marketplace/test/Marketplace/Spec/Bundles.hs diff --git a/MetaLamp/nft-marketplace/plutus-starter.cabal b/MetaLamp/nft-marketplace/plutus-starter.cabal index 910941b80..fa7c26c09 100644 --- a/MetaLamp/nft-marketplace/plutus-starter.cabal +++ b/MetaLamp/nft-marketplace/plutus-starter.cabal @@ -66,7 +66,7 @@ test-suite test main-is: Main.hs hs-source-dirs: test other-modules: - Marketplace.Spec.Start Utils.Data Marketplace.Fixtures.NFT Utils.Trace Utils Marketplace.Fixtures.Wallet Marketplace.Fixtures Marketplace.Fixtures.CheckOptions Marketplace.Fixtures.Script Marketplace.Spec.CreateNft + Marketplace.Spec.Start Utils.Data Marketplace.Fixtures.NFT Marketplace.Spec.Bundles Utils.Trace Utils Marketplace.Fixtures.Wallet Marketplace.Fixtures Marketplace.Fixtures.CheckOptions Marketplace.Fixtures.Script Marketplace.Spec.CreateNft default-language: Haskell2010 ghc-options: -Wall -Wnoncanonical-monad-instances -Wincomplete-uni-patterns -Wincomplete-record-updates diff --git a/MetaLamp/nft-marketplace/test/Main.hs b/MetaLamp/nft-marketplace/test/Main.hs index 34111f9bd..79cf22ec0 100644 --- a/MetaLamp/nft-marketplace/test/Main.hs +++ b/MetaLamp/nft-marketplace/test/Main.hs @@ -2,6 +2,7 @@ module Main ( main ) where +import qualified Marketplace.Spec.Bundles as Bundles import qualified Marketplace.Spec.CreateNft as CreateNft import qualified Marketplace.Spec.Start as Start import Test.Tasty @@ -13,4 +14,5 @@ tests :: TestTree tests = testGroup "NFT Marketplace" [ Start.tests , CreateNft.tests + , Bundles.tests ] diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/CheckOptions.hs b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/CheckOptions.hs index 350fefd82..38ae6eff2 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/CheckOptions.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/CheckOptions.hs @@ -24,6 +24,7 @@ import Test.Tasty options :: CheckOptions options = defaultCheckOptions & emulatorConfig .~ emulatorCfg + & maxSlot .~ 15_000 where emulatorCfg :: Trace.EmulatorConfig emulatorCfg = diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/NFT.hs b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/NFT.hs index 292a5c747..d799b84a3 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/NFT.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/NFT.hs @@ -29,6 +29,28 @@ import Test.Tasty import qualified Utils import Wallet.Emulator.Wallet +cids :: [Marketplace.IpfsCid] +cids = [catTokenIpfsCid, photoTokenIpfsCid] + +bundleId :: Marketplace.BundleId +bundleId = Marketplace.calcBundleIdHash cids + +bundleInfo :: Marketplace.BundleInfo +bundleInfo = Marketplace.BundleInfo + { biName = bundleName + , biDescription = bundleDescription + , biCategory = bundleCategory + } + +bundleName :: ByteString +bundleName = "Picture gallery" + +bundleDescription :: ByteString +bundleDescription = "Collection of visual media" + +bundleCategory :: Marketplace.Category +bundleCategory = ["User","Stan"] + catTokenIpfsCid :: Marketplace.IpfsCid catTokenIpfsCid = "QmPeoJnaDttpFrSySYBY3reRFCzL3qv4Uiqz376EBv9W16" @@ -52,3 +74,21 @@ hasCatTokenRecord Marketplace.NftInfo {..} = photoTokenIpfsCid :: Marketplace.IpfsCid photoTokenIpfsCid = "QmeSFBsEZ7XtK7yv5CQ79tqFnH9V2jhFhSSq1LV5W3kuiB" + +photoTokenIpfsCidHash :: Marketplace.IpfsCidHash +photoTokenIpfsCidHash = sha2_256 photoTokenIpfsCid + +photoTokenName :: ByteString +photoTokenName = "Photo token" + +photoTokenDescription :: ByteString +photoTokenDescription = "A picture of a sunset" + +photoTokenCategory :: Marketplace.Category +photoTokenCategory = ["Photos"] + +hasPhotoTokenRecord :: Marketplace.NftInfo -> Bool +hasPhotoTokenRecord Marketplace.NftInfo {..} = + niCategory == photoTokenCategory && + niName == photoTokenName && + niDescription == photoTokenDescription diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Bundles.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Bundles.hs new file mode 100644 index 000000000..3608acee2 --- /dev/null +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Bundles.hs @@ -0,0 +1,97 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE NumericUnderscores #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE TypeApplications #-} + +module Marketplace.Spec.Bundles + ( tests + ) where + +import Control.Lens (_2, (&), (.~), + (^.), (^?)) +import qualified Control.Lens as Lens +import Control.Monad (void) +import qualified Data.Map as Map +import Data.Maybe (isNothing) +import Data.Text (Text) +import Ledger +import qualified Ledger.Ada as Ada +import qualified Ledger.Value as V +import qualified Marketplace.Fixtures as Fixtures +import qualified Marketplace.Spec.Start as Start +import Plutus.Contract +import Plutus.Contract.Test +import qualified Plutus.Contracts.NftMarketplace.Endpoints as Marketplace +import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace +import qualified Plutus.Trace as Trace +import qualified PlutusTx.AssocMap as AssocMap +import PlutusTx.Builtins (sha2_256) +import PlutusTx.Prelude (ByteString) +import Test.Tasty +import qualified Utils +import Wallet.Emulator.Wallet + +-- Should unbunle +-- Should not create if NFT not minted +tests :: TestTree +tests = + testGroup + "Bundles" + [ + checkPredicateOptions + Fixtures.options + "Should create a bundle for two NFTs transforming Marketplace store" + datumsCheck + bundleUpTrace + ] + +bundleUpTrace :: Trace.EmulatorTrace () +bundleUpTrace = do + _ <- Start.startTrace + h <- Trace.activateContractWallet Fixtures.userWallet $ Marketplace.userEndpoints Fixtures.marketplace + + _ <- Trace.callEndpoint @"createNft" h + Marketplace.CreateNftParams { + Marketplace.cnpIpfsCid = Fixtures.catTokenIpfsCid, + Marketplace.cnpNftName = Fixtures.catTokenName, + Marketplace.cnpNftDescription = Fixtures.catTokenDescription, + Marketplace.cnpNftCategory = Fixtures.catTokenCategory, + Marketplace.cnpRevealIssuer = False + } + _ <- Trace.waitNSlots 50 + _ <- Trace.callEndpoint @"createNft" h + Marketplace.CreateNftParams { + Marketplace.cnpIpfsCid = Fixtures.photoTokenIpfsCid, + Marketplace.cnpNftName = Fixtures.photoTokenName, + Marketplace.cnpNftDescription = Fixtures.photoTokenDescription, + Marketplace.cnpNftCategory = Fixtures.photoTokenCategory, + Marketplace.cnpRevealIssuer = False + } + _ <- Trace.waitNSlots 50 + _ <- Trace.callEndpoint @"bundleUp" h + Marketplace.BundleUpParams { + bupIpfsCids = Fixtures.cids, + bupName = Fixtures.bundleName, + bupDescription = Fixtures.bundleDescription, + bupCategory = Fixtures.bundleCategory + } + + _ <- Trace.waitNSlots 50 + pure () + +datumsCheck :: TracePredicate +datumsCheck = + dataAtAddress + Fixtures.marketplaceAddress + (containsBundle . Marketplace.mdBundles) + where + containsBundle = maybe False checkBundle . + (AssocMap.lookup Fixtures.bundleId) + checkBundle b = maybe False containsNfts (b ^? Marketplace._nbTokens . Marketplace._NoLot) && + (b ^. Marketplace._nbRecord == Fixtures.bundleInfo) + containsNfts b = maybe False Fixtures.hasCatTokenRecord + (AssocMap.lookup Fixtures.catTokenIpfsCidHash b) && + maybe False Fixtures.hasPhotoTokenRecord + (AssocMap.lookup Fixtures.photoTokenIpfsCidHash b) From 57715ae9aac33e2a575be823d3a96b740a1e8397 Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Tue, 17 Aug 2021 20:27:06 +0700 Subject: [PATCH 053/217] add bundle up fail test --- .../src/Plutus/Abstract/ContractResponse.hs | 37 ++++++----- .../Contracts/NftMarketplace/OffChain/User.hs | 2 +- .../OnChain/Core/StateMachine.hs | 16 ++--- .../src/Plutus/PAB/Simulation.hs | 66 +++++++++---------- .../test/Marketplace/Spec/Bundles.hs | 36 +++++++--- MetaLamp/nft-marketplace/test/Utils/Trace.hs | 23 +++++-- 6 files changed, 108 insertions(+), 72 deletions(-) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Abstract/ContractResponse.hs b/MetaLamp/nft-marketplace/src/Plutus/Abstract/ContractResponse.hs index 61e32b826..7484f2967 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Abstract/ContractResponse.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Abstract/ContractResponse.hs @@ -1,14 +1,17 @@ -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE DeriveAnyClass #-} -{-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE DerivingStrategies #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE NoImplicitPrelude #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TypeApplications #-} -{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE FunctionalDependencies #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeFamilies #-} module Plutus.Abstract.ContractResponse where @@ -45,15 +48,17 @@ import Prelude (Monoid (..), Semigroup (..), import qualified Prelude import Text.Printf (printf) -data ContractResponse e a = ContractSuccess a | ContractError e | ContractPending +data ContractResponse e a = CrSuccess a | CrError e | CrPending deriving stock (Prelude.Eq, Show, Generic) deriving anyclass (ToJSON, FromJSON) +Lens.makeClassyPrisms ''ContractResponse + instance Semigroup (ContractResponse e a) where a <> b = b instance Monoid (ContractResponse e a) where - mempty = ContractPending + mempty = CrPending mappend = (<>) withContractResponse :: forall l a p r s. @@ -65,11 +70,11 @@ withContractResponse :: forall l a p r s. withContractResponse _ g c = do e <- runError $ do p <- endpoint @l - _ <- tell ContractPending + _ <- tell CrPending errorHandler `handleError` c p tell $ case e of - Left err -> ContractError err - Right a -> ContractSuccess $ g a + Left err -> CrError err + Right a -> CrSuccess $ g a errorHandler :: Text -> Contract w s Text b errorHandler e = do diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs index 0cc2fec98..a048b3d33 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs @@ -295,7 +295,7 @@ PlutusTx.makeLift ''BundleUpParams Lens.makeClassy_ ''BundleUpParams -- | The user creates a bundle from specified NFTs -bundleUp :: Core.Marketplace -> BundleUpParams -> Contract w s Text () +bundleUp :: forall w s. Core.Marketplace -> BundleUpParams -> Contract w s Text () bundleUp marketplace BundleUpParams {..} = do let bundleId = Core.calcBundleIdHash bupIpfsCids let nftIds = sha2_256 <$> bupIpfsCids diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs index 1d83794e2..2bb405506 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs @@ -95,16 +95,16 @@ nftUnion MarketplaceDatum{..} = foldr union mdSingletons $ fmap getNfts $ toList NoLot val -> fmap (\info -> NFT info Nothing) val HasLot val lot -> fmap (\(cid, info) -> NFT info (Just (cid, lot))) val -{-# INLINABLE bundleUp #-} -bundleUp :: [IpfsCidHash] -> BundleId -> BundleInfo -> MarketplaceDatum -> MarketplaceDatum -bundleUp nftIds bundleId bundleInfo MarketplaceDatum{..} = +{-# INLINABLE bundleUpDatum #-} +bundleUpDatum :: [IpfsCidHash] -> BundleId -> BundleInfo -> MarketplaceDatum -> MarketplaceDatum +bundleUpDatum nftIds bundleId bundleInfo MarketplaceDatum{..} = MarketplaceDatum { mdSingletons = foldr AssocMap.delete mdSingletons nftIds , mdBundles = AssocMap.insert bundleId (makeBundle mdSingletons nftIds bundleInfo) mdBundles } -{-# INLINABLE unbundle #-} -unbundle :: BundleId -> MarketplaceDatum -> MarketplaceDatum -unbundle bundleId MarketplaceDatum{..} = +{-# INLINABLE unbundleDatum #-} +unbundleDatum :: BundleId -> MarketplaceDatum -> MarketplaceDatum +unbundleDatum bundleId MarketplaceDatum{..} = MarketplaceDatum { mdSingletons = foldr insert mdSingletons $ AssocMap.toList tokens , mdBundles = AssocMap.delete bundleId mdBundles } @@ -168,11 +168,11 @@ transition marketplace state redeemer = case redeemer of ) BundleUpRedeemer nftIds bundleId bundleInfo -> Just ( mempty - , State (bundleUp nftIds bundleId bundleInfo nftStore) currStateValue + , State (bundleUpDatum nftIds bundleId bundleInfo nftStore) currStateValue ) UnbundleRedeemer bundleId -> Just ( mempty - , State (unbundle bundleId nftStore) currStateValue + , State (unbundleDatum bundleId nftStore) currStateValue ) _ -> trace "Invalid transition" Nothing where diff --git a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs index 3d490e79a..dd5dce9b3 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs @@ -79,8 +79,8 @@ activateContracts = do cidStart <- Simulator.activateContract ownerWallet MarketplaceStart _ <- Simulator.callEndpointOnInstance cidStart "start" () mp <- flip Simulator.waitForState cidStart $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.OwnerContractState)) of - Success (ContractSuccess (Marketplace.Started mp)) -> Just mp - _ -> Nothing + Success (CrSuccess (Marketplace.Started mp)) -> Just mp + _ -> Nothing Simulator.logString @(Builtin MarketplaceContracts) $ "Marketplace instance created: " ++ show mp cidInfo <- Simulator.activateContract ownerWallet $ MarketplaceInfo mp @@ -112,8 +112,8 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do cnpRevealIssuer = False } flip Simulator.waitForState userCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of - Success (ContractSuccess Marketplace.NftCreated) -> Just () - _ -> Nothing + Success (CrSuccess Marketplace.NftCreated) -> Just () + _ -> Nothing Simulator.logString @(Builtin MarketplaceContracts) $ "Successful createNft" _ <- @@ -123,8 +123,8 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do ospSalePrice = 44*oneAdaInLovelace } sale <- flip Simulator.waitForState userCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of - Success (ContractSuccess Marketplace.OpenedSale) -> Just () - _ -> Nothing + Success (CrSuccess Marketplace.OpenedSale) -> Just () + _ -> Nothing Simulator.logString @(Builtin MarketplaceContracts) $ "Successful openSale" let buyerCid = cidUser Map.! Wallet 3 @@ -135,8 +135,8 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do cspItemId = Marketplace.UserNftId catTokenIpfsCid } _ <- flip Simulator.waitForState buyerCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of - Success (ContractSuccess Marketplace.NftBought) -> Just () - _ -> Nothing + Success (CrSuccess Marketplace.NftBought) -> Just () + _ -> Nothing Simulator.logString @(Builtin MarketplaceContracts) $ "Successful buyNft" _ <- @@ -149,8 +149,8 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do cnpRevealIssuer = True } flip Simulator.waitForState userCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of - Success (ContractSuccess Marketplace.NftCreated) -> Just () - _ -> Nothing + Success (CrSuccess Marketplace.NftCreated) -> Just () + _ -> Nothing Simulator.logString @(Builtin MarketplaceContracts) $ "Successful createNft" _ <- @@ -160,8 +160,8 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do ospSalePrice = 12*oneAdaInLovelace } sale <- flip Simulator.waitForState userCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of - Success (ContractSuccess Marketplace.OpenedSale) -> Just () - _ -> Nothing + Success (CrSuccess Marketplace.OpenedSale) -> Just () + _ -> Nothing Simulator.logString @(Builtin MarketplaceContracts) $ "Successful openSale" _ <- @@ -170,8 +170,8 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do cspItemId = Marketplace.UserNftId photoTokenIpfsCid } sale <- flip Simulator.waitForState userCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of - Success (ContractSuccess Marketplace.ClosedSale) -> Just () - _ -> Nothing + Success (CrSuccess Marketplace.ClosedSale) -> Just () + _ -> Nothing Simulator.logString @(Builtin MarketplaceContracts) $ "Successful closeSale" let auction = Marketplace.HoldAnAuctionParams { @@ -181,8 +181,8 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do _ <- Simulator.callEndpointOnInstance userCid "startAnAuction" auction _ <- flip Simulator.waitForState userCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of - Success (ContractSuccess Marketplace.AuctionStarted) -> Just () - _ -> Nothing + Success (CrSuccess Marketplace.AuctionStarted) -> Just () + _ -> Nothing Simulator.logString @(Builtin MarketplaceContracts) $ "Started An Auction" _ <- @@ -191,21 +191,21 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do boapBid = fromInteger $ 15*oneAdaInLovelace } _ <- flip Simulator.waitForState buyerCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of - Success (ContractSuccess Marketplace.BidSubmitted) -> Just () - _ -> Nothing + Success (CrSuccess Marketplace.BidSubmitted) -> Just () + _ -> Nothing Simulator.logString @(Builtin MarketplaceContracts) $ "Successful bidOnAuction" _ <- Simulator.callEndpointOnInstance cidInfo "getAuctionState" $ Marketplace.UserNftId photoTokenIpfsCid s <- flip Simulator.waitForState cidInfo $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.InfoContractState)) of - Success (ContractSuccess (Marketplace.AuctionState s)) -> Just s - _ -> Nothing + Success (CrSuccess (Marketplace.AuctionState s)) -> Just s + _ -> Nothing Simulator.logString @(Builtin MarketplaceContracts) $ "Final auction state: " <> show s _ <- Simulator.callEndpointOnInstance buyerCid "completeAnAuction" auction _ <- flip Simulator.waitForState buyerCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of - Success (ContractSuccess Marketplace.AuctionComplete) -> Just () - _ -> Nothing + Success (CrSuccess Marketplace.AuctionComplete) -> Just () + _ -> Nothing Simulator.logString @(Builtin MarketplaceContracts) $ "Successful holdAnAuction" _ <- @@ -217,8 +217,8 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do bupCategory = ["User","Stan"] } flip Simulator.waitForState userCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of - Success (ContractSuccess Marketplace.Bundled) -> Just () - _ -> Nothing + Success (CrSuccess Marketplace.Bundled) -> Just () + _ -> Nothing Simulator.logString @(Builtin MarketplaceContracts) $ "Successful bundleUp" _ <- @@ -227,32 +227,32 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do upIpfsCids = [photoTokenIpfsCid,catTokenIpfsCid] } flip Simulator.waitForState userCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of - Success (ContractSuccess Marketplace.Unbundled) -> Just () - _ -> Nothing + Success (CrSuccess Marketplace.Unbundled) -> Just () + _ -> Nothing Simulator.logString @(Builtin MarketplaceContracts) $ "Successful unbundle" _ <- Simulator.callEndpointOnInstance cidInfo "fundsAt" buyer v <- flip Simulator.waitForState cidInfo $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.InfoContractState)) of - Success (ContractSuccess (Marketplace.FundsAt v)) -> Just v - _ -> Nothing + Success (CrSuccess (Marketplace.FundsAt v)) -> Just v + _ -> Nothing Simulator.logString @(Builtin MarketplaceContracts) $ "Final buyer funds: " <> show v _ <- Simulator.callEndpointOnInstance cidInfo "marketplaceStore" () marketplaceStore <- flip Simulator.waitForState cidInfo $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.InfoContractState)) of - Success (ContractSuccess (Marketplace.MarketplaceStore marketplaceStore)) -> Just marketplaceStore + Success (CrSuccess (Marketplace.MarketplaceStore marketplaceStore)) -> Just marketplaceStore _ -> Nothing Simulator.logString @(Builtin MarketplaceContracts) $ "Final marketplaceStore: " <> show marketplaceStore _ <- Simulator.callEndpointOnInstance cidInfo "marketplaceFunds" () v <- flip Simulator.waitForState cidInfo $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.InfoContractState)) of - Success (ContractSuccess (Marketplace.MarketplaceFunds v)) -> Just v - _ -> Nothing + Success (CrSuccess (Marketplace.MarketplaceFunds v)) -> Just v + _ -> Nothing Simulator.logString @(Builtin MarketplaceContracts) $ "Final marketplace funds: " <> show v _ <- Simulator.callEndpointOnInstance cidInfo "fundsAt" sender v <- flip Simulator.waitForState cidInfo $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.InfoContractState)) of - Success (ContractSuccess (Marketplace.FundsAt v)) -> Just v - _ -> Nothing + Success (CrSuccess (Marketplace.FundsAt v)) -> Just v + _ -> Nothing Simulator.logString @(Builtin MarketplaceContracts) $ "Final user funds: " <> show v _ <- liftIO getLine diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Bundles.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Bundles.hs index 3608acee2..f1b86c746 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Bundles.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Bundles.hs @@ -44,9 +44,22 @@ tests = Fixtures.options "Should create a bundle for two NFTs transforming Marketplace store" datumsCheck - bundleUpTrace + bundleUpTrace, + checkPredicateOptions + Fixtures.options + "Should not create a bundle if NFTs are not minted" + bundleErrorCheck + bundleErrorTrace ] +bundleUpParams :: Marketplace.BundleUpParams +bundleUpParams = Marketplace.BundleUpParams { + bupIpfsCids = Fixtures.cids, + bupName = Fixtures.bundleName, + bupDescription = Fixtures.bundleDescription, + bupCategory = Fixtures.bundleCategory + } + bundleUpTrace :: Trace.EmulatorTrace () bundleUpTrace = do _ <- Start.startTrace @@ -70,13 +83,17 @@ bundleUpTrace = do Marketplace.cnpRevealIssuer = False } _ <- Trace.waitNSlots 50 - _ <- Trace.callEndpoint @"bundleUp" h - Marketplace.BundleUpParams { - bupIpfsCids = Fixtures.cids, - bupName = Fixtures.bundleName, - bupDescription = Fixtures.bundleDescription, - bupCategory = Fixtures.bundleCategory - } + _ <- Trace.callEndpoint @"bundleUp" h bundleUpParams + + _ <- Trace.waitNSlots 50 + pure () + +bundleErrorTrace :: Trace.EmulatorTrace () +bundleErrorTrace = do + _ <- Start.startTrace + h <- Trace.activateContractWallet Fixtures.userWallet $ Marketplace.userEndpoints Fixtures.marketplace + + _ <- Trace.callEndpoint @"bundleUp" h bundleUpParams _ <- Trace.waitNSlots 50 pure () @@ -95,3 +112,6 @@ datumsCheck = (AssocMap.lookup Fixtures.catTokenIpfsCidHash b) && maybe False Fixtures.hasPhotoTokenRecord (AssocMap.lookup Fixtures.photoTokenIpfsCidHash b) + +bundleErrorCheck :: TracePredicate +bundleErrorCheck = Utils.assertCrError (Marketplace.userEndpoints Fixtures.marketplace) (Trace.walletInstanceTag Fixtures.userWallet) diff --git a/MetaLamp/nft-marketplace/test/Utils/Trace.hs b/MetaLamp/nft-marketplace/test/Utils/Trace.hs index b7d75d49c..565d9c488 100644 --- a/MetaLamp/nft-marketplace/test/Utils/Trace.hs +++ b/MetaLamp/nft-marketplace/test/Utils/Trace.hs @@ -1,6 +1,7 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE MonoLocalBinds #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RankNTypes #-} {-# LANGUAGE TypeApplications #-} module Utils.Trace where @@ -11,16 +12,20 @@ import Control.Monad.Freer.Error (throwError) import Control.Monad.Freer.Writer (tell) import qualified Data.Aeson as JSON +import Control.Lens (_2, (&), (.~), (^.), (^?)) +import qualified Control.Lens as Lens import qualified Data.Map as Map -import Data.Maybe (mapMaybe) +import Data.Maybe (isJust, mapMaybe) import Data.String (fromString) import Data.Text.Prettyprint.Doc (Doc) import Data.Void (Void) import Ledger (Address) import qualified Ledger import Ledger.AddressMap (UtxoMap) -import Plutus.Abstract.ContractResponse (ContractResponse (..)) -import Plutus.Contract.Test (TracePredicate) +import Plutus.Abstract.ContractResponse +import qualified Plutus.Contract as C +import Plutus.Contract.Test (TracePredicate, + assertAccumState) import qualified Plutus.Trace.Emulator as Trace import Plutus.Trace.Emulator.Types (EmulatorRuntimeError (..)) import PlutusTx (IsData, fromData) @@ -43,6 +48,12 @@ waitForState :: waitForState pick userHandle = do res <- Trace.observableState userHandle case res of - ContractSuccess s -> maybe (throwError . GenericError $ "Unexpected state: " <> show s) pure (pick s) - ContractError e -> throwError . GenericError . show $ e - ContractPending -> Trace.waitNSlots 1 >> waitForState pick userHandle + CrSuccess s -> maybe (throwError . GenericError $ "Unexpected state: " <> show s) pure (pick s) + CrError e -> throwError . GenericError . show $ e + CrPending -> Trace.waitNSlots 1 >> waitForState pick userHandle + +assertCrError :: forall e r s err a. (Show r, Show e) => + C.Contract (ContractResponse e r) s err a + -> Trace.ContractInstanceTag + -> TracePredicate +assertCrError c tag = assertAccumState c tag (isJust . (^? _CrError)) "Expected contract error but there was none" From 624ee99132f32111d2fe14df2a15a4710992b269 Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Tue, 17 Aug 2021 21:21:02 +0700 Subject: [PATCH 054/217] add unbundle tests --- .../Contracts/NftMarketplace/OffChain/User.hs | 4 ++ .../test/Marketplace/Spec/Bundles.hs | 68 +++++++++++++++---- 2 files changed, 59 insertions(+), 13 deletions(-) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs index a048b3d33..8569a9aa6 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs @@ -298,6 +298,8 @@ Lens.makeClassy_ ''BundleUpParams bundleUp :: forall w s. Core.Marketplace -> BundleUpParams -> Contract w s Text () bundleUp marketplace BundleUpParams {..} = do let bundleId = Core.calcBundleIdHash bupIpfsCids + bundles <- Core.mdBundles <$> marketplaceStore marketplace + when (isJust $ AssocMap.lookup bundleId bundles) $ throwError "Bundle entry already exists" let nftIds = sha2_256 <$> bupIpfsCids let bundleInfo = Core.BundleInfo { biName = bupName @@ -326,6 +328,8 @@ Lens.makeClassy_ ''UnbundleParams unbundle :: Core.Marketplace -> UnbundleParams -> Contract w s Text () unbundle marketplace UnbundleParams {..} = do let bundleId = Core.calcBundleIdHash upIpfsCids + bundles <- Core.mdBundles <$> marketplaceStore marketplace + when (isNothing $ AssocMap.lookup bundleId bundles) $ throwError "Bundle entry does not exist" let client = Core.marketplaceClient marketplace void $ mapError' $ runStep client $ Core.UnbundleRedeemer bundleId diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Bundles.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Bundles.hs index f1b86c746..162bb9439 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Bundles.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Bundles.hs @@ -13,14 +13,17 @@ import Control.Lens (_2, (&), (.~), (^.), (^?)) import qualified Control.Lens as Lens import Control.Monad (void) +import Data.Function (on) import qualified Data.Map as Map import Data.Maybe (isNothing) import Data.Text (Text) +import Data.Void import Ledger import qualified Ledger.Ada as Ada import qualified Ledger.Value as V import qualified Marketplace.Fixtures as Fixtures import qualified Marketplace.Spec.Start as Start +import Plutus.Abstract.ContractResponse import Plutus.Contract import Plutus.Contract.Test import qualified Plutus.Contracts.NftMarketplace.Endpoints as Marketplace @@ -33,8 +36,6 @@ import Test.Tasty import qualified Utils import Wallet.Emulator.Wallet --- Should unbunle --- Should not create if NFT not minted tests :: TestTree tests = testGroup @@ -43,13 +44,23 @@ tests = checkPredicateOptions Fixtures.options "Should create a bundle for two NFTs transforming Marketplace store" - datumsCheck - bundleUpTrace, + bundleDatumsCheck + (void bundleTrace), checkPredicateOptions Fixtures.options "Should not create a bundle if NFTs are not minted" - bundleErrorCheck - bundleErrorTrace + errorCheck + bundleErrorTrace, + checkPredicateOptions + Fixtures.options + "Should unbundle transforming Marketplace store" + unbundleDatumsCheck + unbundleTrace, + checkPredicateOptions + Fixtures.options + "Should not unbundle if bundle does not exist" + errorCheck + unbundleErrorTrace ] bundleUpParams :: Marketplace.BundleUpParams @@ -60,8 +71,8 @@ bundleUpParams = Marketplace.BundleUpParams { bupCategory = Fixtures.bundleCategory } -bundleUpTrace :: Trace.EmulatorTrace () -bundleUpTrace = do +bundleTrace :: Trace.EmulatorTrace (Trace.ContractHandle (ContractResponse Text Marketplace.UserContractState) Marketplace.MarketplaceUserSchema Void) +bundleTrace = do _ <- Start.startTrace h <- Trace.activateContractWallet Fixtures.userWallet $ Marketplace.userEndpoints Fixtures.marketplace @@ -86,7 +97,7 @@ bundleUpTrace = do _ <- Trace.callEndpoint @"bundleUp" h bundleUpParams _ <- Trace.waitNSlots 50 - pure () + pure h bundleErrorTrace :: Trace.EmulatorTrace () bundleErrorTrace = do @@ -98,8 +109,8 @@ bundleErrorTrace = do _ <- Trace.waitNSlots 50 pure () -datumsCheck :: TracePredicate -datumsCheck = +bundleDatumsCheck :: TracePredicate +bundleDatumsCheck = dataAtAddress Fixtures.marketplaceAddress (containsBundle . Marketplace.mdBundles) @@ -113,5 +124,36 @@ datumsCheck = maybe False Fixtures.hasPhotoTokenRecord (AssocMap.lookup Fixtures.photoTokenIpfsCidHash b) -bundleErrorCheck :: TracePredicate -bundleErrorCheck = Utils.assertCrError (Marketplace.userEndpoints Fixtures.marketplace) (Trace.walletInstanceTag Fixtures.userWallet) +errorCheck :: TracePredicate +errorCheck = Utils.assertCrError (Marketplace.userEndpoints Fixtures.marketplace) (Trace.walletInstanceTag Fixtures.userWallet) + +unbundleTrace :: Trace.EmulatorTrace () +unbundleTrace = do + h <- bundleTrace + + _ <- Trace.callEndpoint @"unbundle" h $ Marketplace.UnbundleParams Fixtures.cids + + _ <- Trace.waitNSlots 50 + pure () + +unbundleErrorTrace :: Trace.EmulatorTrace () +unbundleErrorTrace = do + _ <- Start.startTrace + h <- Trace.activateContractWallet Fixtures.userWallet $ Marketplace.userEndpoints Fixtures.marketplace + + _ <- Trace.callEndpoint @"unbundle" h $ Marketplace.UnbundleParams Fixtures.cids + + _ <- Trace.waitNSlots 50 + pure () + +unbundleDatumsCheck :: TracePredicate +unbundleDatumsCheck = + dataAtAddress + Fixtures.marketplaceAddress $ + \mp -> (containsNoBundle . Marketplace.mdBundles $ mp) && (containsNfts . Marketplace.mdSingletons $ mp) + where + containsNoBundle = isNothing . (AssocMap.lookup Fixtures.bundleId) + containsNfts store = maybe False (Fixtures.hasCatTokenRecord . Marketplace.nftRecord) + (AssocMap.lookup Fixtures.catTokenIpfsCidHash store) && + maybe False (Fixtures.hasPhotoTokenRecord . Marketplace.nftRecord) + (AssocMap.lookup Fixtures.photoTokenIpfsCidHash store) From 88b876c8dd46e0660768d2aacfdef484232618c4 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Wed, 18 Aug 2021 16:58:45 +0700 Subject: [PATCH 055/217] add nft sale tests --- MetaLamp/nft-marketplace/plutus-starter.cabal | 2 +- .../Contracts/NftMarketplace/OffChain/User.hs | 8 +- .../src/Plutus/PAB/Simulation.hs | 4 +- MetaLamp/nft-marketplace/test/Main.hs | 2 + .../test/Marketplace/Fixtures/NFT.hs | 3 + .../test/Marketplace/Spec/CreateNft.hs | 10 +- .../test/Marketplace/Spec/Sale.hs | 194 ++++++++++++++++++ 7 files changed, 212 insertions(+), 11 deletions(-) create mode 100644 MetaLamp/nft-marketplace/test/Marketplace/Spec/Sale.hs diff --git a/MetaLamp/nft-marketplace/plutus-starter.cabal b/MetaLamp/nft-marketplace/plutus-starter.cabal index fa7c26c09..888a74fda 100644 --- a/MetaLamp/nft-marketplace/plutus-starter.cabal +++ b/MetaLamp/nft-marketplace/plutus-starter.cabal @@ -66,7 +66,7 @@ test-suite test main-is: Main.hs hs-source-dirs: test other-modules: - Marketplace.Spec.Start Utils.Data Marketplace.Fixtures.NFT Marketplace.Spec.Bundles Utils.Trace Utils Marketplace.Fixtures.Wallet Marketplace.Fixtures Marketplace.Fixtures.CheckOptions Marketplace.Fixtures.Script Marketplace.Spec.CreateNft + Marketplace.Spec.Start Utils.Data Marketplace.Fixtures.NFT Marketplace.Spec.Bundles Marketplace.Spec.Sale Utils.Trace Utils Marketplace.Fixtures.Wallet Marketplace.Fixtures Marketplace.Fixtures.CheckOptions Marketplace.Fixtures.Script Marketplace.Spec.CreateNft default-language: Haskell2010 ghc-options: -Wall -Wnoncanonical-monad-instances -Wincomplete-uni-patterns -Wincomplete-record-updates diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs index 8569a9aa6..7ac1dbee7 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs @@ -139,8 +139,8 @@ PlutusTx.makeLift ''CompleteSaleParams Lens.makeClassy_ ''CompleteSaleParams -- | The user buys specified NFT lot -buyNft :: Core.Marketplace -> CompleteSaleParams -> Contract w s Text () -buyNft marketplace CompleteSaleParams {..} = do +buyItem :: Core.Marketplace -> CompleteSaleParams -> Contract w s Text () +buyItem marketplace CompleteSaleParams {..} = do let internalId = toInternalId cspItemId nftStore <- marketplaceStore marketplace sale <- case internalId of @@ -346,7 +346,7 @@ ownPubKeyBalance = getOwnPubKey >>= fundsAt type MarketplaceUserSchema = Endpoint "createNft" CreateNftParams .\/ Endpoint "openSale" OpenSaleParams - .\/ Endpoint "buyNft" CompleteSaleParams + .\/ Endpoint "buyItem" CompleteSaleParams .\/ Endpoint "closeSale" CompleteSaleParams .\/ Endpoint "startAnAuction" HoldAnAuctionParams .\/ Endpoint "completeAnAuction" HoldAnAuctionParams @@ -377,7 +377,7 @@ userEndpoints :: Core.Marketplace -> Contract (ContractResponse Text UserContrac userEndpoints marketplace = forever $ withContractResponse (Proxy @"createNft") (const NftCreated) (createNft marketplace) `select` withContractResponse (Proxy @"openSale") (const OpenedSale) (openSale marketplace) - `select` withContractResponse (Proxy @"buyNft") (const NftBought) (buyNft marketplace) + `select` withContractResponse (Proxy @"buyItem") (const NftBought) (buyItem marketplace) `select` withContractResponse (Proxy @"closeSale") (const ClosedSale) (closeSale marketplace) `select` withContractResponse (Proxy @"startAnAuction") (const AuctionStarted) (startAnAuction marketplace) `select` withContractResponse (Proxy @"completeAnAuction") (const AuctionComplete) (completeAnAuction marketplace) diff --git a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs index dd5dce9b3..3fa2dbf07 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs @@ -131,13 +131,13 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do buyer = pubKeyHash . walletPubKey $ Wallet 3 _ <- - Simulator.callEndpointOnInstance buyerCid "buyNft" Marketplace.CompleteSaleParams { + Simulator.callEndpointOnInstance buyerCid "buyItem" Marketplace.CompleteSaleParams { cspItemId = Marketplace.UserNftId catTokenIpfsCid } _ <- flip Simulator.waitForState buyerCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of Success (CrSuccess Marketplace.NftBought) -> Just () _ -> Nothing - Simulator.logString @(Builtin MarketplaceContracts) $ "Successful buyNft" + Simulator.logString @(Builtin MarketplaceContracts) $ "Successful buyItem" _ <- Simulator.callEndpointOnInstance userCid "createNft" $ diff --git a/MetaLamp/nft-marketplace/test/Main.hs b/MetaLamp/nft-marketplace/test/Main.hs index 79cf22ec0..dcfba0cad 100644 --- a/MetaLamp/nft-marketplace/test/Main.hs +++ b/MetaLamp/nft-marketplace/test/Main.hs @@ -4,6 +4,7 @@ module Main import qualified Marketplace.Spec.Bundles as Bundles import qualified Marketplace.Spec.CreateNft as CreateNft +import qualified Marketplace.Spec.Sale as Sale import qualified Marketplace.Spec.Start as Start import Test.Tasty @@ -15,4 +16,5 @@ tests = testGroup "NFT Marketplace" [ Start.tests , CreateNft.tests , Bundles.tests + , Sale.tests ] diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/NFT.hs b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/NFT.hs index d799b84a3..9146a13a2 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/NFT.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/NFT.hs @@ -92,3 +92,6 @@ hasPhotoTokenRecord Marketplace.NftInfo {..} = niCategory == photoTokenCategory && niName == photoTokenName && niDescription == photoTokenDescription + +oneAdaInLovelace :: Integer +oneAdaInLovelace = 1000000 diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/CreateNft.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/CreateNft.hs index 1fe23aa6b..739590678 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Spec/CreateNft.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/CreateNft.hs @@ -5,7 +5,7 @@ {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TypeApplications #-} module Marketplace.Spec.CreateNft - ( tests + ( tests, createNftTrace ) where import Control.Lens (_2, (&), (.~), @@ -15,11 +15,13 @@ import Control.Monad (void) import qualified Data.Map as Map import Data.Maybe (isNothing) import Data.Text (Text) +import Data.Void import Ledger import qualified Ledger.Ada as Ada import qualified Ledger.Value as V import qualified Marketplace.Fixtures as Fixtures import qualified Marketplace.Spec.Start as Start +import Plutus.Abstract.ContractResponse import Plutus.Contract import Plutus.Contract.Test import qualified Plutus.Contracts.NftMarketplace.Endpoints as Marketplace @@ -40,7 +42,7 @@ tests = Fixtures.options "Should mint NFT token into the user wallet and create the Marketplace entry hiding issuer" (datumsCheck .&&. valueCheck) - createNftTrace, + (void createNftTrace), checkPredicateOptions Fixtures.options "Should mint NFT token into the user wallet and create the Marketplace entry revealing issuer" @@ -57,13 +59,13 @@ createNftParams = Marketplace.CreateNftParams { Marketplace.cnpRevealIssuer = False } -createNftTrace :: Trace.EmulatorTrace () +createNftTrace :: Trace.EmulatorTrace (Trace.ContractHandle (ContractResponse Text Marketplace.UserContractState) Marketplace.MarketplaceUserSchema Void) createNftTrace = do _ <- Start.startTrace h <- Trace.activateContractWallet Fixtures.userWallet $ Marketplace.userEndpoints Fixtures.marketplace _ <- Trace.callEndpoint @"createNft" h createNftParams _ <- Trace.waitNSlots 50 - pure () + pure h createNftTrace' :: Trace.EmulatorTrace () createNftTrace' = do diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Sale.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Sale.hs new file mode 100644 index 000000000..35978b234 --- /dev/null +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Sale.hs @@ -0,0 +1,194 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE NumericUnderscores #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE TypeApplications #-} + +module Marketplace.Spec.Sale + ( tests + ) where + +import Control.Lens (_2, _Left, (&), + (.~), (^.), (^?)) +import qualified Control.Lens as Lens +import Control.Monad (void) +import Data.Foldable (find) +import Data.Function (on) +import qualified Data.Map as Map +import Data.Maybe (isNothing) +import Data.Text (Text) +import Data.Void +import Ledger +import qualified Ledger.Ada as Ada +import qualified Ledger.Value as V +import qualified Marketplace.Fixtures as Fixtures +import qualified Marketplace.Spec.CreateNft as CreateNft +import qualified Marketplace.Spec.Start as Start +import Plutus.Abstract.ContractResponse +import Plutus.Contract +import Plutus.Contract.Test +import qualified Plutus.Contracts.NftMarketplace.Endpoints as Marketplace +import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace +import qualified Plutus.Contracts.Services.Sale as Sale +import qualified Plutus.Trace as Trace +import qualified PlutusTx.AssocMap as AssocMap +import PlutusTx.Builtins (sha2_256) +import PlutusTx.Prelude (ByteString) +import Test.Tasty +import qualified Utils +import Wallet.Emulator.Wallet + +tests :: TestTree +tests = + testGroup + "Sale" + [ + checkPredicateOptions + Fixtures.options + "Should put on sale one NFT locking it in sale script & saving link" + (openSaleValueCheck .&&. openSaleDatumsCheck) + (void openSaleTrace), + checkPredicateOptions + Fixtures.options + "Should not put on sale if NFT does not exist" + errorCheckUser + openSaleTrace', + checkPredicateOptions + Fixtures.options + "Should close sale and pay locked NFT back" + (closeSaleValueCheck .&&. completeSaleDatumsCheck) + closeSaleTrace, + checkPredicateOptions + Fixtures.options + "Should not close sale if it was not started" + errorCheckUser + closeSaleTrace', + checkPredicateOptions + Fixtures.options + "Should sell NFT and pay the token to buyer" + (buyItemValueCheck .&&. completeSaleDatumsCheck) + buyItemTrace, + checkPredicateOptions + Fixtures.options + "Should not sell NFT if it has no lot" + errorCheckBuyer + buyItemTrace' + ] + +openSaleParams :: Marketplace.OpenSaleParams +openSaleParams = Marketplace.OpenSaleParams { + ospItemId = Marketplace.UserNftId Fixtures.catTokenIpfsCid, + ospSalePrice = 44 * Fixtures.oneAdaInLovelace + } + +completeSaleParams :: Marketplace.CompleteSaleParams +completeSaleParams = Marketplace.CompleteSaleParams { + cspItemId = Marketplace.UserNftId Fixtures.catTokenIpfsCid + } + +openSaleTrace :: Trace.EmulatorTrace (Trace.ContractHandle (ContractResponse Text Marketplace.UserContractState) Marketplace.MarketplaceUserSchema Void) +openSaleTrace = do + h <- CreateNft.createNftTrace + + _ <- Trace.callEndpoint @"openSale" h openSaleParams + + _ <- Trace.waitNSlots 50 + pure h + +openSaleTrace' :: Trace.EmulatorTrace () +openSaleTrace' = do + _ <- Start.startTrace + h <- Trace.activateContractWallet Fixtures.userWallet $ Marketplace.userEndpoints Fixtures.marketplace + + _ <- Trace.callEndpoint @"openSale" h openSaleParams + + _ <- Trace.waitNSlots 50 + pure () + +closeSaleTrace :: Trace.EmulatorTrace () +closeSaleTrace = do + h <- openSaleTrace + + _ <- Trace.callEndpoint @"closeSale" h completeSaleParams + + _ <- Trace.waitNSlots 50 + pure () + +closeSaleTrace' :: Trace.EmulatorTrace () +closeSaleTrace' = do + h <- CreateNft.createNftTrace + + _ <- Trace.callEndpoint @"closeSale" h completeSaleParams + + _ <- Trace.waitNSlots 50 + pure () + +buyItemTrace :: Trace.EmulatorTrace () +buyItemTrace = do + _ <- openSaleTrace + + h <- Trace.activateContractWallet Fixtures.buyerWallet $ Marketplace.userEndpoints Fixtures.marketplace + _ <- Trace.callEndpoint @"buyItem" h completeSaleParams + + _ <- Trace.waitNSlots 50 + pure () + +buyItemTrace' :: Trace.EmulatorTrace () +buyItemTrace' = do + _ <- CreateNft.createNftTrace + + h <- Trace.activateContractWallet Fixtures.buyerWallet $ Marketplace.userEndpoints Fixtures.marketplace + _ <- Trace.callEndpoint @"buyItem" h completeSaleParams + + _ <- Trace.waitNSlots 50 + pure () + +openSaleDatumsCheck :: TracePredicate +openSaleDatumsCheck = + dataAtAddress + Fixtures.marketplaceAddress + (nftIsOnSale . Marketplace.mdSingletons) + where + nftIsOnSale = maybe False (\t -> t ^. Marketplace._nftLot ^? traverse . _2 . _Left & fmap Sale.saleValue & + (== Just (Marketplace.nftValue Fixtures.catTokenIpfsCid t))) . + (AssocMap.lookup Fixtures.catTokenIpfsCidHash) + +completeSaleDatumsCheck :: TracePredicate +completeSaleDatumsCheck = + dataAtAddress + Fixtures.marketplaceAddress + (nftNotOnSale . Marketplace.mdSingletons) + where + nftNotOnSale = maybe False (isNothing . Marketplace.nftLot) . + (AssocMap.lookup Fixtures.catTokenIpfsCidHash) + +openSaleValueCheck :: TracePredicate +openSaleValueCheck = + valueAtAddress + (walletAddress Fixtures.userWallet) + (isNothing . find hasNft . V.flattenValue) + where + hasNft v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCid + +closeSaleValueCheck :: TracePredicate +closeSaleValueCheck = + valueAtAddress + (walletAddress Fixtures.userWallet) + (Utils.one hasNft . V.flattenValue) + where + hasNft v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCid + +buyItemValueCheck :: TracePredicate +buyItemValueCheck = + valueAtAddress + (walletAddress Fixtures.buyerWallet) + (Utils.one hasNft . V.flattenValue) + where + hasNft v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCid + +errorCheckUser :: TracePredicate +errorCheckUser = Utils.assertCrError (Marketplace.userEndpoints Fixtures.marketplace) (Trace.walletInstanceTag Fixtures.userWallet) + +errorCheckBuyer :: TracePredicate +errorCheckBuyer = Utils.assertCrError (Marketplace.userEndpoints Fixtures.marketplace) (Trace.walletInstanceTag Fixtures.buyerWallet) From cd2ce4cd1f491ad45c581b305a0d88f02ddb1689 Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Wed, 18 Aug 2021 18:41:22 +0700 Subject: [PATCH 056/217] add sale bundle tests --- .../test/Marketplace/Spec/Bundles.hs | 2 +- .../test/Marketplace/Spec/Sale.hs | 126 +++++++++++++++++- 2 files changed, 126 insertions(+), 2 deletions(-) diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Bundles.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Bundles.hs index 162bb9439..51c5f3ba8 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Bundles.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Bundles.hs @@ -6,7 +6,7 @@ {-# LANGUAGE TypeApplications #-} module Marketplace.Spec.Bundles - ( tests + ( tests, bundleTrace ) where import Control.Lens (_2, (&), (.~), diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Sale.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Sale.hs index 35978b234..45c5cda0b 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Sale.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Sale.hs @@ -23,6 +23,7 @@ import Ledger import qualified Ledger.Ada as Ada import qualified Ledger.Value as V import qualified Marketplace.Fixtures as Fixtures +import qualified Marketplace.Spec.Bundles as Bundles import qualified Marketplace.Spec.CreateNft as CreateNft import qualified Marketplace.Spec.Start as Start import Plutus.Abstract.ContractResponse @@ -43,6 +44,8 @@ tests :: TestTree tests = testGroup "Sale" + [testGroup + "NFT singletons" [ checkPredicateOptions Fixtures.options @@ -74,8 +77,33 @@ tests = "Should not sell NFT if it has no lot" errorCheckBuyer buyItemTrace' - ] + ], + testGroup + "NFT bundles" + [ + checkPredicateOptions + Fixtures.options + "Should put on sale NFT bundle locking bundle value in sale script & saving link" + (openSaleValueCheckB .&&. openSaleDatumsCheckB) + (void openSaleTraceB), + checkPredicateOptions + Fixtures.options + "Should not put on sale if bundle does not exist" + errorCheckUser + openSaleTraceB', + checkPredicateOptions + Fixtures.options + "Should close sale and pay locked bundle value back" + (closeSaleValueCheckB .&&. completeSaleDatumsCheckB) + closeSaleTraceB, + checkPredicateOptions + Fixtures.options + "Should sell bundle and pay its value to buyer" + (buyItemValueCheckB .&&. completeSaleDatumsCheckB) + buyItemTraceB + ]] +-- \/\/\/ "NFT singletons" openSaleParams :: Marketplace.OpenSaleParams openSaleParams = Marketplace.OpenSaleParams { ospItemId = Marketplace.UserNftId Fixtures.catTokenIpfsCid, @@ -192,3 +220,99 @@ errorCheckUser = Utils.assertCrError (Marketplace.userEndpoints Fixtures.marketp errorCheckBuyer :: TracePredicate errorCheckBuyer = Utils.assertCrError (Marketplace.userEndpoints Fixtures.marketplace) (Trace.walletInstanceTag Fixtures.buyerWallet) + +-- \/\/\/ "NFT bundles" +openSaleParamsB :: Marketplace.OpenSaleParams +openSaleParamsB = Marketplace.OpenSaleParams { + ospItemId = Marketplace.UserBundleId Fixtures.cids, + ospSalePrice = 65 * Fixtures.oneAdaInLovelace + } + +completeSaleParamsB :: Marketplace.CompleteSaleParams +completeSaleParamsB = Marketplace.CompleteSaleParams { + cspItemId = Marketplace.UserBundleId Fixtures.cids + } + +openSaleTraceB :: Trace.EmulatorTrace (Trace.ContractHandle (ContractResponse Text Marketplace.UserContractState) Marketplace.MarketplaceUserSchema Void) +openSaleTraceB = do + h <- Bundles.bundleTrace + + _ <- Trace.callEndpoint @"openSale" h openSaleParamsB + + _ <- Trace.waitNSlots 50 + pure h + +openSaleTraceB' :: Trace.EmulatorTrace () +openSaleTraceB' = do + _ <- Start.startTrace + h <- Trace.activateContractWallet Fixtures.userWallet $ Marketplace.userEndpoints Fixtures.marketplace + + _ <- Trace.callEndpoint @"openSale" h openSaleParamsB + + _ <- Trace.waitNSlots 50 + pure () + +closeSaleTraceB :: Trace.EmulatorTrace () +closeSaleTraceB = do + h <- openSaleTraceB + + _ <- Trace.callEndpoint @"closeSale" h completeSaleParamsB + + _ <- Trace.waitNSlots 50 + pure () + +buyItemTraceB :: Trace.EmulatorTrace () +buyItemTraceB = do + _ <- openSaleTraceB + + h <- Trace.activateContractWallet Fixtures.buyerWallet $ Marketplace.userEndpoints Fixtures.marketplace + _ <- Trace.callEndpoint @"buyItem" h completeSaleParamsB + + _ <- Trace.waitNSlots 50 + pure () + +openSaleDatumsCheckB :: TracePredicate +openSaleDatumsCheckB = + dataAtAddress + Fixtures.marketplaceAddress + (bundleIsOnSale . Marketplace.mdBundles) + where + bundleIsOnSale = maybe False (\b -> b ^. Marketplace._nbTokens ^? Marketplace._HasLot . _2 . _Left & fmap Sale.saleValue & + (== Just (Marketplace.bundleValue AssocMap.empty b))) . + (AssocMap.lookup Fixtures.bundleId) + +completeSaleDatumsCheckB :: TracePredicate +completeSaleDatumsCheckB = + dataAtAddress + Fixtures.marketplaceAddress + (bundleNotOnSale . Marketplace.mdBundles) + where + bundleNotOnSale = maybe False (Prelude.not . Marketplace.hasLotBundle) . + (AssocMap.lookup Fixtures.bundleId) + +openSaleValueCheckB :: TracePredicate +openSaleValueCheckB = + valueAtAddress + (walletAddress Fixtures.userWallet) $ + \v -> (isNothing . find hasCatToken . V.flattenValue $ v) && (isNothing . find hasPhotoToken . V.flattenValue $ v) + where + hasCatToken v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCid + hasPhotoToken v = (v ^. _2 & V.unTokenName) == Fixtures.photoTokenIpfsCid + +closeSaleValueCheckB :: TracePredicate +closeSaleValueCheckB = + valueAtAddress + (walletAddress Fixtures.userWallet) $ + \v -> (Utils.one hasCatToken . V.flattenValue $ v) && (Utils.one hasPhotoToken . V.flattenValue $ v) + where + hasCatToken v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCid + hasPhotoToken v = (v ^. _2 & V.unTokenName) == Fixtures.photoTokenIpfsCid + +buyItemValueCheckB :: TracePredicate +buyItemValueCheckB = + valueAtAddress + (walletAddress Fixtures.buyerWallet) $ + \v -> (Utils.one hasCatToken . V.flattenValue $ v) && (Utils.one hasPhotoToken . V.flattenValue $ v) + where + hasCatToken v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCid + hasPhotoToken v = (v ^. _2 & V.unTokenName) == Fixtures.photoTokenIpfsCid From 43b95ac8e98568ba3f0983c9efa178854be89f5f Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Wed, 18 Aug 2021 21:34:13 +0700 Subject: [PATCH 057/217] add nft auction tests --- MetaLamp/nft-marketplace/plutus-starter.cabal | 2 +- .../Contracts/NftMarketplace/OffChain/User.hs | 60 ++--- .../src/Plutus/PAB/Simulation.hs | 16 +- MetaLamp/nft-marketplace/test/Main.hs | 2 + .../test/Marketplace/Spec/Auction.hs | 229 ++++++++++++++++++ .../test/Marketplace/Spec/Sale.hs | 24 +- 6 files changed, 282 insertions(+), 51 deletions(-) create mode 100644 MetaLamp/nft-marketplace/test/Marketplace/Spec/Auction.hs diff --git a/MetaLamp/nft-marketplace/plutus-starter.cabal b/MetaLamp/nft-marketplace/plutus-starter.cabal index 888a74fda..4366518ff 100644 --- a/MetaLamp/nft-marketplace/plutus-starter.cabal +++ b/MetaLamp/nft-marketplace/plutus-starter.cabal @@ -66,7 +66,7 @@ test-suite test main-is: Main.hs hs-source-dirs: test other-modules: - Marketplace.Spec.Start Utils.Data Marketplace.Fixtures.NFT Marketplace.Spec.Bundles Marketplace.Spec.Sale Utils.Trace Utils Marketplace.Fixtures.Wallet Marketplace.Fixtures Marketplace.Fixtures.CheckOptions Marketplace.Fixtures.Script Marketplace.Spec.CreateNft + Marketplace.Spec.Start Utils.Data Marketplace.Spec.Auction Marketplace.Fixtures.NFT Marketplace.Spec.Bundles Marketplace.Spec.Sale Utils.Trace Utils Marketplace.Fixtures.Wallet Marketplace.Fixtures Marketplace.Fixtures.CheckOptions Marketplace.Fixtures.Script Marketplace.Spec.CreateNft default-language: Haskell2010 ghc-options: -Wall -Wnoncanonical-monad-instances -Wincomplete-uni-patterns -Wincomplete-record-updates diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs index 7ac1dbee7..a36b2c343 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs @@ -127,21 +127,21 @@ openSale marketplace OpenSaleParams {..} = do logInfo @Haskell.String $ printf "Created NFT sale %s" (Haskell.show sale) pure () -data CompleteSaleParams = - CompleteSaleParams { - cspItemId :: UserItemId +data CloseLotParams = + CloseLotParams { + clpItemId :: UserItemId } deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON, Schema.ToSchema) -PlutusTx.unstableMakeIsData ''CompleteSaleParams -PlutusTx.makeLift ''CompleteSaleParams -Lens.makeClassy_ ''CompleteSaleParams +PlutusTx.unstableMakeIsData ''CloseLotParams +PlutusTx.makeLift ''CloseLotParams +Lens.makeClassy_ ''CloseLotParams -- | The user buys specified NFT lot -buyItem :: Core.Marketplace -> CompleteSaleParams -> Contract w s Text () -buyItem marketplace CompleteSaleParams {..} = do - let internalId = toInternalId cspItemId +buyItem :: Core.Marketplace -> CloseLotParams -> Contract w s Text () +buyItem marketplace CloseLotParams {..} = do + let internalId = toInternalId clpItemId nftStore <- marketplaceStore marketplace sale <- case internalId of Left nftId@(Core.InternalNftId ipfsCidHash ipfsCid) -> do @@ -163,9 +163,9 @@ buyItem marketplace CompleteSaleParams {..} = do pure () -- | The user closes NFT sale and receives his token back -closeSale :: Core.Marketplace -> CompleteSaleParams -> Contract w s Text () -closeSale marketplace CompleteSaleParams {..} = do - let internalId = toInternalId cspItemId +closeSale :: Core.Marketplace -> CloseLotParams -> Contract w s Text () +closeSale marketplace CloseLotParams {..} = do + let internalId = toInternalId clpItemId nftStore <- marketplaceStore marketplace sale <- case internalId of Left nftId@(Core.InternalNftId ipfsCidHash ipfsCid) -> do @@ -186,22 +186,22 @@ closeSale marketplace CompleteSaleParams {..} = do logInfo @Haskell.String $ printf "Closed lot sale %s" (Haskell.show sale) pure () -data HoldAnAuctionParams = - HoldAnAuctionParams { - haapItemId :: UserItemId, - haapDuration :: Slot +data StartAnAuctionParams = + StartAnAuctionParams { + saapItemId :: UserItemId, + saapDuration :: Slot } deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON, Schema.ToSchema) -PlutusTx.unstableMakeIsData ''HoldAnAuctionParams -PlutusTx.makeLift ''HoldAnAuctionParams -Lens.makeClassy_ ''HoldAnAuctionParams +PlutusTx.unstableMakeIsData ''StartAnAuctionParams +PlutusTx.makeLift ''StartAnAuctionParams +Lens.makeClassy_ ''StartAnAuctionParams -- | The user starts an auction for specified NFT -startAnAuction :: Core.Marketplace -> HoldAnAuctionParams -> Contract w s Text () -startAnAuction marketplace HoldAnAuctionParams {..} = do - let internalId = toInternalId haapItemId +startAnAuction :: Core.Marketplace -> StartAnAuctionParams -> Contract w s Text () +startAnAuction marketplace StartAnAuctionParams {..} = do + let internalId = toInternalId saapItemId nftStore <- marketplaceStore marketplace auctionValue <- case internalId of Left nftId@(Core.InternalNftId ipfsCidHash ipfsCid) -> @@ -210,7 +210,7 @@ startAnAuction marketplace HoldAnAuctionParams {..} = do Core.bundleValue cids <$> getBundleEntry nftStore bundleId currSlot <- currentSlot - let endTime = currSlot + haapDuration + let endTime = currSlot + saapDuration (auctionToken, auctionParams) <- mapError (T.pack . Haskell.show) $ Auction.startAuction auctionValue endTime let client = Core.marketplaceClient marketplace @@ -221,9 +221,9 @@ startAnAuction marketplace HoldAnAuctionParams {..} = do pure () -- | The user completes the auction for specified NFT -completeAnAuction :: Core.Marketplace -> HoldAnAuctionParams -> Contract w s Text () -completeAnAuction marketplace HoldAnAuctionParams {..} = do - let internalId = toInternalId haapItemId +completeAnAuction :: Core.Marketplace -> CloseLotParams -> Contract w s Text () +completeAnAuction marketplace CloseLotParams {..} = do + let internalId = toInternalId clpItemId nftStore <- marketplaceStore marketplace auction <- case internalId of Left nftId@(Core.InternalNftId ipfsCidHash ipfsCid) -> do @@ -346,10 +346,10 @@ ownPubKeyBalance = getOwnPubKey >>= fundsAt type MarketplaceUserSchema = Endpoint "createNft" CreateNftParams .\/ Endpoint "openSale" OpenSaleParams - .\/ Endpoint "buyItem" CompleteSaleParams - .\/ Endpoint "closeSale" CompleteSaleParams - .\/ Endpoint "startAnAuction" HoldAnAuctionParams - .\/ Endpoint "completeAnAuction" HoldAnAuctionParams + .\/ Endpoint "buyItem" CloseLotParams + .\/ Endpoint "closeSale" CloseLotParams + .\/ Endpoint "startAnAuction" StartAnAuctionParams + .\/ Endpoint "completeAnAuction" CloseLotParams .\/ Endpoint "bidOnAuction" BidOnAuctionParams .\/ Endpoint "bundleUp" BundleUpParams .\/ Endpoint "unbundle" UnbundleParams diff --git a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs index 3fa2dbf07..eb74db9a2 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs @@ -131,8 +131,8 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do buyer = pubKeyHash . walletPubKey $ Wallet 3 _ <- - Simulator.callEndpointOnInstance buyerCid "buyItem" Marketplace.CompleteSaleParams { - cspItemId = Marketplace.UserNftId catTokenIpfsCid + Simulator.callEndpointOnInstance buyerCid "buyItem" Marketplace.CloseLotParams { + clpItemId = Marketplace.UserNftId catTokenIpfsCid } _ <- flip Simulator.waitForState buyerCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of Success (CrSuccess Marketplace.NftBought) -> Just () @@ -166,17 +166,17 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do _ <- Simulator.callEndpointOnInstance userCid "closeSale" - Marketplace.CompleteSaleParams { - cspItemId = Marketplace.UserNftId photoTokenIpfsCid + Marketplace.CloseLotParams { + clpItemId = Marketplace.UserNftId photoTokenIpfsCid } sale <- flip Simulator.waitForState userCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of Success (CrSuccess Marketplace.ClosedSale) -> Just () _ -> Nothing Simulator.logString @(Builtin MarketplaceContracts) $ "Successful closeSale" - let auction = Marketplace.HoldAnAuctionParams { - haapItemId = Marketplace.UserNftId photoTokenIpfsCid, - haapDuration = 80 + let auction = Marketplace.StartAnAuctionParams { + saapItemId = Marketplace.UserNftId photoTokenIpfsCid, + saapDuration = 80 } _ <- Simulator.callEndpointOnInstance userCid "startAnAuction" auction @@ -202,7 +202,7 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do Simulator.logString @(Builtin MarketplaceContracts) $ "Final auction state: " <> show s _ <- - Simulator.callEndpointOnInstance buyerCid "completeAnAuction" auction + Simulator.callEndpointOnInstance buyerCid "completeAnAuction" $ Marketplace.CloseLotParams $ Marketplace.UserNftId photoTokenIpfsCid _ <- flip Simulator.waitForState buyerCid $ \json -> case (fromJSON json :: Result (ContractResponse Text Marketplace.UserContractState)) of Success (CrSuccess Marketplace.AuctionComplete) -> Just () _ -> Nothing diff --git a/MetaLamp/nft-marketplace/test/Main.hs b/MetaLamp/nft-marketplace/test/Main.hs index dcfba0cad..dc152dd95 100644 --- a/MetaLamp/nft-marketplace/test/Main.hs +++ b/MetaLamp/nft-marketplace/test/Main.hs @@ -2,6 +2,7 @@ module Main ( main ) where +import qualified Marketplace.Spec.Auction as Auction import qualified Marketplace.Spec.Bundles as Bundles import qualified Marketplace.Spec.CreateNft as CreateNft import qualified Marketplace.Spec.Sale as Sale @@ -17,4 +18,5 @@ tests = testGroup "NFT Marketplace" , CreateNft.tests , Bundles.tests , Sale.tests + , Auction.tests ] diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Auction.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Auction.hs new file mode 100644 index 000000000..0c077ac38 --- /dev/null +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Auction.hs @@ -0,0 +1,229 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE NumericUnderscores #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE TypeApplications #-} + +module Marketplace.Spec.Auction + ( tests + ) where + +import Control.Lens (_2, _Left, + _Right, (&), + (.~), (^.), (^?)) +import qualified Control.Lens as Lens +import Control.Monad (void) +import Data.Foldable (find) +import Data.Function (on) +import qualified Data.Map as Map +import Data.Maybe (isNothing) +import Data.Text (Text) +import Data.Void +import qualified Ext.Plutus.Contracts.Auction as Auction +import Ledger +import qualified Ledger.Ada as Ada +import qualified Ledger.Value as V +import qualified Marketplace.Fixtures as Fixtures +import qualified Marketplace.Spec.Bundles as Bundles +import qualified Marketplace.Spec.CreateNft as CreateNft +import qualified Marketplace.Spec.Start as Start +import Plutus.Abstract.ContractResponse +import Plutus.Contract +import Plutus.Contract.Test +import qualified Plutus.Contracts.NftMarketplace.Endpoints as Marketplace +import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace +import qualified Plutus.Trace as Trace +import qualified PlutusTx.AssocMap as AssocMap +import PlutusTx.Builtins (sha2_256) +import PlutusTx.Prelude (ByteString) +import Test.Tasty +import qualified Utils +import Wallet.Emulator.Wallet + +tests :: TestTree +tests = + testGroup + "Auction" + [testGroup + "NFT singletons" + [ + checkPredicateOptions + Fixtures.options + "Should open an auction for one NFT locking it in auction script & saving link" + (startAnAuctionValueCheck .&&. startAnAuctionDatumsCheck) + (void startAnAuctionTrace), + checkPredicateOptions + Fixtures.options + "Should not put on auction if NFT does not exist" + errorCheckUser + startAnAuctionTrace', + checkPredicateOptions + Fixtures.options + "Should close auction and pay locked NFT back if there were no bids" + (completeAnAuctionValueCheck .&&. completeAuctionDatumsCheck) + completeAnAuctionTrace, + checkPredicateOptions + Fixtures.options + "Should not close auction if it was not started" + errorCheckUser + completeAnAuctionTrace', + checkPredicateOptions + Fixtures.options + "Should bid on NFT" + startAnAuctionDatumsCheck + (void bidOnAuctionTrace), + checkPredicateOptions + Fixtures.options + "Should not bid if NFT is not on auction" + errorCheckBuyer + bidOnAuctionTrace', + checkPredicateOptions + Fixtures.options + "Should close auction and pay locked NFT to highest bidder" + (buyOnAuctionValueCheck .&&. completeAuctionDatumsCheck) + buyOnAuctionTrace + ], + testGroup + "NFT bundles" + [ + + ]] + +auctionValue :: Marketplace.Auction -> Value +auctionValue = Auction.apAsset . Auction.fromTuple + +-- \/\/\/ "NFT singletons" +startAnAuctionParams :: Marketplace.StartAnAuctionParams +startAnAuctionParams = Marketplace.StartAnAuctionParams + { + saapItemId = Marketplace.UserNftId Fixtures.catTokenIpfsCid, + saapDuration = 155 + } + +closeLotParams :: Marketplace.CloseLotParams +closeLotParams = Marketplace.CloseLotParams { + clpItemId = Marketplace.UserNftId Fixtures.catTokenIpfsCid + } +bidOnAuctionParams :: Marketplace.BidOnAuctionParams +bidOnAuctionParams = Marketplace.BidOnAuctionParams { + boapItemId = Marketplace.UserNftId Fixtures.catTokenIpfsCid, + boapBid = fromInteger $ 25 * Fixtures.oneAdaInLovelace + } + +startAnAuctionTrace :: Trace.EmulatorTrace (Trace.ContractHandle (ContractResponse Text Marketplace.UserContractState) Marketplace.MarketplaceUserSchema Void) +startAnAuctionTrace = do + h <- CreateNft.createNftTrace + + _ <- Trace.callEndpoint @"startAnAuction" h startAnAuctionParams + + _ <- Trace.waitNSlots 50 + pure h + +startAnAuctionTrace' :: Trace.EmulatorTrace () +startAnAuctionTrace' = do + _ <- Start.startTrace + h <- Trace.activateContractWallet Fixtures.userWallet $ Marketplace.userEndpoints Fixtures.marketplace + + _ <- Trace.callEndpoint @"startAnAuction" h startAnAuctionParams + + _ <- Trace.waitNSlots 50 + pure () + +completeAnAuctionTrace :: Trace.EmulatorTrace () +completeAnAuctionTrace = do + h <- startAnAuctionTrace + + _ <- Trace.callEndpoint @"completeAnAuction" h closeLotParams + + _ <- Trace.waitNSlots 250 + pure () + +completeAnAuctionTrace' :: Trace.EmulatorTrace () +completeAnAuctionTrace' = do + h <- CreateNft.createNftTrace + + _ <- Trace.callEndpoint @"completeAnAuction" h closeLotParams + + _ <- Trace.waitNSlots 50 + pure () + +bidOnAuctionTrace :: Trace.EmulatorTrace (Trace.ContractHandle (ContractResponse Text Marketplace.UserContractState) Marketplace.MarketplaceUserSchema Void) +bidOnAuctionTrace = do + _ <- startAnAuctionTrace + + h <- Trace.activateContractWallet Fixtures.buyerWallet $ Marketplace.userEndpoints Fixtures.marketplace + _ <- Trace.callEndpoint @"bidOnAuction" h bidOnAuctionParams + + _ <- Trace.waitNSlots 50 + pure h + +bidOnAuctionTrace' :: Trace.EmulatorTrace () +bidOnAuctionTrace' = do + _ <- CreateNft.createNftTrace + + h <- Trace.activateContractWallet Fixtures.buyerWallet $ Marketplace.userEndpoints Fixtures.marketplace + _ <- Trace.callEndpoint @"bidOnAuction" h bidOnAuctionParams + + _ <- Trace.waitNSlots 50 + pure () + +buyOnAuctionTrace :: Trace.EmulatorTrace () +buyOnAuctionTrace = do + h <- bidOnAuctionTrace + + _ <- Trace.callEndpoint @"completeAnAuction" h closeLotParams + + _ <- Trace.waitNSlots 250 + pure () + +startAnAuctionDatumsCheck :: TracePredicate +startAnAuctionDatumsCheck = + dataAtAddress + Fixtures.marketplaceAddress + (nftIsOnAuction . Marketplace.mdSingletons) + where + nftIsOnAuction = maybe False (\t -> t ^. Marketplace._nftLot ^? traverse . _2 . _Right & fmap auctionValue & + (== Just (Marketplace.nftValue Fixtures.catTokenIpfsCid t))) . + (AssocMap.lookup Fixtures.catTokenIpfsCidHash) + +completeAuctionDatumsCheck :: TracePredicate +completeAuctionDatumsCheck = + dataAtAddress + Fixtures.marketplaceAddress + (nftNotOnAuction . Marketplace.mdSingletons) + where + nftNotOnAuction = maybe False (isNothing . Marketplace.nftLot) . + (AssocMap.lookup Fixtures.catTokenIpfsCidHash) + +startAnAuctionValueCheck :: TracePredicate +startAnAuctionValueCheck = + valueAtAddress + (walletAddress Fixtures.userWallet) + (isNothing . find hasNft . V.flattenValue) + where + hasNft v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCid + +completeAnAuctionValueCheck :: TracePredicate +completeAnAuctionValueCheck = + valueAtAddress + (walletAddress Fixtures.userWallet) + (Utils.one hasNft . V.flattenValue) + where + hasNft v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCid + +buyOnAuctionValueCheck :: TracePredicate +buyOnAuctionValueCheck = + valueAtAddress + (walletAddress Fixtures.buyerWallet) + (Utils.one hasNft . V.flattenValue) + where + hasNft v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCid + +errorCheckUser :: TracePredicate +errorCheckUser = Utils.assertCrError (Marketplace.userEndpoints Fixtures.marketplace) (Trace.walletInstanceTag Fixtures.userWallet) + +errorCheckBuyer :: TracePredicate +errorCheckBuyer = Utils.assertCrError (Marketplace.userEndpoints Fixtures.marketplace) (Trace.walletInstanceTag Fixtures.buyerWallet) + +-- \/\/\/ "NFT bundles" diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Sale.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Sale.hs index 45c5cda0b..0a38f30a8 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Sale.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Sale.hs @@ -110,9 +110,9 @@ openSaleParams = Marketplace.OpenSaleParams { ospSalePrice = 44 * Fixtures.oneAdaInLovelace } -completeSaleParams :: Marketplace.CompleteSaleParams -completeSaleParams = Marketplace.CompleteSaleParams { - cspItemId = Marketplace.UserNftId Fixtures.catTokenIpfsCid +closeLotParams :: Marketplace.CloseLotParams +closeLotParams = Marketplace.CloseLotParams { + clpItemId = Marketplace.UserNftId Fixtures.catTokenIpfsCid } openSaleTrace :: Trace.EmulatorTrace (Trace.ContractHandle (ContractResponse Text Marketplace.UserContractState) Marketplace.MarketplaceUserSchema Void) @@ -138,7 +138,7 @@ closeSaleTrace :: Trace.EmulatorTrace () closeSaleTrace = do h <- openSaleTrace - _ <- Trace.callEndpoint @"closeSale" h completeSaleParams + _ <- Trace.callEndpoint @"closeSale" h closeLotParams _ <- Trace.waitNSlots 50 pure () @@ -147,7 +147,7 @@ closeSaleTrace' :: Trace.EmulatorTrace () closeSaleTrace' = do h <- CreateNft.createNftTrace - _ <- Trace.callEndpoint @"closeSale" h completeSaleParams + _ <- Trace.callEndpoint @"closeSale" h closeLotParams _ <- Trace.waitNSlots 50 pure () @@ -157,7 +157,7 @@ buyItemTrace = do _ <- openSaleTrace h <- Trace.activateContractWallet Fixtures.buyerWallet $ Marketplace.userEndpoints Fixtures.marketplace - _ <- Trace.callEndpoint @"buyItem" h completeSaleParams + _ <- Trace.callEndpoint @"buyItem" h closeLotParams _ <- Trace.waitNSlots 50 pure () @@ -167,7 +167,7 @@ buyItemTrace' = do _ <- CreateNft.createNftTrace h <- Trace.activateContractWallet Fixtures.buyerWallet $ Marketplace.userEndpoints Fixtures.marketplace - _ <- Trace.callEndpoint @"buyItem" h completeSaleParams + _ <- Trace.callEndpoint @"buyItem" h closeLotParams _ <- Trace.waitNSlots 50 pure () @@ -228,9 +228,9 @@ openSaleParamsB = Marketplace.OpenSaleParams { ospSalePrice = 65 * Fixtures.oneAdaInLovelace } -completeSaleParamsB :: Marketplace.CompleteSaleParams -completeSaleParamsB = Marketplace.CompleteSaleParams { - cspItemId = Marketplace.UserBundleId Fixtures.cids +closeLotParamsB :: Marketplace.CloseLotParams +closeLotParamsB = Marketplace.CloseLotParams { + clpItemId = Marketplace.UserBundleId Fixtures.cids } openSaleTraceB :: Trace.EmulatorTrace (Trace.ContractHandle (ContractResponse Text Marketplace.UserContractState) Marketplace.MarketplaceUserSchema Void) @@ -256,7 +256,7 @@ closeSaleTraceB :: Trace.EmulatorTrace () closeSaleTraceB = do h <- openSaleTraceB - _ <- Trace.callEndpoint @"closeSale" h completeSaleParamsB + _ <- Trace.callEndpoint @"closeSale" h closeLotParamsB _ <- Trace.waitNSlots 50 pure () @@ -266,7 +266,7 @@ buyItemTraceB = do _ <- openSaleTraceB h <- Trace.activateContractWallet Fixtures.buyerWallet $ Marketplace.userEndpoints Fixtures.marketplace - _ <- Trace.callEndpoint @"buyItem" h completeSaleParamsB + _ <- Trace.callEndpoint @"buyItem" h closeLotParamsB _ <- Trace.waitNSlots 50 pure () From 316e9dbc95cfaf3ce7d093a73aafd4fa516bc12f Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Wed, 18 Aug 2021 23:43:10 +0700 Subject: [PATCH 058/217] add bundle auction tests --- .../test/Marketplace/Spec/Auction.hs | 138 +++++++++++++++++- 1 file changed, 136 insertions(+), 2 deletions(-) diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Auction.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Auction.hs index 0c077ac38..3989a277e 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Auction.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Auction.hs @@ -80,14 +80,38 @@ tests = bidOnAuctionTrace', checkPredicateOptions Fixtures.options - "Should close auction and pay locked NFT to highest bidder" + "Should close auction and pay locked NFT to the highest bidder" (buyOnAuctionValueCheck .&&. completeAuctionDatumsCheck) buyOnAuctionTrace ], testGroup "NFT bundles" [ - + checkPredicateOptions + Fixtures.options + "Should open an auction for NFT bundle locking its value in auction script & saving link" + (startAnAuctionValueCheckB .&&. startAnAuctionDatumsCheckB) + (void startAnAuctionTraceB), + checkPredicateOptions + Fixtures.options + "Should not put on auction if bundle does not exist" + errorCheckUser + startAnAuctionTraceB', + checkPredicateOptions + Fixtures.options + "Should close auction and pay locked bundle value back if there were no bids" + (completeAnAuctionValueCheckB .&&. completeAuctionDatumsCheckB) + completeAnAuctionTraceB, + checkPredicateOptions + Fixtures.options + "Should bid on bundle" + startAnAuctionDatumsCheckB + (void bidOnAuctionTraceB), + checkPredicateOptions + Fixtures.options + "Should close auction and pay locked bundle value to the highest bidder" + (buyOnAuctionValueCheckB .&&. completeAuctionDatumsCheckB) + buyOnAuctionTraceB ]] auctionValue :: Marketplace.Auction -> Value @@ -227,3 +251,113 @@ errorCheckBuyer :: TracePredicate errorCheckBuyer = Utils.assertCrError (Marketplace.userEndpoints Fixtures.marketplace) (Trace.walletInstanceTag Fixtures.buyerWallet) -- \/\/\/ "NFT bundles" +startAnAuctionParamsB :: Marketplace.StartAnAuctionParams +startAnAuctionParamsB = Marketplace.StartAnAuctionParams + { + saapItemId = Marketplace.UserBundleId Fixtures.cids, + saapDuration = 142 + } + +closeLotParamsB :: Marketplace.CloseLotParams +closeLotParamsB = Marketplace.CloseLotParams { + clpItemId = Marketplace.UserBundleId Fixtures.cids + } + +bidOnAuctionParamsB :: Marketplace.BidOnAuctionParams +bidOnAuctionParamsB = Marketplace.BidOnAuctionParams { + boapItemId = Marketplace.UserBundleId Fixtures.cids, + boapBid = fromInteger $ 35 * Fixtures.oneAdaInLovelace + } + +startAnAuctionTraceB :: Trace.EmulatorTrace (Trace.ContractHandle (ContractResponse Text Marketplace.UserContractState) Marketplace.MarketplaceUserSchema Void) +startAnAuctionTraceB = do + h <- Bundles.bundleTrace + + _ <- Trace.callEndpoint @"startAnAuction" h startAnAuctionParamsB + + _ <- Trace.waitNSlots 50 + pure h + +startAnAuctionTraceB' :: Trace.EmulatorTrace () +startAnAuctionTraceB' = do + _ <- Start.startTrace + h <- Trace.activateContractWallet Fixtures.userWallet $ Marketplace.userEndpoints Fixtures.marketplace + + _ <- Trace.callEndpoint @"startAnAuction" h startAnAuctionParamsB + + _ <- Trace.waitNSlots 50 + pure () + +completeAnAuctionTraceB :: Trace.EmulatorTrace () +completeAnAuctionTraceB = do + h <- startAnAuctionTraceB + + _ <- Trace.callEndpoint @"completeAnAuction" h closeLotParamsB + + _ <- Trace.waitNSlots 250 + pure () + +bidOnAuctionTraceB :: Trace.EmulatorTrace (Trace.ContractHandle (ContractResponse Text Marketplace.UserContractState) Marketplace.MarketplaceUserSchema Void) +bidOnAuctionTraceB = do + _ <- startAnAuctionTraceB + + h <- Trace.activateContractWallet Fixtures.buyerWallet $ Marketplace.userEndpoints Fixtures.marketplace + _ <- Trace.callEndpoint @"bidOnAuction" h bidOnAuctionParamsB + + _ <- Trace.waitNSlots 50 + pure h + +buyOnAuctionTraceB :: Trace.EmulatorTrace () +buyOnAuctionTraceB = do + h <- bidOnAuctionTraceB + + _ <- Trace.callEndpoint @"completeAnAuction" h closeLotParamsB + + _ <- Trace.waitNSlots 250 + pure () + +startAnAuctionDatumsCheckB :: TracePredicate +startAnAuctionDatumsCheckB = + dataAtAddress + Fixtures.marketplaceAddress + (bundleIsOnAuction . Marketplace.mdBundles) + where + bundleIsOnAuction = maybe False (\b -> b ^. Marketplace._nbTokens ^? Marketplace._HasLot . _2 . _Right & fmap auctionValue & + (== Just (Marketplace.bundleValue AssocMap.empty b))) . + (AssocMap.lookup Fixtures.bundleId) + +completeAuctionDatumsCheckB :: TracePredicate +completeAuctionDatumsCheckB = + dataAtAddress + Fixtures.marketplaceAddress + (bundleNotOnAuction . Marketplace.mdBundles) + where + bundleNotOnAuction = maybe False (Prelude.not . Marketplace.hasLotBundle) . + (AssocMap.lookup Fixtures.bundleId) + +startAnAuctionValueCheckB :: TracePredicate +startAnAuctionValueCheckB = + valueAtAddress + (walletAddress Fixtures.userWallet) $ + \v -> (isNothing . find hasCatToken . V.flattenValue $ v) && (isNothing . find hasPhotoToken . V.flattenValue $ v) + where + hasCatToken v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCid + hasPhotoToken v = (v ^. _2 & V.unTokenName) == Fixtures.photoTokenIpfsCid + +completeAnAuctionValueCheckB :: TracePredicate +completeAnAuctionValueCheckB = + valueAtAddress + (walletAddress Fixtures.userWallet) $ + \v -> (Utils.one hasCatToken . V.flattenValue $ v) && (Utils.one hasPhotoToken . V.flattenValue $ v) + where + hasCatToken v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCid + hasPhotoToken v = (v ^. _2 & V.unTokenName) == Fixtures.photoTokenIpfsCid + +buyOnAuctionValueCheckB :: TracePredicate +buyOnAuctionValueCheckB = + valueAtAddress + (walletAddress Fixtures.buyerWallet) $ + \v -> (Utils.one hasCatToken . V.flattenValue $ v) && (Utils.one hasPhotoToken . V.flattenValue $ v) + where + hasCatToken v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCid + hasPhotoToken v = (v ^. _2 & V.unTokenName) == Fixtures.photoTokenIpfsCid From c0a93140d68c9d0ac4e7023491cda4d53830b6a5 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Thu, 19 Aug 2021 00:02:19 +0700 Subject: [PATCH 059/217] rm unused code --- MetaLamp/nft-marketplace/plutus-starter.cabal | 2 +- .../src/Ext/Plutus/Contracts/Auction.hs | 1 - .../src/Ext/Plutus/Ledger/Contexts.hs | 59 ----------- .../src/Ext/PlutusTx/AssocMap.hs | 4 - .../src/Plutus/Abstract/ContractResponse.hs | 3 - .../src/Plutus/Abstract/OutputValue.hs | 19 ---- .../src/Plutus/Abstract/TxUtils.hs | 97 ------------------- .../Contracts/NftMarketplace/OffChain/Info.hs | 2 - .../Contracts/NftMarketplace/OnChain/Core.hs | 1 - .../test/Marketplace/Fixtures/NFT.hs | 10 +- .../test/Marketplace/Fixtures/Script.hs | 5 +- .../test/Marketplace/Spec/Auction.hs | 30 +++--- .../test/Marketplace/Spec/Bundles.hs | 18 ++-- .../test/Marketplace/Spec/CreateNft.hs | 11 +-- .../test/Marketplace/Spec/Sale.hs | 22 ++--- .../test/Marketplace/Spec/Start.hs | 5 +- MetaLamp/nft-marketplace/test/Utils/Data.hs | 3 - MetaLamp/nft-marketplace/test/Utils/Trace.hs | 21 ---- 18 files changed, 46 insertions(+), 267 deletions(-) delete mode 100644 MetaLamp/nft-marketplace/src/Ext/Plutus/Ledger/Contexts.hs delete mode 100644 MetaLamp/nft-marketplace/src/Plutus/Abstract/OutputValue.hs delete mode 100644 MetaLamp/nft-marketplace/src/Plutus/Abstract/TxUtils.hs diff --git a/MetaLamp/nft-marketplace/plutus-starter.cabal b/MetaLamp/nft-marketplace/plutus-starter.cabal index 4366518ff..bd26fd347 100644 --- a/MetaLamp/nft-marketplace/plutus-starter.cabal +++ b/MetaLamp/nft-marketplace/plutus-starter.cabal @@ -23,7 +23,7 @@ maintainer: Your email library exposed-modules: - Plutus.Contracts.NftMarketplace.OnChain.Core Plutus.Contracts.NftMarketplace.OffChain.ID Plutus.Contracts.NftMarketplace.OnChain.Core.ID Plutus.Contracts.Services.Sale.Core Ext.PlutusTx.AssocMap Plutus.Contracts.NftMarketplace.OnChain.Core.NFT Ext.Plutus.Contracts.Auction Plutus.Contracts.Services.Sale Plutus.Contracts.Services.Sale.Endpoints Plutus.Contracts.Services.Sale.StateMachine Plutus.Contracts.NftMarketplace.OffChain.Info Ext.Plutus.Ledger.Contexts Ext.Plutus.Ledger.Value Plutus.Contracts.NftMarketplace.OffChain.User Plutus.Abstract.ContractResponse Plutus.Abstract.OutputValue Plutus.Abstract.TxUtils Plutus.Contracts.NftMarketplace.Endpoints Plutus.Contracts.NftMarketplace.OffChain.Owner Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine Plutus.PAB.Simulation + Plutus.Contracts.NftMarketplace.OnChain.Core Plutus.Contracts.NftMarketplace.OffChain.ID Plutus.Contracts.NftMarketplace.OnChain.Core.ID Plutus.Contracts.Services.Sale.Core Ext.PlutusTx.AssocMap Plutus.Contracts.NftMarketplace.OnChain.Core.NFT Ext.Plutus.Contracts.Auction Plutus.Contracts.Services.Sale Plutus.Contracts.Services.Sale.Endpoints Plutus.Contracts.Services.Sale.StateMachine Plutus.Contracts.NftMarketplace.OffChain.Info Ext.Plutus.Ledger.Value Plutus.Contracts.NftMarketplace.OffChain.User Plutus.Abstract.ContractResponse Plutus.Contracts.NftMarketplace.Endpoints Plutus.Contracts.NftMarketplace.OffChain.Owner Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine Plutus.PAB.Simulation build-depends: base >= 4.9 && < 5, aeson, diff --git a/MetaLamp/nft-marketplace/src/Ext/Plutus/Contracts/Auction.hs b/MetaLamp/nft-marketplace/src/Ext/Plutus/Contracts/Auction.hs index 265250a13..4e84fd7ff 100644 --- a/MetaLamp/nft-marketplace/src/Ext/Plutus/Contracts/Auction.hs +++ b/MetaLamp/nft-marketplace/src/Ext/Plutus/Contracts/Auction.hs @@ -1,7 +1,6 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE DerivingVia #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE LambdaCase #-} diff --git a/MetaLamp/nft-marketplace/src/Ext/Plutus/Ledger/Contexts.hs b/MetaLamp/nft-marketplace/src/Ext/Plutus/Ledger/Contexts.hs deleted file mode 100644 index 6a699ec23..000000000 --- a/MetaLamp/nft-marketplace/src/Ext/Plutus/Ledger/Contexts.hs +++ /dev/null @@ -1,59 +0,0 @@ -{-# LANGUAGE NoImplicitPrelude #-} -{-# OPTIONS_GHC -fno-specialise #-} -{-# LANGUAGE NamedFieldPuns #-} - -module Ext.Plutus.Ledger.Contexts where - -import Ledger (Address (Address), - Datum (getDatum), DatumHash, - PubKeyHash, - TxInInfo (txInInfoResolved), - TxInfo (txInfoInputs), - TxOut (TxOut, txOutAddress, txOutDatumHash, txOutValue), - ValidatorHash, Value, findDatum) -import Plutus.V1.Ledger.Credential (Credential (PubKeyCredential, ScriptCredential)) -import qualified PlutusTx -import PlutusTx.Prelude (Eq ((==)), Maybe (..), filter, - find, fst, mapMaybe, mconcat, - otherwise, snd, ($), (.), (<$>), - (>>=)) - -{-# INLINABLE findOnlyOneDatumHashByValue #-} --- | Find the hash of a datum, if it is part of the script's outputs. --- Assume search failed if more than one correspondence is found. -findOnlyOneDatumHashByValue :: Value -> [(DatumHash, Value)] -> Maybe DatumHash -findOnlyOneDatumHashByValue val outs = fst <$> case filter f outs of - [res] -> Just res - _ -> Nothing - where - f (_, val') = val' == val - -{-# INLINABLE findValueByDatumHash #-} --- | Concat value of the script's outputs that have the specified hash of a datum -findValueByDatumHash :: DatumHash -> [(DatumHash, Value)] -> Value -findValueByDatumHash dh outs = mconcat $ mapMaybe f outs - where - f (dh', val) | dh' == dh = Just val - | otherwise = Nothing - -{-# INLINABLE parseDatum #-} --- | Find datum inside pending transaction and parse it from data -parseDatum :: PlutusTx.IsData a => TxInfo -> DatumHash -> Maybe a -parseDatum txInfo dh = findDatum dh txInfo >>= (PlutusTx.fromData . getDatum) - -{-# INLINABLE valueSpentFrom #-} --- | Concat value of the inputs belonging to the provided public key inside the pending transaction's inputs -valueSpentFrom :: TxInfo -> PubKeyHash -> Value -valueSpentFrom txInfo pk = - let flt TxOut {txOutAddress = Address (PubKeyCredential pk') _, txOutValue} - | pk == pk' = Just txOutValue - flt _ = Nothing - in mconcat $ mapMaybe flt (txInInfoResolved <$> txInfoInputs txInfo) - -{-# INLINABLE scriptInputsAt #-} --- | Finds all inputs belonging to a specific script inside the pending transaction's inputs -scriptInputsAt :: ValidatorHash -> TxInfo -> [(DatumHash, Value)] -scriptInputsAt h p = - let flt TxOut{txOutDatumHash=Just ds, txOutAddress=Address (ScriptCredential s) _, txOutValue} | s == h = Just (ds, txOutValue) - flt _ = Nothing - in mapMaybe flt (txInInfoResolved <$> txInfoInputs p) diff --git a/MetaLamp/nft-marketplace/src/Ext/PlutusTx/AssocMap.hs b/MetaLamp/nft-marketplace/src/Ext/PlutusTx/AssocMap.hs index 7831f03a2..36914ae50 100644 --- a/MetaLamp/nft-marketplace/src/Ext/PlutusTx/AssocMap.hs +++ b/MetaLamp/nft-marketplace/src/Ext/PlutusTx/AssocMap.hs @@ -1,14 +1,10 @@ -{-# LANGUAGE DeriveAnyClass #-} -{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE LambdaCase #-} {-# LANGUAGE MonoLocalBinds #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE UndecidableInstances #-} {-# OPTIONS_GHC -Wno-name-shadowing #-} {-# OPTIONS_GHC -fno-strictness #-} diff --git a/MetaLamp/nft-marketplace/src/Plutus/Abstract/ContractResponse.hs b/MetaLamp/nft-marketplace/src/Plutus/Abstract/ContractResponse.hs index 7484f2967..ef2ec572d 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Abstract/ContractResponse.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Abstract/ContractResponse.hs @@ -5,7 +5,6 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FunctionalDependencies #-} -{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} @@ -31,8 +30,6 @@ import Ledger.Constraints.TxConstraints as Constraints import qualified Ledger.Scripts as Scripts import qualified Ledger.Typed.Scripts as Scripts import Playground.Contract -import Plutus.Abstract.OutputValue (OutputValue (..)) -import qualified Plutus.Abstract.TxUtils as TxUtils import Plutus.Contract hiding (when) import Plutus.Contracts.Currency as Currency import Plutus.V1.Ledger.Ada (adaValueOf, lovelaceValueOf) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Abstract/OutputValue.hs b/MetaLamp/nft-marketplace/src/Plutus/Abstract/OutputValue.hs deleted file mode 100644 index 96fafabac..000000000 --- a/MetaLamp/nft-marketplace/src/Plutus/Abstract/OutputValue.hs +++ /dev/null @@ -1,19 +0,0 @@ -{-# LANGUAGE DeriveFunctor #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE FunctionalDependencies #-} -{-# LANGUAGE TemplateHaskell #-} - -module Plutus.Abstract.OutputValue where - -import Control.Lens (makeClassy_) -import Ledger (TxOutRef, TxOutTx) -import qualified PlutusTx.Prelude as PlutuxTx - -data OutputValue a = - OutputValue { - ovOutRef :: TxOutRef, - ovOutTx :: TxOutTx, - ovValue :: a - } deriving (Prelude.Show, Prelude.Functor) - -makeClassy_ ''OutputValue diff --git a/MetaLamp/nft-marketplace/src/Plutus/Abstract/TxUtils.hs b/MetaLamp/nft-marketplace/src/Plutus/Abstract/TxUtils.hs deleted file mode 100644 index b511312a0..000000000 --- a/MetaLamp/nft-marketplace/src/Plutus/Abstract/TxUtils.hs +++ /dev/null @@ -1,97 +0,0 @@ -{-# LANGUAGE ConstraintKinds #-} -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE DerivingStrategies #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE NoImplicitPrelude #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TypeFamilies #-} - -module Plutus.Abstract.TxUtils where - -import Control.Lens (review) -import Control.Monad (void) -import Data.ByteString (ByteString) -import qualified Data.Map as Map -import Data.Text (Text) -import Data.Void (Void) -import Ledger hiding (singleton) -import qualified Ledger.Constraints as Constraints -import qualified Ledger.Constraints.OnChain as Constraints -import qualified Ledger.Constraints.TxConstraints as Constraints -import Ledger.Typed.Scripts (DatumType, MonetaryPolicy, - RedeemerType, TypedValidator) -import qualified Ledger.Typed.Scripts as Scripts -import Plutus.Abstract.OutputValue (OutputValue (..)) -import Plutus.Contract -import Plutus.V1.Ledger.Contexts (ScriptContext, - scriptCurrencySymbol) -import qualified Plutus.V1.Ledger.Scripts as Scripts -import Plutus.V1.Ledger.Value (AssetClass (unAssetClass), - TokenName (..), assetClass, - assetClassValue, - assetClassValueOf) -import qualified PlutusTx -import PlutusTx.Prelude hiding (Semigroup (..)) -import Prelude (Semigroup (..)) -import qualified Prelude - -type TxPair a = (Constraints.ScriptLookups a, Constraints.TxConstraints (RedeemerType a) (DatumType a)) - -type IsScriptData a = (PlutusTx.IsData (RedeemerType a), PlutusTx.IsData (DatumType a)) - -submitTxPair :: (AsContractError e, IsScriptData a) => - TxPair a - -> Contract w s e Tx -submitTxPair = Prelude.uncurry submitTxConstraintsWith - -mustForgeValue :: (IsScriptData a) => - MonetaryPolicy - -> Value - -> TxPair a -mustForgeValue policy value = (lookups, tx) - where - lookups = Constraints.monetaryPolicy policy - tx = Constraints.mustForgeValue value - -mustPayToScript :: (IsScriptData a) => - TypedValidator a - -> PubKeyHash - -> DatumType a - -> Value - -> TxPair a -mustPayToScript script pkh datum value = (lookups, tx) - where - lookups = Constraints.ownPubKeyHash pkh <> Constraints.typedValidatorLookups script - tx = Constraints.mustPayToTheScript datum value - -mustSpendScriptOutputs :: (IsScriptData a) => - TypedValidator a - -> [OutputValue (RedeemerType a)] - -> TxPair a -mustSpendScriptOutputs script inputs = (lookups, tx) - where - unspent = Map.fromList $ fmap (\(OutputValue ref tx _) -> (ref, tx)) inputs - lookups = Constraints.otherScript (Scripts.validatorScript script) <> Constraints.unspentOutputs unspent - tx = Prelude.mconcat $ - fmap (\(OutputValue ref _ redeemer) -> Constraints.mustSpendScriptOutput ref (Redeemer $ PlutusTx.toData redeemer)) inputs - -mustSpendFromScript :: (IsScriptData a) => - TypedValidator a - -> [OutputValue (RedeemerType a)] - -> PubKeyHash - -> Value - -> TxPair a -mustSpendFromScript script inputs pkh value = (lookups, tx) <> mustSpendScriptOutputs script inputs - where - lookups = Constraints.ownPubKeyHash pkh - tx = Constraints.mustPayToPubKey pkh value - -mustRoundTripToScript :: (IsScriptData a) => - TypedValidator a - -> [OutputValue (RedeemerType a)] - -> DatumType a - -> PubKeyHash - -> Value - -> TxPair a -mustRoundTripToScript script inputs datum pkh value = mustSpendScriptOutputs script inputs <> mustPayToScript script pkh datum value diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs index ab0b28709..3120a542a 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs @@ -5,9 +5,7 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeOperators #-} diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core.hs index fad32e68a..1a32e686f 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core.hs @@ -1,6 +1,5 @@ {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE TypeApplications #-} module Plutus.Contracts.NftMarketplace.OnChain.Core ( module Export diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/NFT.hs b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/NFT.hs index 9146a13a2..91b9785e4 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/NFT.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/NFT.hs @@ -1,9 +1,7 @@ -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE NumericUnderscores #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} module Marketplace.Fixtures.NFT where diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/Script.hs b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/Script.hs index 851b154e3..26d862ccd 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/Script.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/Script.hs @@ -1,6 +1,5 @@ -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE NumericUnderscores #-} -{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE OverloadedStrings #-} module Marketplace.Fixtures.Script where diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Auction.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Auction.hs index 3989a277e..b77a59376 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Auction.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Auction.hs @@ -1,9 +1,7 @@ -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE NumericUnderscores #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeApplications #-} module Marketplace.Spec.Auction ( tests @@ -121,18 +119,18 @@ auctionValue = Auction.apAsset . Auction.fromTuple startAnAuctionParams :: Marketplace.StartAnAuctionParams startAnAuctionParams = Marketplace.StartAnAuctionParams { - saapItemId = Marketplace.UserNftId Fixtures.catTokenIpfsCid, - saapDuration = 155 + Marketplace.saapItemId = Marketplace.UserNftId Fixtures.catTokenIpfsCid, + Marketplace.saapDuration = 155 } closeLotParams :: Marketplace.CloseLotParams closeLotParams = Marketplace.CloseLotParams { - clpItemId = Marketplace.UserNftId Fixtures.catTokenIpfsCid + Marketplace.clpItemId = Marketplace.UserNftId Fixtures.catTokenIpfsCid } bidOnAuctionParams :: Marketplace.BidOnAuctionParams bidOnAuctionParams = Marketplace.BidOnAuctionParams { - boapItemId = Marketplace.UserNftId Fixtures.catTokenIpfsCid, - boapBid = fromInteger $ 25 * Fixtures.oneAdaInLovelace + Marketplace.boapItemId = Marketplace.UserNftId Fixtures.catTokenIpfsCid, + Marketplace.boapBid = fromInteger $ 25 * Fixtures.oneAdaInLovelace } startAnAuctionTrace :: Trace.EmulatorTrace (Trace.ContractHandle (ContractResponse Text Marketplace.UserContractState) Marketplace.MarketplaceUserSchema Void) @@ -254,19 +252,19 @@ errorCheckBuyer = Utils.assertCrError (Marketplace.userEndpoints Fixtures.market startAnAuctionParamsB :: Marketplace.StartAnAuctionParams startAnAuctionParamsB = Marketplace.StartAnAuctionParams { - saapItemId = Marketplace.UserBundleId Fixtures.cids, - saapDuration = 142 + Marketplace.saapItemId = Marketplace.UserBundleId Fixtures.cids, + Marketplace.saapDuration = 142 } closeLotParamsB :: Marketplace.CloseLotParams closeLotParamsB = Marketplace.CloseLotParams { - clpItemId = Marketplace.UserBundleId Fixtures.cids + Marketplace.clpItemId = Marketplace.UserBundleId Fixtures.cids } bidOnAuctionParamsB :: Marketplace.BidOnAuctionParams bidOnAuctionParamsB = Marketplace.BidOnAuctionParams { - boapItemId = Marketplace.UserBundleId Fixtures.cids, - boapBid = fromInteger $ 35 * Fixtures.oneAdaInLovelace + Marketplace.boapItemId = Marketplace.UserBundleId Fixtures.cids, + Marketplace.boapBid = fromInteger $ 35 * Fixtures.oneAdaInLovelace } startAnAuctionTraceB :: Trace.EmulatorTrace (Trace.ContractHandle (ContractResponse Text Marketplace.UserContractState) Marketplace.MarketplaceUserSchema Void) diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Bundles.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Bundles.hs index 51c5f3ba8..cb1be337f 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Bundles.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Bundles.hs @@ -1,9 +1,7 @@ -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE NumericUnderscores #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeApplications #-} module Marketplace.Spec.Bundles ( tests, bundleTrace @@ -65,10 +63,10 @@ tests = bundleUpParams :: Marketplace.BundleUpParams bundleUpParams = Marketplace.BundleUpParams { - bupIpfsCids = Fixtures.cids, - bupName = Fixtures.bundleName, - bupDescription = Fixtures.bundleDescription, - bupCategory = Fixtures.bundleCategory + Marketplace.bupIpfsCids = Fixtures.cids, + Marketplace.bupName = Fixtures.bundleName, + Marketplace.bupDescription = Fixtures.bundleDescription, + Marketplace.bupCategory = Fixtures.bundleCategory } bundleTrace :: Trace.EmulatorTrace (Trace.ContractHandle (ContractResponse Text Marketplace.UserContractState) Marketplace.MarketplaceUserSchema Void) diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/CreateNft.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/CreateNft.hs index 739590678..cd5484104 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Spec/CreateNft.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/CreateNft.hs @@ -1,9 +1,8 @@ -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE NumericUnderscores #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeApplications #-} + module Marketplace.Spec.CreateNft ( tests, createNftTrace ) where diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Sale.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Sale.hs index 0a38f30a8..21bf89ffe 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Sale.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Sale.hs @@ -1,9 +1,7 @@ -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE NumericUnderscores #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeApplications #-} module Marketplace.Spec.Sale ( tests @@ -106,13 +104,13 @@ tests = -- \/\/\/ "NFT singletons" openSaleParams :: Marketplace.OpenSaleParams openSaleParams = Marketplace.OpenSaleParams { - ospItemId = Marketplace.UserNftId Fixtures.catTokenIpfsCid, - ospSalePrice = 44 * Fixtures.oneAdaInLovelace + Marketplace.ospItemId = Marketplace.UserNftId Fixtures.catTokenIpfsCid, + Marketplace.ospSalePrice = 44 * Fixtures.oneAdaInLovelace } closeLotParams :: Marketplace.CloseLotParams closeLotParams = Marketplace.CloseLotParams { - clpItemId = Marketplace.UserNftId Fixtures.catTokenIpfsCid + Marketplace.clpItemId = Marketplace.UserNftId Fixtures.catTokenIpfsCid } openSaleTrace :: Trace.EmulatorTrace (Trace.ContractHandle (ContractResponse Text Marketplace.UserContractState) Marketplace.MarketplaceUserSchema Void) @@ -224,13 +222,13 @@ errorCheckBuyer = Utils.assertCrError (Marketplace.userEndpoints Fixtures.market -- \/\/\/ "NFT bundles" openSaleParamsB :: Marketplace.OpenSaleParams openSaleParamsB = Marketplace.OpenSaleParams { - ospItemId = Marketplace.UserBundleId Fixtures.cids, - ospSalePrice = 65 * Fixtures.oneAdaInLovelace + Marketplace.ospItemId = Marketplace.UserBundleId Fixtures.cids, + Marketplace.ospSalePrice = 65 * Fixtures.oneAdaInLovelace } closeLotParamsB :: Marketplace.CloseLotParams closeLotParamsB = Marketplace.CloseLotParams { - clpItemId = Marketplace.UserBundleId Fixtures.cids + Marketplace.clpItemId = Marketplace.UserBundleId Fixtures.cids } openSaleTraceB :: Trace.EmulatorTrace (Trace.ContractHandle (ContractResponse Text Marketplace.UserContractState) Marketplace.MarketplaceUserSchema Void) diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Start.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Start.hs index 61f15b74e..4d3dc939b 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Start.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Start.hs @@ -1,6 +1,5 @@ -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE NumericUnderscores #-} -{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE OverloadedStrings #-} module Marketplace.Spec.Start ( tests, startTrace diff --git a/MetaLamp/nft-marketplace/test/Utils/Data.hs b/MetaLamp/nft-marketplace/test/Utils/Data.hs index 4f54e18aa..028a899d3 100644 --- a/MetaLamp/nft-marketplace/test/Utils/Data.hs +++ b/MetaLamp/nft-marketplace/test/Utils/Data.hs @@ -7,9 +7,6 @@ import qualified PlutusTx.AssocMap as AssocMap import qualified PlutusTx.Prelude as PlutusTx import Wallet.Emulator.Wallet (Wallet, walletPubKey) -allSatisfy :: [a -> Bool] -> a -> Bool -allSatisfy fs a = and . fmap (a &) $ fs - one :: (a -> Bool) -> [a] -> Bool one f = foldr reducer False where diff --git a/MetaLamp/nft-marketplace/test/Utils/Trace.hs b/MetaLamp/nft-marketplace/test/Utils/Trace.hs index 565d9c488..a789d946c 100644 --- a/MetaLamp/nft-marketplace/test/Utils/Trace.hs +++ b/MetaLamp/nft-marketplace/test/Utils/Trace.hs @@ -2,7 +2,6 @@ {-# LANGUAGE MonoLocalBinds #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} -{-# LANGUAGE TypeApplications #-} module Utils.Trace where @@ -32,26 +31,6 @@ import PlutusTx (IsData, fromData) import qualified Wallet.Emulator.Folds as Folds import Wallet.Emulator.MultiAgent (EmulatorEvent) -waitForState :: - (Show a - , Show e - , Trace.ContractConstraints s - , JSON.FromJSON e - , JSON.FromJSON a - , JSON.ToJSON e - , JSON.ToJSON a - , JSON.FromJSON e' - ) - => (a -> Maybe b) -> - Trace.ContractHandle (ContractResponse e a) s e' -> - Trace.EmulatorTrace b -waitForState pick userHandle = do - res <- Trace.observableState userHandle - case res of - CrSuccess s -> maybe (throwError . GenericError $ "Unexpected state: " <> show s) pure (pick s) - CrError e -> throwError . GenericError . show $ e - CrPending -> Trace.waitNSlots 1 >> waitForState pick userHandle - assertCrError :: forall e r s err a. (Show r, Show e) => C.Contract (ContractResponse e r) s err a -> Trace.ContractInstanceTag From e6481e80533ffe338150b0da728131be749966fd Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Thu, 19 Aug 2021 00:19:14 +0700 Subject: [PATCH 060/217] fix ide hints --- .../test/Marketplace/Fixtures/CheckOptions.hs | 12 ++------ .../test/Marketplace/Fixtures/NFT.hs | 18 ------------ .../test/Marketplace/Fixtures/Script.hs | 15 ++-------- .../test/Marketplace/Spec/Auction.hs | 28 +++++++------------ .../test/Marketplace/Spec/Bundles.hs | 23 ++++----------- .../test/Marketplace/Spec/CreateNft.hs | 21 +++++--------- .../test/Marketplace/Spec/Sale.hs | 24 ++++++---------- .../test/Marketplace/Spec/Start.hs | 7 +---- MetaLamp/nft-marketplace/test/Utils/Data.hs | 8 ++---- MetaLamp/nft-marketplace/test/Utils/Trace.hs | 24 +++------------- 10 files changed, 43 insertions(+), 137 deletions(-) diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/CheckOptions.hs b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/CheckOptions.hs index 38ae6eff2..ee17d0951 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/CheckOptions.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/CheckOptions.hs @@ -1,26 +1,20 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TupleSections #-} module Marketplace.Fixtures.CheckOptions where import Control.Lens ((&), (.~)) -import qualified Control.Lens as Lens -import Control.Monad (void) import qualified Data.Map as Map -import Data.Text (Text) -import Ledger +import Ledger (Value) import qualified Ledger.Ada as Ada import qualified Ledger.Value as V import qualified Marketplace.Fixtures.Script as Fixtures import qualified Marketplace.Fixtures.Wallet as Fixtures -import Plutus.Contract import Plutus.Contract.Test -import qualified Plutus.Contracts.NftMarketplace.Endpoints as Marketplace import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace import qualified Plutus.Trace as Trace -import qualified PlutusTx.AssocMap as AssocMap -import Test.Tasty options :: CheckOptions options = defaultCheckOptions & emulatorConfig .~ emulatorCfg @@ -29,7 +23,7 @@ options = defaultCheckOptions & emulatorConfig .~ emulatorCfg emulatorCfg :: Trace.EmulatorConfig emulatorCfg = Trace.EmulatorConfig $ Left $ - Map.singleton Fixtures.ownerWallet v <> Map.fromList ((\w -> (w, Ada.lovelaceValueOf 1_000_000_000)) <$> Fixtures.userWallets) + Map.singleton Fixtures.ownerWallet v <> Map.fromList ((, Ada.lovelaceValueOf 1_000_000_000) <$> Fixtures.userWallets) v :: Value v = Ada.lovelaceValueOf 1_000_000_000 <> diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/NFT.hs b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/NFT.hs index 91b9785e4..c603fc0fd 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/NFT.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/NFT.hs @@ -5,27 +5,9 @@ module Marketplace.Fixtures.NFT where -import Control.Lens (_2, (&), (.~), - (^.), (^?)) -import qualified Control.Lens as Lens -import Control.Monad (void) -import qualified Data.Map as Map -import Data.Maybe (isNothing) -import Data.Text (Text) -import Ledger -import qualified Ledger.Ada as Ada -import qualified Ledger.Value as V -import Plutus.Contract -import Plutus.Contract.Test -import qualified Plutus.Contracts.NftMarketplace.Endpoints as Marketplace import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace -import qualified Plutus.Trace as Trace -import qualified PlutusTx.AssocMap as AssocMap import PlutusTx.Builtins (sha2_256) import PlutusTx.Prelude (ByteString) -import Test.Tasty -import qualified Utils -import Wallet.Emulator.Wallet cids :: [Marketplace.IpfsCid] cids = [catTokenIpfsCid, photoTokenIpfsCid] diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/Script.hs b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/Script.hs index 26d862ccd..5f55743e3 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/Script.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/Script.hs @@ -3,21 +3,10 @@ module Marketplace.Fixtures.Script where -import Control.Lens ((&), (.~)) -import qualified Control.Lens as Lens -import Control.Monad (void) -import qualified Data.Map as Map -import Data.Text (Text) -import Ledger -import qualified Ledger.Ada as Ada +import Ledger (Address, + CurrencySymbol) import qualified Ledger.Value as V -import Plutus.Contract -import Plutus.Contract.Test -import qualified Plutus.Contracts.NftMarketplace.Endpoints as Marketplace import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace -import qualified Plutus.Trace as Trace -import qualified PlutusTx.AssocMap as AssocMap -import Test.Tasty marketplace :: Marketplace.Marketplace marketplace = diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Auction.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Auction.hs index b77a59376..14e5a4d04 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Auction.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Auction.hs @@ -7,37 +7,29 @@ module Marketplace.Spec.Auction ( tests ) where -import Control.Lens (_2, _Left, - _Right, (&), - (.~), (^.), (^?)) -import qualified Control.Lens as Lens +import Control.Lens (_2, _Right, (&), + (^.), (^?)) import Control.Monad (void) import Data.Foldable (find) -import Data.Function (on) -import qualified Data.Map as Map import Data.Maybe (isNothing) import Data.Text (Text) -import Data.Void +import Data.Void (Void) import qualified Ext.Plutus.Contracts.Auction as Auction -import Ledger -import qualified Ledger.Ada as Ada +import Ledger (Value) import qualified Ledger.Value as V import qualified Marketplace.Fixtures as Fixtures import qualified Marketplace.Spec.Bundles as Bundles import qualified Marketplace.Spec.CreateNft as CreateNft import qualified Marketplace.Spec.Start as Start -import Plutus.Abstract.ContractResponse -import Plutus.Contract +import Plutus.Abstract.ContractResponse (ContractResponse) import Plutus.Contract.Test import qualified Plutus.Contracts.NftMarketplace.Endpoints as Marketplace import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace import qualified Plutus.Trace as Trace import qualified PlutusTx.AssocMap as AssocMap -import PlutusTx.Builtins (sha2_256) -import PlutusTx.Prelude (ByteString) import Test.Tasty import qualified Utils -import Wallet.Emulator.Wallet +import Wallet.Emulator.Wallet (walletAddress) tests :: TestTree tests = @@ -207,7 +199,7 @@ startAnAuctionDatumsCheck = where nftIsOnAuction = maybe False (\t -> t ^. Marketplace._nftLot ^? traverse . _2 . _Right & fmap auctionValue & (== Just (Marketplace.nftValue Fixtures.catTokenIpfsCid t))) . - (AssocMap.lookup Fixtures.catTokenIpfsCidHash) + AssocMap.lookup Fixtures.catTokenIpfsCidHash completeAuctionDatumsCheck :: TracePredicate completeAuctionDatumsCheck = @@ -216,7 +208,7 @@ completeAuctionDatumsCheck = (nftNotOnAuction . Marketplace.mdSingletons) where nftNotOnAuction = maybe False (isNothing . Marketplace.nftLot) . - (AssocMap.lookup Fixtures.catTokenIpfsCidHash) + AssocMap.lookup Fixtures.catTokenIpfsCidHash startAnAuctionValueCheck :: TracePredicate startAnAuctionValueCheck = @@ -322,7 +314,7 @@ startAnAuctionDatumsCheckB = where bundleIsOnAuction = maybe False (\b -> b ^. Marketplace._nbTokens ^? Marketplace._HasLot . _2 . _Right & fmap auctionValue & (== Just (Marketplace.bundleValue AssocMap.empty b))) . - (AssocMap.lookup Fixtures.bundleId) + AssocMap.lookup Fixtures.bundleId completeAuctionDatumsCheckB :: TracePredicate completeAuctionDatumsCheckB = @@ -331,7 +323,7 @@ completeAuctionDatumsCheckB = (bundleNotOnAuction . Marketplace.mdBundles) where bundleNotOnAuction = maybe False (Prelude.not . Marketplace.hasLotBundle) . - (AssocMap.lookup Fixtures.bundleId) + AssocMap.lookup Fixtures.bundleId startAnAuctionValueCheckB :: TracePredicate startAnAuctionValueCheckB = diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Bundles.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Bundles.hs index cb1be337f..7babb9524 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Bundles.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Bundles.hs @@ -7,32 +7,21 @@ module Marketplace.Spec.Bundles ( tests, bundleTrace ) where -import Control.Lens (_2, (&), (.~), - (^.), (^?)) -import qualified Control.Lens as Lens +import Control.Lens ((^.), (^?)) import Control.Monad (void) -import Data.Function (on) -import qualified Data.Map as Map import Data.Maybe (isNothing) import Data.Text (Text) -import Data.Void -import Ledger -import qualified Ledger.Ada as Ada -import qualified Ledger.Value as V +import Data.Void (Void) import qualified Marketplace.Fixtures as Fixtures import qualified Marketplace.Spec.Start as Start -import Plutus.Abstract.ContractResponse -import Plutus.Contract +import Plutus.Abstract.ContractResponse (ContractResponse) import Plutus.Contract.Test import qualified Plutus.Contracts.NftMarketplace.Endpoints as Marketplace import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace import qualified Plutus.Trace as Trace import qualified PlutusTx.AssocMap as AssocMap -import PlutusTx.Builtins (sha2_256) -import PlutusTx.Prelude (ByteString) import Test.Tasty import qualified Utils -import Wallet.Emulator.Wallet tests :: TestTree tests = @@ -114,9 +103,9 @@ bundleDatumsCheck = (containsBundle . Marketplace.mdBundles) where containsBundle = maybe False checkBundle . - (AssocMap.lookup Fixtures.bundleId) + AssocMap.lookup Fixtures.bundleId checkBundle b = maybe False containsNfts (b ^? Marketplace._nbTokens . Marketplace._NoLot) && - (b ^. Marketplace._nbRecord == Fixtures.bundleInfo) + b ^. Marketplace._nbRecord == Fixtures.bundleInfo containsNfts b = maybe False Fixtures.hasCatTokenRecord (AssocMap.lookup Fixtures.catTokenIpfsCidHash b) && maybe False Fixtures.hasPhotoTokenRecord @@ -150,7 +139,7 @@ unbundleDatumsCheck = Fixtures.marketplaceAddress $ \mp -> (containsNoBundle . Marketplace.mdBundles $ mp) && (containsNfts . Marketplace.mdSingletons $ mp) where - containsNoBundle = isNothing . (AssocMap.lookup Fixtures.bundleId) + containsNoBundle = isNothing . AssocMap.lookup Fixtures.bundleId containsNfts store = maybe False (Fixtures.hasCatTokenRecord . Marketplace.nftRecord) (AssocMap.lookup Fixtures.catTokenIpfsCidHash store) && maybe False (Fixtures.hasPhotoTokenRecord . Marketplace.nftRecord) diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/CreateNft.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/CreateNft.hs index cd5484104..bdb7892de 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Spec/CreateNft.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/CreateNft.hs @@ -8,30 +8,23 @@ module Marketplace.Spec.CreateNft ) where import Control.Lens (_2, (&), (.~), - (^.), (^?)) -import qualified Control.Lens as Lens + (^.)) import Control.Monad (void) -import qualified Data.Map as Map import Data.Maybe (isNothing) import Data.Text (Text) -import Data.Void -import Ledger -import qualified Ledger.Ada as Ada +import Data.Void (Void) import qualified Ledger.Value as V import qualified Marketplace.Fixtures as Fixtures import qualified Marketplace.Spec.Start as Start -import Plutus.Abstract.ContractResponse -import Plutus.Contract +import Plutus.Abstract.ContractResponse (ContractResponse) import Plutus.Contract.Test import qualified Plutus.Contracts.NftMarketplace.Endpoints as Marketplace import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace import qualified Plutus.Trace as Trace import qualified PlutusTx.AssocMap as AssocMap -import PlutusTx.Builtins (sha2_256) -import PlutusTx.Prelude (ByteString) import Test.Tasty import qualified Utils -import Wallet.Emulator.Wallet +import Wallet.Emulator.Wallet (walletAddress) tests :: TestTree tests = @@ -83,7 +76,7 @@ datumsCheck = containsNft = maybe False (\t -> (t ^. Marketplace._nftLot & isNothing) && (t ^. Marketplace._nftRecord . Marketplace._niIssuer & isNothing) && (t ^. Marketplace._nftRecord & Fixtures.hasCatTokenRecord)) . - (AssocMap.lookup Fixtures.catTokenIpfsCidHash) + AssocMap.lookup Fixtures.catTokenIpfsCidHash datumsCheck' :: TracePredicate datumsCheck' = @@ -92,9 +85,9 @@ datumsCheck' = (containsNft . Marketplace.mdSingletons) where containsNft = maybe False (\t -> (t ^. Marketplace._nftLot & isNothing) && - (t ^. Marketplace._nftRecord . Marketplace._niIssuer == Just (Utils.walletPkh Fixtures.userWallet)) && + t ^. Marketplace._nftRecord . Marketplace._niIssuer == Just (Utils.walletPkh Fixtures.userWallet) && (t ^. Marketplace._nftRecord & Fixtures.hasCatTokenRecord)) . - (AssocMap.lookup Fixtures.catTokenIpfsCidHash) + AssocMap.lookup Fixtures.catTokenIpfsCidHash valueCheck :: TracePredicate valueCheck = diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Sale.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Sale.hs index 21bf89ffe..5ee05b5a3 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Sale.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Sale.hs @@ -8,35 +8,27 @@ module Marketplace.Spec.Sale ) where import Control.Lens (_2, _Left, (&), - (.~), (^.), (^?)) -import qualified Control.Lens as Lens + (^.), (^?)) import Control.Monad (void) import Data.Foldable (find) -import Data.Function (on) -import qualified Data.Map as Map import Data.Maybe (isNothing) import Data.Text (Text) -import Data.Void -import Ledger -import qualified Ledger.Ada as Ada +import Data.Void (Void) import qualified Ledger.Value as V import qualified Marketplace.Fixtures as Fixtures import qualified Marketplace.Spec.Bundles as Bundles import qualified Marketplace.Spec.CreateNft as CreateNft import qualified Marketplace.Spec.Start as Start -import Plutus.Abstract.ContractResponse -import Plutus.Contract +import Plutus.Abstract.ContractResponse (ContractResponse) import Plutus.Contract.Test import qualified Plutus.Contracts.NftMarketplace.Endpoints as Marketplace import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace import qualified Plutus.Contracts.Services.Sale as Sale import qualified Plutus.Trace as Trace import qualified PlutusTx.AssocMap as AssocMap -import PlutusTx.Builtins (sha2_256) -import PlutusTx.Prelude (ByteString) import Test.Tasty import qualified Utils -import Wallet.Emulator.Wallet +import Wallet.Emulator.Wallet (walletAddress) tests :: TestTree tests = @@ -178,7 +170,7 @@ openSaleDatumsCheck = where nftIsOnSale = maybe False (\t -> t ^. Marketplace._nftLot ^? traverse . _2 . _Left & fmap Sale.saleValue & (== Just (Marketplace.nftValue Fixtures.catTokenIpfsCid t))) . - (AssocMap.lookup Fixtures.catTokenIpfsCidHash) + AssocMap.lookup Fixtures.catTokenIpfsCidHash completeSaleDatumsCheck :: TracePredicate completeSaleDatumsCheck = @@ -187,7 +179,7 @@ completeSaleDatumsCheck = (nftNotOnSale . Marketplace.mdSingletons) where nftNotOnSale = maybe False (isNothing . Marketplace.nftLot) . - (AssocMap.lookup Fixtures.catTokenIpfsCidHash) + AssocMap.lookup Fixtures.catTokenIpfsCidHash openSaleValueCheck :: TracePredicate openSaleValueCheck = @@ -277,7 +269,7 @@ openSaleDatumsCheckB = where bundleIsOnSale = maybe False (\b -> b ^. Marketplace._nbTokens ^? Marketplace._HasLot . _2 . _Left & fmap Sale.saleValue & (== Just (Marketplace.bundleValue AssocMap.empty b))) . - (AssocMap.lookup Fixtures.bundleId) + AssocMap.lookup Fixtures.bundleId completeSaleDatumsCheckB :: TracePredicate completeSaleDatumsCheckB = @@ -286,7 +278,7 @@ completeSaleDatumsCheckB = (bundleNotOnSale . Marketplace.mdBundles) where bundleNotOnSale = maybe False (Prelude.not . Marketplace.hasLotBundle) . - (AssocMap.lookup Fixtures.bundleId) + AssocMap.lookup Fixtures.bundleId openSaleValueCheckB :: TracePredicate openSaleValueCheckB = diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Start.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Start.hs index 4d3dc939b..afaa85bd6 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Start.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Start.hs @@ -5,16 +5,11 @@ module Marketplace.Spec.Start ( tests, startTrace ) where -import Control.Lens ((&), (.~)) -import qualified Control.Lens as Lens import Control.Monad (void) -import qualified Data.Map as Map import Data.Text (Text) -import Ledger -import qualified Ledger.Ada as Ada import qualified Ledger.Value as V import qualified Marketplace.Fixtures as Fixtures -import Plutus.Contract +import Plutus.Contract (Contract) import Plutus.Contract.Test import qualified Plutus.Contracts.NftMarketplace.Endpoints as Marketplace import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace diff --git a/MetaLamp/nft-marketplace/test/Utils/Data.hs b/MetaLamp/nft-marketplace/test/Utils/Data.hs index 028a899d3..94844a487 100644 --- a/MetaLamp/nft-marketplace/test/Utils/Data.hs +++ b/MetaLamp/nft-marketplace/test/Utils/Data.hs @@ -1,11 +1,7 @@ module Utils.Data where -import Data.Function ((&)) -import Plutus.Abstract.ContractResponse (ContractResponse (..)) -import Plutus.V1.Ledger.Crypto (PubKeyHash, pubKeyHash) -import qualified PlutusTx.AssocMap as AssocMap -import qualified PlutusTx.Prelude as PlutusTx -import Wallet.Emulator.Wallet (Wallet, walletPubKey) +import Plutus.V1.Ledger.Crypto (PubKeyHash, pubKeyHash) +import Wallet.Emulator.Wallet (Wallet, walletPubKey) one :: (a -> Bool) -> [a] -> Bool one f = foldr reducer False diff --git a/MetaLamp/nft-marketplace/test/Utils/Trace.hs b/MetaLamp/nft-marketplace/test/Utils/Trace.hs index a789d946c..ca756fb92 100644 --- a/MetaLamp/nft-marketplace/test/Utils/Trace.hs +++ b/MetaLamp/nft-marketplace/test/Utils/Trace.hs @@ -5,31 +5,15 @@ module Utils.Trace where -import qualified Control.Foldl as L -import Control.Monad (unless) -import Control.Monad.Freer.Error (throwError) -import Control.Monad.Freer.Writer (tell) -import qualified Data.Aeson as JSON -import Control.Lens (_2, (&), (.~), (^.), (^?)) -import qualified Control.Lens as Lens -import qualified Data.Map as Map -import Data.Maybe (isJust, mapMaybe) -import Data.String (fromString) -import Data.Text.Prettyprint.Doc (Doc) -import Data.Void (Void) -import Ledger (Address) -import qualified Ledger -import Ledger.AddressMap (UtxoMap) -import Plutus.Abstract.ContractResponse +import Control.Lens ((^?)) +import Data.Maybe (isJust) +import Plutus.Abstract.ContractResponse (AsContractResponse (_CrError), + ContractResponse) import qualified Plutus.Contract as C import Plutus.Contract.Test (TracePredicate, assertAccumState) import qualified Plutus.Trace.Emulator as Trace -import Plutus.Trace.Emulator.Types (EmulatorRuntimeError (..)) -import PlutusTx (IsData, fromData) -import qualified Wallet.Emulator.Folds as Folds -import Wallet.Emulator.MultiAgent (EmulatorEvent) assertCrError :: forall e r s err a. (Show r, Show e) => C.Contract (ContractResponse e r) s err a From caa9682e3dcf7b523e955c2c7279ede6c4da40c8 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Thu, 19 Aug 2021 18:25:12 +0700 Subject: [PATCH 061/217] add cabal apps interacting with client --- .../nft-marketplace/generate-purs/Main.hs | 173 ++++++++++++++++++ .../generate-purs/MarketplaceTypes.hs | 70 +++++++ MetaLamp/nft-marketplace/pab/Main.hs | 4 + MetaLamp/nft-marketplace/plutus-starter.cabal | 39 ++++ .../src/Plutus/PAB/Simulation.hs | 9 + 5 files changed, 295 insertions(+) create mode 100644 MetaLamp/nft-marketplace/generate-purs/Main.hs create mode 100644 MetaLamp/nft-marketplace/generate-purs/MarketplaceTypes.hs create mode 100644 MetaLamp/nft-marketplace/pab/Main.hs diff --git a/MetaLamp/nft-marketplace/generate-purs/Main.hs b/MetaLamp/nft-marketplace/generate-purs/Main.hs new file mode 100644 index 000000000..5aa41d505 --- /dev/null +++ b/MetaLamp/nft-marketplace/generate-purs/Main.hs @@ -0,0 +1,173 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE EmptyDataDecls #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeFamilies #-} + +module Main where + +import MarketplaceTypes (marketplaceTypes, + ratioBridge) +import Cardano.Metadata.Types (AnnotatedSignature, + HashFunction, + Property, + PropertyKey, + Subject, + SubjectProperties) +import Cardano.Wallet.Types (WalletInfo) +import Control.Applicative ((<|>)) +import Control.Lens (set, view, (&)) +import Control.Monad (when) +import Control.Monad.Freer.Extras.Log (LogLevel, + LogMessage) +import Data.Proxy (Proxy (Proxy)) +import qualified Data.Text as Text +import Language.PureScript.Bridge (BridgePart, + Language (Haskell), + SumType, + TypeInfo (TypeInfo), + buildBridge, equal, + genericShow, + haskType, + mkSumType, order, + typeModule, + typeName, + writePSTypesWith, + (^==)) +import Language.PureScript.Bridge.CodeGenSwitches (ForeignOptions (ForeignOptions), + genForeign, + unwrapSingleConstructors) +import Language.PureScript.Bridge.TypeParameters (A) +import qualified PSGenerator.Common +import Plutus.Contract.Checkpoint (CheckpointKey, + CheckpointStore, + CheckpointStoreItem) +import Plutus.Contract.Effects (TxConfirmed) +import Plutus.Contract.Resumable (Responses) +import Plutus.PAB.Effects.Contract.ContractExe (ContractExe) +import Plutus.PAB.Events.ContractInstanceState (PartiallyDecodedResponse) +import qualified Plutus.PAB.Webserver.API as API +import Plutus.PAB.Webserver.Types (ChainReport, + CombinedWSStreamToClient, + CombinedWSStreamToServer, + ContractActivationArgs, + ContractInstanceClientState, + ContractReport, + ContractSignatureResponse, + FullReport, + InstanceStatusToClient) +import Plutus.V1.Ledger.Value (AssetClass, + TokenName (..)) +import Servant ((:<|>)) +import Servant.PureScript (HasBridge, + Settings, + _generateSubscriberAPI, + apiModuleName, + defaultBridge, + defaultSettings, + languageBridge, + writeAPIModuleWithSettings) +import System.Directory (doesDirectoryExist, + removeDirectoryRecursive) +import Wallet.Emulator.Wallet (Wallet (..)) + +myBridge :: BridgePart +myBridge = + PSGenerator.Common.aesonBridge <|> + PSGenerator.Common.containersBridge <|> + PSGenerator.Common.languageBridge <|> + PSGenerator.Common.ledgerBridge <|> + PSGenerator.Common.servantBridge <|> + PSGenerator.Common.miscBridge <|> + ratioBridge <|> + metadataBridge <|> + defaultBridge + +-- Some of the metadata types have a datakind type parameter that +-- PureScript won't support, so we must drop it. +metadataBridge :: BridgePart +metadataBridge = do + (typeName ^== "Property") + <|> (typeName ^== "SubjectProperties") + <|> (typeName ^== "AnnotatedSignature") + typeModule ^== "Cardano.Metadata.Types" + moduleName <- view (haskType . typeModule) + name <- view (haskType . typeName) + pure $ TypeInfo "plutus-pab" moduleName name [] + +data MyBridge + +myBridgeProxy :: Proxy MyBridge +myBridgeProxy = Proxy + +instance HasBridge MyBridge where + languageBridge _ = buildBridge myBridge + +myTypes :: [SumType 'Haskell] +myTypes = + marketplaceTypes <> + PSGenerator.Common.ledgerTypes <> + PSGenerator.Common.playgroundTypes <> + PSGenerator.Common.walletTypes <> + [ (equal <*> (genericShow <*> mkSumType)) (Proxy @ContractExe) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @(FullReport A)) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @ChainReport) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @(ContractReport A)) + , (equal <*> (genericShow <*> mkSumType)) + (Proxy @(ContractSignatureResponse A)) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @(PartiallyDecodedResponse A)) + + -- Contract request / response types + , (equal <*> (genericShow <*> mkSumType)) (Proxy @TxConfirmed) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @CheckpointStore) + , (order <*> (genericShow <*> mkSumType)) (Proxy @CheckpointKey) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @(CheckpointStoreItem A)) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @(Responses A)) + + -- Logging types + , (equal <*> (genericShow <*> mkSumType)) (Proxy @(LogMessage A)) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @LogLevel) + + -- Metadata types + , (order <*> (genericShow <*> mkSumType)) (Proxy @Subject) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @(SubjectProperties A)) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @(Property A)) + , (order <*> (genericShow <*> mkSumType)) (Proxy @PropertyKey) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @HashFunction) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @(AnnotatedSignature A)) + + -- * Web API types + , (equal <*> (genericShow <*> mkSumType)) (Proxy @(ContractActivationArgs A)) + , (genericShow <*> mkSumType) (Proxy @(ContractInstanceClientState A)) + , (genericShow <*> mkSumType) (Proxy @InstanceStatusToClient) + , (genericShow <*> mkSumType) (Proxy @CombinedWSStreamToClient) + , (genericShow <*> mkSumType) (Proxy @CombinedWSStreamToServer) + , (genericShow <*> mkSumType) (Proxy @WalletInfo) + ] + +mySettings :: Settings +mySettings = + (defaultSettings & set apiModuleName "Plutus.PAB.Webserver") + {_generateSubscriberAPI = False} + +defaultWallet :: Wallet +defaultWallet = Wallet 1 +------------------------------------------------------------ + +generateTo :: FilePath -> IO () +generateTo outputDir = do + exists <- doesDirectoryExist outputDir + when exists $ removeDirectoryRecursive outputDir + writePSTypesWith + (genForeign (ForeignOptions {unwrapSingleConstructors = True})) + outputDir + (buildBridge myBridge) + myTypes + +main :: IO () +main = generateTo "client/generated" diff --git a/MetaLamp/nft-marketplace/generate-purs/MarketplaceTypes.hs b/MetaLamp/nft-marketplace/generate-purs/MarketplaceTypes.hs new file mode 100644 index 000000000..fe6aa2408 --- /dev/null +++ b/MetaLamp/nft-marketplace/generate-purs/MarketplaceTypes.hs @@ -0,0 +1,70 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} + +module MarketplaceTypes where + +import Control.Monad.Reader (MonadReader) +import Data.Proxy (Proxy (Proxy)) +import Language.PureScript.Bridge (BridgePart, + Language (Haskell), + PSType, SumType, + TypeInfo (TypeInfo), + buildBridge, + equal, + genericShow, + haskType, + mkSumType, order, + psTypeParameters, + typeModule, + typeName, + writePSTypesWith, + (^==)) +import Language.PureScript.Bridge.Builder (BridgeData) +import Language.PureScript.Bridge.TypeParameters (A, E) +import qualified PSGenerator.Common +import Plutus.Abstract.ContractResponse (ContractResponse) +import qualified Plutus.Contracts.NftMarketplace.Endpoints as Marketplace +import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace +import Plutus.PAB.Simulation (MarketplaceContracts (..)) +import Plutus.V1.Ledger.Value (AssetClass) + +ratioBridge :: BridgePart +ratioBridge = do + typeName ^== "Ratio" + typeModule ^== "PlutusTx.Ratio" + psRatio + +psRatio :: MonadReader BridgeData m => m PSType +psRatio = expand <$> psTypeParameters + where + expand [x] = TypeInfo "web-common" "Data.Json.JsonTuple" "JsonTuple" [x, x] + +marketplaceTypes :: [SumType 'Haskell] +marketplaceTypes = [ (equal <*> (genericShow <*> mkSumType)) (Proxy @MarketplaceContracts) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.Marketplace) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @(ContractResponse E A)) + , (order <*> (equal <*> (genericShow <*> mkSumType))) (Proxy @AssetClass) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.UserContractState) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.InfoContractState) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.MarketplaceDatum) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.NftInfo) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.NFT) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.Bundle) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.BundleInfo) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.NftBundle) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.UserItemId) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.CreateNftParams) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.OpenSaleParams) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.CloseLotParams) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.StartAnAuctionParams) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.BidOnAuctionParams) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.BundleUpParams) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.UnbundleParams) ] diff --git a/MetaLamp/nft-marketplace/pab/Main.hs b/MetaLamp/nft-marketplace/pab/Main.hs new file mode 100644 index 000000000..2cbe5106a --- /dev/null +++ b/MetaLamp/nft-marketplace/pab/Main.hs @@ -0,0 +1,4 @@ +import Plutus.PAB.Simulation (startMpServer) + +main :: IO () +main = startMpServer diff --git a/MetaLamp/nft-marketplace/plutus-starter.cabal b/MetaLamp/nft-marketplace/plutus-starter.cabal index bd26fd347..c76e3c78a 100644 --- a/MetaLamp/nft-marketplace/plutus-starter.cabal +++ b/MetaLamp/nft-marketplace/plutus-starter.cabal @@ -52,6 +52,15 @@ library -- See Plutus Tx readme -fobject-code -fno-ignore-interface-pragmas -fno-omit-interface-pragmas +executable pab + main-is: Main.hs + hs-source-dirs: pab + ghc-options: + -threaded + build-depends: + base >= 4.9 && < 5, + plutus-starter + executable pab-simulation main-is: Main.hs hs-source-dirs: pab-simulation @@ -61,6 +70,36 @@ executable pab-simulation base >= 4.9 && < 5, plutus-starter +executable generate-purs + main-is: Main.hs + hs-source-dirs: generate-purs + other-modules: MarketplaceTypes + ghc-options: + -threaded + build-depends: + base >= 4.9 && < 5, + aeson, + directory, + servant-purescript, + filepath, + servant-server, + bytestring, + aeson-pretty, + freer-extras, + lens, + text, + mtl, + -- Plutus: + plutus-ledger-api, + purescript-bridge, + playground-common, + plutus-starter, + plutus-pab, + plutus-contract, + plutus-use-cases, + plutus-ledger, + plutus-tx + test-suite test type: exitcode-stdio-1.0 main-is: Main.hs diff --git a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs index eb74db9a2..887fc2e68 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs @@ -92,6 +92,15 @@ activateContracts = do pure $ ContractIDs users cidInfo +startMpServer :: IO () +startMpServer = void $ Simulator.runSimulationWith handlers $ do + Simulator.logString @(Builtin MarketplaceContracts) "Starting NFT Marketplace PAB webserver on port 8080. Press enter to exit." + shutdown <- PAB.Server.startServerDebug + _ <- activateContracts + Simulator.logString @(Builtin MarketplaceContracts) "NFT Marketplace PAB webserver started on port 8080. Initialization complete. Press enter to exit." + _ <- liftIO getLine + shutdown + runNftMarketplace :: IO () runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do Simulator.logString @(Builtin MarketplaceContracts) "Starting Marketplace PAB webserver on port 8080. Press enter to exit." From 2cca670a2e2465b62e7151dfddb2a05404bd6a8f Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Fri, 20 Aug 2021 17:57:10 +0700 Subject: [PATCH 062/217] wip add client --- MetaLamp/nft-marketplace/client/.gitignore | 15 + MetaLamp/nft-marketplace/client/README.md | 52 + MetaLamp/nft-marketplace/client/entry.js | 9 + .../nft-marketplace/client/package-lock.json | 7019 +++++++++++++++++ MetaLamp/nft-marketplace/client/package.json | 42 + .../nft-marketplace/client/packages.dhall | 178 + .../client/scripts/fetch-plutus-purs.sh | 9 + .../client/scripts/generate-purs.sh | 2 + .../client/scripts/start-chrome.sh | 1 + MetaLamp/nft-marketplace/client/spago.dhall | 38 + .../nft-marketplace/client/src/AppAff.purs | 90 + .../client/src/Business/Marketplace.purs | 54 + .../client/src/Business/MarketplaceInfo.purs | 39 + .../client/src/Capability/Contract.purs | 45 + .../client/src/Capability/LogMessages.purs | 13 + .../client/src/Capability/PollContract.purs | 76 + .../client/src/Component/MainPage.purs | 53 + .../client/src/Component/MainPage.scss | 17 + .../client/src/Component/Utils.purs | 22 + MetaLamp/nft-marketplace/client/src/Main.purs | 21 + .../nft-marketplace/client/src/Utils/BEM.purs | 9 + .../client/src/Utils/WithRemoteData.purs | 18 + .../client/src/View/FundsTable.purs | 26 + .../client/src/View/RemoteDataState.purs | 12 + .../client/src/View/Utils.purs | 8 + .../nft-marketplace/client/static/favicon.ico | Bin 0 -> 1406 bytes .../nft-marketplace/client/static/index.html | 9 + .../nft-marketplace/client/webpack.config.js | 118 + .../generate-purs/MarketplaceTypes.hs | 9 +- .../Contracts/NftMarketplace/OffChain/Info.hs | 3 + 30 files changed, 8005 insertions(+), 2 deletions(-) create mode 100644 MetaLamp/nft-marketplace/client/.gitignore create mode 100644 MetaLamp/nft-marketplace/client/README.md create mode 100644 MetaLamp/nft-marketplace/client/entry.js create mode 100644 MetaLamp/nft-marketplace/client/package-lock.json create mode 100644 MetaLamp/nft-marketplace/client/package.json create mode 100644 MetaLamp/nft-marketplace/client/packages.dhall create mode 100755 MetaLamp/nft-marketplace/client/scripts/fetch-plutus-purs.sh create mode 100755 MetaLamp/nft-marketplace/client/scripts/generate-purs.sh create mode 100755 MetaLamp/nft-marketplace/client/scripts/start-chrome.sh create mode 100644 MetaLamp/nft-marketplace/client/spago.dhall create mode 100644 MetaLamp/nft-marketplace/client/src/AppAff.purs create mode 100644 MetaLamp/nft-marketplace/client/src/Business/Marketplace.purs create mode 100644 MetaLamp/nft-marketplace/client/src/Business/MarketplaceInfo.purs create mode 100644 MetaLamp/nft-marketplace/client/src/Capability/Contract.purs create mode 100644 MetaLamp/nft-marketplace/client/src/Capability/LogMessages.purs create mode 100644 MetaLamp/nft-marketplace/client/src/Capability/PollContract.purs create mode 100644 MetaLamp/nft-marketplace/client/src/Component/MainPage.purs create mode 100644 MetaLamp/nft-marketplace/client/src/Component/MainPage.scss create mode 100644 MetaLamp/nft-marketplace/client/src/Component/Utils.purs create mode 100644 MetaLamp/nft-marketplace/client/src/Main.purs create mode 100644 MetaLamp/nft-marketplace/client/src/Utils/BEM.purs create mode 100644 MetaLamp/nft-marketplace/client/src/Utils/WithRemoteData.purs create mode 100644 MetaLamp/nft-marketplace/client/src/View/FundsTable.purs create mode 100644 MetaLamp/nft-marketplace/client/src/View/RemoteDataState.purs create mode 100644 MetaLamp/nft-marketplace/client/src/View/Utils.purs create mode 100644 MetaLamp/nft-marketplace/client/static/favicon.ico create mode 100644 MetaLamp/nft-marketplace/client/static/index.html create mode 100644 MetaLamp/nft-marketplace/client/webpack.config.js diff --git a/MetaLamp/nft-marketplace/client/.gitignore b/MetaLamp/nft-marketplace/client/.gitignore new file mode 100644 index 000000000..b9ced14b5 --- /dev/null +++ b/MetaLamp/nft-marketplace/client/.gitignore @@ -0,0 +1,15 @@ +generated +generated/ +generated-docs/ +node_modules/ +output/ +yarn-error.log +yarn.lock +.psci_modules/ +.psc-package/ +.psc-package2nix/ +.spago/ +.spago2nix/ +plutus-pab.yaml +.psc-ide-port +plutus-purs/ \ No newline at end of file diff --git a/MetaLamp/nft-marketplace/client/README.md b/MetaLamp/nft-marketplace/client/README.md new file mode 100644 index 000000000..4a9bbdb90 --- /dev/null +++ b/MetaLamp/nft-marketplace/client/README.md @@ -0,0 +1,52 @@ +# Lending pool client + +The client application has a minimalistic interface to the PAB [server](/MetaLamp/lending-pool/README.md). + +## Running the project + +1. Enter the nix shell (from `lending-pool` directory): + +``` +nix-shell +``` + +Cd to `./client` folder. + + +2. Install npm packages. + +``` +npm install +``` + +3. Generate necessary PureScript code from Haskell source. This step runs an executable(`generate-purs`) from `lending-pool` directory, which requires a certain environment. The setup steps are described in `lending-pool/README`. Provided that you are able to build the backend, you can use the same approach to run purescript generation from `client` folder, i.e. + + +``` +npm run generate-purs +``` + +4. Start the client: + +``` +npm start +``` + +5. Open browser to interact with the app at https://localhost:8009/. +CORS protection needs to be disabled. You can use this script to launch chromium (note that first you need to close chromium completely, otherwise security won't be disabled): + +``` +npm run start-chrome +``` + +## Troubleshooting + +Sometimes the build results in error with Haskell IDE enabled. If the build does not work or the app behaves strangely, disable IDE and clean all source files: + +``` +cd MetaLamp/lending-pool/ && cabal clean +``` + +``` +cd MetaLamp/lending-pool/client && rm -rf node_modules/ generated/ output/ plutus-purs/ .spago/ +``` diff --git a/MetaLamp/nft-marketplace/client/entry.js b/MetaLamp/nft-marketplace/client/entry.js new file mode 100644 index 000000000..b5880a3fc --- /dev/null +++ b/MetaLamp/nft-marketplace/client/entry.js @@ -0,0 +1,9 @@ +import './src/Main.purs'; + +function importAll(resolve) { + resolve.keys().forEach(resolve); +} + +importAll( + require.context('./src', true, /\.scss$/) +); diff --git a/MetaLamp/nft-marketplace/client/package-lock.json b/MetaLamp/nft-marketplace/client/package-lock.json new file mode 100644 index 000000000..77b310829 --- /dev/null +++ b/MetaLamp/nft-marketplace/client/package-lock.json @@ -0,0 +1,7019 @@ +{ + "name": "plutus-pab-client", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@fortawesome/fontawesome-free": { + "version": "5.15.3", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.3.tgz", + "integrity": "sha512-rFnSUN/QOtnOAgqFRooTA3H57JLDm0QEG/jPdk+tLQNL/eWd+Aok8g3qCI+Q1xuDPWpGW/i9JySpJVsq8Q0s9w==" + }, + "@types/glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA==" + }, + "@types/node": { + "version": "15.12.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.1.tgz", + "integrity": "sha512-zyxJM8I1c9q5sRMtVF+zdd13Jt6RU4r4qfhTd7lQubyThvLfx6yYekWSQjGCGV2Tkecgxnlpl/DNlb6Hg+dmEw==" + }, + "@webassemblyjs/ast": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", + "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", + "requires": { + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz", + "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==" + }, + "@webassemblyjs/helper-api-error": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz", + "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==" + }, + "@webassemblyjs/helper-buffer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz", + "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==" + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz", + "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==", + "requires": { + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz", + "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==" + }, + "@webassemblyjs/helper-module-context": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz", + "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==", + "requires": { + "@webassemblyjs/ast": "1.9.0" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", + "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==" + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", + "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", + "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", + "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", + "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==" + }, + "@webassemblyjs/wasm-edit": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", + "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/helper-wasm-section": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-opt": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", + "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", + "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", + "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz", + "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/floating-point-hex-parser": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-code-frame": "1.9.0", + "@webassemblyjs/helper-fsm": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", + "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==" + }, + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==" + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" + }, + "ansi-colors": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", + "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==" + }, + "ansi-html": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", + "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=" + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "optional": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=" + }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=" + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" + }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "requires": { + "lodash": "^4.17.14" + } + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==" + }, + "async-foreach": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", + "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=" + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" + }, + "bignumber": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/bignumber/-/bignumber-1.1.0.tgz", + "integrity": "sha1-5qsKdD2l8+oBjlwXWX0SH3howVk=" + }, + "bignumber.js": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz", + "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==" + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "optional": true + }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "requires": { + "inherits": "~2.0.0" + } + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "dependencies": { + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + } + } + }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "requires": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" + }, + "bootstrap": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.0.tgz", + "integrity": "sha512-Io55IuQY3kydzHtbGvQya3H+KorS/M9rSNyfCGCg9WZ4pyT/lCxIlpJgG1GXW/PswzC84Tr2fBYi+7+jFVQQBw==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "requires": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "requires": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "requires": { + "pako": "~1.0.5" + } + }, + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==" + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "cacache": { + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", + "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", + "requires": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + } + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", + "requires": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "chokidar": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "optional": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.3.1", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + }, + "dependencies": { + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "optional": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "optional": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "optional": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "optional": true, + "requires": { + "is-number": "^7.0.0" + } + } + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==" + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "clean-css": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", + "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", + "requires": { + "source-map": "~0.6.0" + } + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==" + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==" + }, + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==" + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", + "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", + "requires": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "css-loader": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-1.0.1.tgz", + "integrity": "sha512-+ZHAZm/yqvJ2kDtPne3uX0C+Vr3Zn5jFn2N4HywtS5ujwvsVkyg0VArEXpl3BgczDA8anieki1FIzhchX4yrDw==", + "requires": { + "babel-code-frame": "^6.26.0", + "css-selector-tokenizer": "^0.7.0", + "icss-utils": "^2.1.0", + "loader-utils": "^1.0.2", + "lodash": "^4.17.11", + "postcss": "^6.0.23", + "postcss-modules-extract-imports": "^1.2.0", + "postcss-modules-local-by-default": "^1.2.0", + "postcss-modules-scope": "^1.1.0", + "postcss-modules-values": "^1.3.0", + "postcss-value-parser": "^3.3.0", + "source-list-map": "^2.0.0" + } + }, + "css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "requires": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "css-selector-tokenizer": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz", + "integrity": "sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==", + "requires": { + "cssesc": "^3.0.0", + "fastparse": "^1.1.2" + } + }, + "css-what": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", + "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==" + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "requires": { + "array-find-index": "^1.0.1" + } + }, + "cyclist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=" + }, + "dargs": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-5.1.0.tgz", + "integrity": "sha1-7H6lDHhWTNNsnV7Bj2Yyn63ieCk=" + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + }, + "default-gateway": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", + "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", + "requires": { + "execa": "^1.0.0", + "ip-regex": "^2.1.0" + } + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "del": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "requires": { + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + }, + "dependencies": { + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + } + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=" + }, + "detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=" + }, + "dns-packet": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", + "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", + "requires": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "requires": { + "buffer-indexof": "^1.0.0" + } + }, + "dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "requires": { + "utila": "~0.4" + } + }, + "dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "requires": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==" + } + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==" + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", + "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==", + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + }, + "dependencies": { + "memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" + }, + "err-code": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz", + "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=" + }, + "errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.3.tgz", + "integrity": "sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw==", + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "is-callable": "^1.2.3", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.3", + "is-string": "^1.0.6", + "object-inspect": "^1.10.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==" + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, + "eventsource": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz", + "integrity": "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==", + "requires": { + "original": "^1.0.0" + } + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "extract-text-webpack-plugin": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extract-text-webpack-plugin/-/extract-text-webpack-plugin-3.0.2.tgz", + "integrity": "sha512-bt/LZ4m5Rqt/Crl2HiKuAl/oqg0psx1tsTLkvWbJen1CtD+fftkZhMaQ9HOtY2gWsl2Wq+sABmMVi9z3DhKWQQ==", + "requires": { + "async": "^2.4.1", + "loader-utils": "^1.1.0", + "schema-utils": "^0.3.0", + "webpack-sources": "^1.0.1" + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==" + }, + "faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "figgy-pudding": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", + "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==" + }, + "file-loader": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-2.0.0.tgz", + "integrity": "sha512-YCsBfd1ZGCyonOKLxPiKPdu+8ld9HAaMEvJewzz+b2eTF7uL5Zm/HdBF6FjCrpCMRq25Mi0U1gl4pwn2TlH7hQ==", + "requires": { + "loader-utils": "^1.0.2", + "schema-utils": "^1.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "findup-sync": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", + "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + } + }, + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "follow-redirects": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", + "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==" + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "requires": { + "map-cache": "^0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "optional": true + }, + "fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "gaze": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", + "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", + "requires": { + "globule": "^1.0.0" + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=" + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "optional": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "requires": { + "global-prefix": "^3.0.0" + }, + "dependencies": { + "global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "requires": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + } + } + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "requires": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + } + }, + "globby": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-4.1.0.tgz", + "integrity": "sha1-CA9UVJ7BuCpsYOYx/ILhIR2+lfg=", + "requires": { + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "glob": "^6.0.1", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "globule": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.2.tgz", + "integrity": "sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA==", + "requires": { + "glob": "~7.1.1", + "lodash": "~4.17.10", + "minimatch": "~3.0.2" + } + }, + "graceful-fs": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" + }, + "handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + } + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "html-entities": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.4.0.tgz", + "integrity": "sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==" + }, + "html-minifier": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz", + "integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==", + "requires": { + "camel-case": "3.0.x", + "clean-css": "4.2.x", + "commander": "2.17.x", + "he": "1.2.x", + "param-case": "2.1.x", + "relateurl": "0.2.x", + "uglify-js": "3.4.x" + } + }, + "html-webpack-plugin": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", + "integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=", + "requires": { + "html-minifier": "^3.2.3", + "loader-utils": "^0.2.16", + "lodash": "^4.17.3", + "pretty-error": "^2.0.2", + "tapable": "^1.0.0", + "toposort": "^1.0.0", + "util.promisify": "1.0.0" + }, + "dependencies": { + "big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==" + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=" + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=" + }, + "loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "requires": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" + } + } + } + }, + "htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "requires": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + } + } + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=" + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } + } + }, + "http-parser-js": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", + "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==" + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-middleware": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", + "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", + "requires": { + "http-proxy": "^1.17.0", + "is-glob": "^4.0.0", + "lodash": "^4.17.11", + "micromatch": "^3.1.10" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-replace-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", + "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=" + }, + "icss-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-2.1.0.tgz", + "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=", + "requires": { + "postcss": "^6.0.1" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=" + }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" + }, + "in-publish": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.1.tgz", + "integrity": "sha512-oDM0kUSNFC31ShNxHKUyfZKy8ZeXZBWMjMdZHKLOk13uvT27VTL/QzRGfRUcevJhpkZAvlhPYuXkF7eNWrtyxQ==" + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "requires": { + "repeating": "^2.0.0" + } + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "internal-ip": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", + "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", + "requires": { + "default-gateway": "^4.2.0", + "ipaddr.js": "^1.9.0" + } + }, + "interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==" + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" + }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==" + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arguments": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz", + "integrity": "sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==", + "requires": { + "call-bind": "^1.0.0" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-bigint": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz", + "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==" + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "optional": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-boolean-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz", + "integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==", + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "is-callable": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", + "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==" + }, + "is-core-module": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", + "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", + "requires": { + "has": "^1.0.3" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-date-object": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.4.tgz", + "integrity": "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==" + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==" + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-number-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz", + "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==" + }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==" + }, + "is-path-in-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "requires": { + "is-path-inside": "^2.1.0" + } + }, + "is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "requires": { + "path-is-inside": "^1.0.2" + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "requires": { + "isobject": "^3.0.1" + } + }, + "is-regex": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz", + "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==", + "requires": { + "call-bind": "^1.0.2", + "has-symbols": "^1.0.2" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-string": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz", + "integrity": "sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==" + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "requires": { + "has-symbols": "^1.0.2" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jquery": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz", + "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==" + }, + "js-base64": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", + "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==" + }, + "js-string-escape": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz", + "integrity": "sha1-4mJbrbwNZ8dTPp7cEGjFh65BN+8=" + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "requires": { + "bignumber.js": "^9.0.0" + } + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "json3": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", + "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==" + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "requires": { + "minimist": "^1.2.0" + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "killable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==" + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==" + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "dependencies": { + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + } + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=" + }, + "loglevel": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.7.1.tgz", + "integrity": "sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw==" + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=" + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + } + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=" + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "requires": { + "object-visit": "^1.0.0" + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "mime": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", + "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==" + }, + "mime-db": { + "version": "1.48.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", + "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==" + }, + "mime-types": { + "version": "2.1.31", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", + "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", + "requires": { + "mime-db": "1.48.0" + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "requires": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + } + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=" + }, + "nan": { + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==" + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, + "no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "requires": { + "lower-case": "^1.1.1" + } + }, + "node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==" + }, + "node-gyp": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", + "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", + "requires": { + "fstream": "^1.0.0", + "glob": "^7.0.3", + "graceful-fs": "^4.1.2", + "mkdirp": "^0.5.0", + "nopt": "2 || 3", + "npmlog": "0 || 1 || 2 || 3 || 4", + "osenv": "0", + "request": "^2.87.0", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^2.0.0", + "which": "1" + }, + "dependencies": { + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" + } + } + }, + "node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "node-sass": { + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.14.1.tgz", + "integrity": "sha512-sjCuOlvGyCJS40R8BscF5vhVlQjNN069NtQ1gSxyK1u9iqvn6tf7O1R4GNowVZfiZUCRt5MmMs1xd+4V/7Yr0g==", + "requires": { + "async-foreach": "^0.1.3", + "chalk": "^1.1.1", + "cross-spawn": "^3.0.0", + "gaze": "^1.0.0", + "get-stdin": "^4.0.1", + "glob": "^7.0.3", + "in-publish": "^2.0.0", + "lodash": "^4.17.15", + "meow": "^3.7.0", + "mkdirp": "^0.5.1", + "nan": "^2.13.2", + "node-gyp": "^3.8.0", + "npmlog": "^4.0.0", + "request": "^2.88.0", + "sass-graph": "2.2.5", + "stdout-stream": "^1.4.0", + "true-case-path": "^1.0.2" + } + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "requires": { + "abbrev": "1" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "requires": { + "path-key": "^2.0.0" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "requires": { + "boolbase": "~1.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-inspect": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz", + "integrity": "sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==" + }, + "object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "object.getownpropertydescriptors": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz", + "integrity": "sha512-WtxeKSzfBjlzL+F9b7M7hewDzMwy+C8NRssHd1YrNlzHzIDrXcXiNOMrezdAEM4UXixgV+vvnyBeN7Rygl2ttQ==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "requires": { + "isobject": "^3.0.1" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "opn": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "requires": { + "is-wsl": "^1.1.0" + } + }, + "original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "requires": { + "url-parse": "^1.4.3" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==" + }, + "p-retry": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", + "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", + "requires": { + "retry": "^0.12.0" + }, + "dependencies": { + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" + } + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, + "parallel-transform": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", + "requires": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", + "requires": { + "no-case": "^2.2.0" + } + }, + "parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "requires": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "^1.2.0" + } + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=" + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" + }, + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==" + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "optional": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "^2.0.0" + } + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "requires": { + "find-up": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + } + } + }, + "portfinder": { + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", + "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "requires": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" + }, + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-modules-extract-imports": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.1.tgz", + "integrity": "sha512-6jt9XZwUhwmRUhb/CkyJY020PYaPJsCyt3UjbaWo6XEbH/94Hmv6MP7fG2C5NDU/BcHzyGYxNtHvM+LTf9HrYw==", + "requires": { + "postcss": "^6.0.1" + } + }, + "postcss-modules-local-by-default": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", + "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", + "requires": { + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" + } + }, + "postcss-modules-scope": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", + "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", + "requires": { + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" + } + }, + "postcss-modules-values": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", + "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", + "requires": { + "icss-replace-symbols": "^1.1.0", + "postcss": "^6.0.1" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "pretty-error": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.2.tgz", + "integrity": "sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw==", + "requires": { + "lodash": "^4.17.20", + "renderkid": "^2.0.4" + } + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=" + }, + "promise-retry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-1.1.1.tgz", + "integrity": "sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=", + "requires": { + "err-code": "^1.0.0", + "retry": "^0.10.0" + } + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "purs-loader": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/purs-loader/-/purs-loader-3.7.2.tgz", + "integrity": "sha512-Sidqk2RE1R2DTPt30I6G3p//c9pMaV9jd36NI3HXXSyf4Kf5X01FiP/2wMTJ8a5XKAXKdKCJ3WPqA8Whlxi0tg==", + "requires": { + "bluebird": "^3.3.5", + "chalk": "^1.1.3", + "cross-spawn": "^3.0.1", + "dargs": "^5.1.0", + "debug": "^2.6.0", + "globby": "^4.0.0", + "js-string-escape": "^1.0.1", + "loader-utils": "^1.0.2", + "lodash.difference": "^4.5.0", + "promise-retry": "^1.1.0" + } + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" + }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + } + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "optional": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regexp.prototype.flags": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", + "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=" + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" + }, + "renderkid": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.5.tgz", + "integrity": "sha512-ccqoLg+HLOHq1vdfYNm4TBeaCDIi1FLt3wGojTDSvdewUv65oTmI3cnT2E4hRjl1gzKZIPK+KZrXzlUYKnR+vQ==", + "requires": { + "css-select": "^2.0.2", + "dom-converter": "^0.2", + "htmlparser2": "^3.10.1", + "lodash": "^4.17.20", + "strip-ansi": "^3.0.0" + } + }, + "repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==" + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "requires": { + "is-finite": "^1.0.0" + } + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "requires": { + "resolve-from": "^3.0.0" + } + }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, + "dependencies": { + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + } + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=" + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" + }, + "retry": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", + "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=" + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "requires": { + "aproba": "^1.1.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sass-graph": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.5.tgz", + "integrity": "sha512-VFWDAHOe6mRuT4mZRd4eKE+d8Uedrk6Xnh7Sh9b4NGufQLQjOrvf/MQoOdx+0s92L89FeyUUNfU597j/3uNpag==", + "requires": { + "glob": "^7.0.0", + "lodash": "^4.0.0", + "scss-tokenizer": "^0.2.3", + "yargs": "^13.3.2" + } + }, + "sass-loader": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.3.1.tgz", + "integrity": "sha512-tuU7+zm0pTCynKYHpdqaPpe+MMTQ76I9TPZ7i4/5dZsigE350shQWe5EZNl5dBidM49TPET75tNqRbcsUZWeNA==", + "requires": { + "clone-deep": "^4.0.1", + "loader-utils": "^1.0.1", + "neo-async": "^2.5.0", + "pify": "^4.0.1", + "semver": "^6.3.0" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "schema-utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.3.0.tgz", + "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=", + "requires": { + "ajv": "^5.0.0" + } + }, + "scss-tokenizer": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", + "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", + "requires": { + "js-base64": "^2.1.8", + "source-map": "^0.4.2" + }, + "dependencies": { + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=" + }, + "selfsigned": { + "version": "1.10.11", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.11.tgz", + "integrity": "sha512-aVmbPOfViZqOZPgRBT0+3u4yZFHpmnIghLMlAcb5/xhp5ZtB/RVnKhz5vl2M32CLXAqR4kha9zfhNg0Lf/sxKA==", + "requires": { + "node-forge": "^0.10.0" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "requires": { + "kind-of": "^6.0.2" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "sockjs": { + "version": "0.3.21", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.21.tgz", + "integrity": "sha512-DhbPFGpxjc6Z3I+uX07Id5ZO2XwYsWOrYjaSeieES78cq+JaJvVe5q/m1uvjIQhXinhIeCFRH6JgXe+mvVMyXw==", + "requires": { + "faye-websocket": "^0.11.3", + "uuid": "^3.4.0", + "websocket-driver": "^0.7.4" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } + } + }, + "sockjs-client": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.5.1.tgz", + "integrity": "sha512-VnVAb663fosipI/m6pqRXakEOw7nvd7TUgdr3PlR/8V2I95QIdwT8L4nMxhyU8SmDBHYXU1TOElaKOmKLfYzeQ==", + "requires": { + "debug": "^3.2.6", + "eventsource": "^1.0.7", + "faye-websocket": "^0.11.3", + "inherits": "^2.0.4", + "json3": "^3.3.3", + "url-parse": "^1.5.1" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==" + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz", + "integrity": "sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ==" + }, + "spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz", + "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "stdout-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", + "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", + "requires": { + "readable-stream": "^2.0.1" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "requires": { + "get-stdin": "^4.0.1" + } + }, + "style-loader": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.23.1.tgz", + "integrity": "sha512-XK+uv9kWwhZMZ1y7mysB+zoihsEj4wneFWAS5qoiLwzW0WzSqMrrsIy+a3zkQJq0ipFtBpX5W3MqyRIBF/WFGg==", + "requires": { + "loader-utils": "^1.1.0", + "schema-utils": "^1.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" + }, + "tar": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", + "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", + "requires": { + "block-stream": "*", + "fstream": "^1.0.12", + "inherits": "2" + } + }, + "terser": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", + "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + } + } + }, + "terser-webpack-plugin": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz", + "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==", + "requires": { + "cacache": "^12.0.2", + "find-cache-dir": "^2.1.0", + "is-wsl": "^1.1.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^4.0.0", + "source-map": "^0.6.1", + "terser": "^4.1.2", + "webpack-sources": "^1.4.0", + "worker-farm": "^1.7.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" + }, + "timers-browserify": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "requires": { + "setimmediate": "^1.0.4" + } + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=" + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "toposort": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz", + "integrity": "sha1-LmhELZ9k7HILjMieZEOsbKqVACk=" + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=" + }, + "true-case-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", + "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", + "requires": { + "glob": "^7.1.2" + } + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=" + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "uglify-js": { + "version": "3.4.10", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz", + "integrity": "sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==", + "requires": { + "commander": "~2.19.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==" + } + } + }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + } + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" + } + } + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==" + }, + "upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=" + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + } + } + }, + "url-loader": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-1.1.2.tgz", + "integrity": "sha512-dXHkKmw8FhPqu8asTc1puBfe3TehOCo2+RmOOev5suNCIYBcT626kxiWg1NBVkwc4rO8BGa7gP70W7VXuqHrjg==", + "requires": { + "loader-utils": "^1.1.0", + "mime": "^2.0.3", + "schema-utils": "^1.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "url-parse": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz", + "integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "requires": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", + "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==" + }, + "uuid-validate": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/uuid-validate/-/uuid-validate-0.0.3.tgz", + "integrity": "sha512-Fykw5U4eZESbq739BeLvEBFRuJODfrlmjx5eJux7W817LjRaq4b7/i4t2zxQmhcX+fAj4nMfRdTzO4tmwLKn0w==" + }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==" + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==" + }, + "watchpack": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", + "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", + "requires": { + "chokidar": "^3.4.1", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0", + "watchpack-chokidar2": "^2.0.1" + } + }, + "watchpack-chokidar2": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", + "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==", + "optional": true, + "requires": { + "chokidar": "^2.1.8" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "optional": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "optional": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "optional": true + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "optional": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "optional": true, + "requires": { + "nan": "^2.12.1" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "optional": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "optional": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "optional": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "optional": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "optional": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "webpack": { + "version": "4.46.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.46.0.tgz", + "integrity": "sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/wasm-edit": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "acorn": "^6.4.1", + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^4.5.0", + "eslint-scope": "^4.0.3", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.4.0", + "loader-utils": "^1.2.3", + "memory-fs": "^0.4.1", + "micromatch": "^3.1.10", + "mkdirp": "^0.5.3", + "neo-async": "^2.6.1", + "node-libs-browser": "^2.2.1", + "schema-utils": "^1.0.0", + "tapable": "^1.1.3", + "terser-webpack-plugin": "^1.4.3", + "watchpack": "^1.7.4", + "webpack-sources": "^1.4.1" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "webpack-cli": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.12.tgz", + "integrity": "sha512-NVWBaz9k839ZH/sinurM+HcDvJOTXwSjYp1ku+5XKeOC03z8v5QitnK/x+lAxGXFyhdayoIf/GOpv85z3/xPag==", + "requires": { + "chalk": "^2.4.2", + "cross-spawn": "^6.0.5", + "enhanced-resolve": "^4.1.1", + "findup-sync": "^3.0.0", + "global-modules": "^2.0.0", + "import-local": "^2.0.0", + "interpret": "^1.4.0", + "loader-utils": "^1.4.0", + "supports-color": "^6.1.0", + "v8-compile-cache": "^2.1.1", + "yargs": "^13.3.2" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "webpack-dev-middleware": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz", + "integrity": "sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==", + "requires": { + "memory-fs": "^0.4.1", + "mime": "^2.4.4", + "mkdirp": "^0.5.1", + "range-parser": "^1.2.1", + "webpack-log": "^2.0.0" + } + }, + "webpack-dev-server": { + "version": "3.11.2", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.2.tgz", + "integrity": "sha512-A80BkuHRQfCiNtGBS1EMf2ChTUs0x+B3wGDFmOeT4rmJOHhHTCH2naNxIHhmkr0/UillP4U3yeIyv1pNp+QDLQ==", + "requires": { + "ansi-html": "0.0.7", + "bonjour": "^3.5.0", + "chokidar": "^2.1.8", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "debug": "^4.1.1", + "del": "^4.1.1", + "express": "^4.17.1", + "html-entities": "^1.3.1", + "http-proxy-middleware": "0.19.1", + "import-local": "^2.0.0", + "internal-ip": "^4.3.0", + "ip": "^1.1.5", + "is-absolute-url": "^3.0.3", + "killable": "^1.0.1", + "loglevel": "^1.6.8", + "opn": "^5.5.0", + "p-retry": "^3.0.1", + "portfinder": "^1.0.26", + "schema-utils": "^1.0.0", + "selfsigned": "^1.10.8", + "semver": "^6.3.0", + "serve-index": "^1.9.1", + "sockjs": "^0.3.21", + "sockjs-client": "^1.5.0", + "spdy": "^4.0.2", + "strip-ansi": "^3.0.1", + "supports-color": "^6.1.0", + "url": "^0.11.0", + "webpack-dev-middleware": "^3.7.2", + "webpack-log": "^2.0.0", + "ws": "^6.2.1", + "yargs": "^13.3.2" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==" + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "optional": true, + "requires": { + "nan": "^2.12.1" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "webpack-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", + "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "requires": { + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } + } + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, + "websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "requires": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==" + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "requires": { + "errno": "~0.1.7" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "ws": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", + "requires": { + "async-limiter": "~1.0.0" + } + }, + "xhr2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/xhr2/-/xhr2-0.1.4.tgz", + "integrity": "sha1-f4dliEdxbbUCYyOBL4GMras4el8=" + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + } + } + } + } +} diff --git a/MetaLamp/nft-marketplace/client/package.json b/MetaLamp/nft-marketplace/client/package.json new file mode 100644 index 000000000..8e1155d8e --- /dev/null +++ b/MetaLamp/nft-marketplace/client/package.json @@ -0,0 +1,42 @@ +{ + "name": "plutus-pab-client", + "version": "1.0.0", + "scripts": { + "webpack": "DEBUG=purs-loader* DEBUG_DEPTH=100 webpack --progress --bail --mode=production -p", + "webpack:watch": "PATH=$PATH:../releases/psc-package DEBUG=purs-loader* DEBUG_DEPTH=100 webpack --progress --display-error-details --display verbose --watch", + "webpack:server": "webpack-dev-server --progress --inline --hot --mode=development", + "webpack:server:debug": "DEBUG=purs-loader* DEBUG_DEPTH=100 webpack-dev-server --progress --inline --hot", + "purs:compile": "spago build", + "docs": "spago docs", + "repl": "spago repl", + "fetch-plutus-purs": "sh ./scripts/fetch-plutus-purs.sh", + "generate-purs-only": "sh ./scripts/generate-purs.sh", + "generate-purs": "npm run fetch-plutus-purs && npm run generate-purs-only", + "start-chrome": "sh ./scripts/start-chrome.sh", + "start": "npm run purs:compile && npm run webpack:server" + }, + "dependencies": { + "@fortawesome/fontawesome-free": "^5.10.2", + "bignumber": "^1.1.0", + "bootstrap": "^4.3.1", + "css-loader": "^1.0.0", + "extract-text-webpack-plugin": "^3.0.2", + "file-loader": "^2.0.0", + "html-webpack-plugin": "^3.2.0", + "jquery": "^3.3.1", + "json-bigint": "^1.0.0", + "node-sass": "^4.12.0", + "purs-loader": "^3.6.0", + "sass-loader": "^7.1.0", + "style-loader": "^0.23.1", + "url-loader": "^1.1.2", + "uuid": "^7.0.2", + "uuid-validate": "^0.0.3", + "webpack": "^4.41.0", + "webpack-cli": "^3.1.2", + "webpack-dev-server": "^3.1.10", + "xhr2": "^0.1.4" + }, + "resolutions": {}, + "license": "Apache-2.0" +} diff --git a/MetaLamp/nft-marketplace/client/packages.dhall b/MetaLamp/nft-marketplace/client/packages.dhall new file mode 100644 index 000000000..6a35493a5 --- /dev/null +++ b/MetaLamp/nft-marketplace/client/packages.dhall @@ -0,0 +1,178 @@ +{- +Welcome to your new Dhall package-set! + +Below are instructions for how to edit this file for most use +cases, so that you don't need to know Dhall to use it. + +## Warning: Don't Move This Top-Level Comment! + +Due to how `dhall format` currently works, this comment's +instructions cannot appear near corresponding sections below +because `dhall format` will delete the comment. However, +it will not delete a top-level comment like this one. + +## Use Cases + +Most will want to do one or both of these options: +1. Override/Patch a package's dependency +2. Add a package not already in the default package set + +This file will continue to work whether you use one or both options. +Instructions for each option are explained below. + +### Overriding/Patching a package + +Purpose: +- Change a package's dependency to a newer/older release than the + default package set's release +- Use your own modified version of some dependency that may + include new API, changed API, removed API by + using your custom git repo of the library rather than + the package set's repo + +Syntax: +Replace the overrides' "{=}" (an empty record) with the following idea +The "//" or "⫽" means "merge these two records and + when they have the same value, use the one on the right:" +------------------------------- +let override = + { packageName = + upstream.packageName // { updateEntity1 = "new value", updateEntity2 = "new value" } + , packageName = + upstream.packageName // { version = "v4.0.0" } + , packageName = + upstream.packageName // { repo = "https://www.example.com/path/to/new/repo.git" } + } +------------------------------- + +Example: +------------------------------- +let overrides = + { halogen = + upstream.halogen // { version = "master" } + , halogen-vdom = + upstream.halogen-vdom // { version = "v4.0.0" } + } +------------------------------- + +### Additions + +Purpose: +- Add packages that aren't already included in the default package set + +Syntax: +Replace the additions' "{=}" (an empty record) with the following idea: +------------------------------- +let additions = + { package-name = + { dependencies = + [ "dependency1" + , "dependency2" + ] + , repo = + "https://example.com/path/to/git/repo.git" + , version = + "tag ('v4.0.0') or branch ('master')" + } + , package-name = + { dependencies = + [ "dependency1" + , "dependency2" + ] + , repo = + "https://example.com/path/to/git/repo.git" + , version = + "tag ('v4.0.0') or branch ('master')" + } + , etc. + } +------------------------------- + +Example: +------------------------------- +let additions = + { benchotron = + { dependencies = + [ "arrays" + , "exists" + , "profunctor" + , "strings" + , "quickcheck" + , "lcg" + , "transformers" + , "foldable-traversable" + , "exceptions" + , "node-fs" + , "node-buffer" + , "node-readline" + , "datetime" + , "now" + ] + , repo = + "https://github.com/hdgarrood/purescript-benchotron.git" + , version = + "v7.0.0" + } + } +------------------------------- +-} + + +let upstream = + https://github.com/purescript/package-sets/releases/download/psc-0.13.6-20200502/packages.dhall sha256:1e1ecbf222c709b76cc7e24cf63af3c2089ffd22bbb1e3379dfd3c07a1787694 + +let overrides = {=} + +let additions = + { servant-support = + { dependencies = + [ "console" + , "prelude" + , "either" + , "foldable-traversable" + , "generics-rep" + , "effect" + , "aff" + , "affjax" + , "exceptions" + , "web-xhr" + , "foreign-generic" + ] + , repo = + "https://github.com/shmish111/purescript-servant-support" + , version = + "1805f896560751c48a04d3e29f9c109df850d8d3" + } + , concurrent-queues = + { dependencies = + [ "aff" + , "avar" + ] + , repo = + "https://github.com/purescript-contrib/purescript-concurrent-queues.git" + , version = + "v1.1.0" + } + , foreign-generic = + upstream.foreign-generic + ⫽ { repo = + "https://github.com/shmish111/purescript-foreign-generic" + , version = + "57692ed7b1bc512bcfddd2c00c27e865e9c21b84" + } + , matryoshka = + { dependencies = + [ "prelude" + , "fixed-points" + , "free" + , "transformers" + , "profunctor" + ] + , repo = + "https://github.com/slamdata/purescript-matryoshka.git" + , version = + "v0.4.0" + } + } + +in upstream ⫽ overrides ⫽ additions diff --git a/MetaLamp/nft-marketplace/client/scripts/fetch-plutus-purs.sh b/MetaLamp/nft-marketplace/client/scripts/fetch-plutus-purs.sh new file mode 100755 index 000000000..4b185c343 --- /dev/null +++ b/MetaLamp/nft-marketplace/client/scripts/fetch-plutus-purs.sh @@ -0,0 +1,9 @@ +rm -rf plutus-purs +mkdir plutus-purs +cd plutus-purs +git init +git remote add origin -f https://github.com/input-output-hk/plutus +git config core.sparseCheckout true +echo 'web-common-plutus/*' >> .git/info/sparse-checkout +echo 'web-common/*' >> .git/info/sparse-checkout +git pull origin bd16cc29045ffc7eaa6beaabe3b985a56cb9292a # plutus-starter-devcontainer/v1.0.6 diff --git a/MetaLamp/nft-marketplace/client/scripts/generate-purs.sh b/MetaLamp/nft-marketplace/client/scripts/generate-purs.sh new file mode 100755 index 000000000..0947c5a43 --- /dev/null +++ b/MetaLamp/nft-marketplace/client/scripts/generate-purs.sh @@ -0,0 +1,2 @@ +cd .. +cabal run generate-purs \ No newline at end of file diff --git a/MetaLamp/nft-marketplace/client/scripts/start-chrome.sh b/MetaLamp/nft-marketplace/client/scripts/start-chrome.sh new file mode 100755 index 000000000..776df5378 --- /dev/null +++ b/MetaLamp/nft-marketplace/client/scripts/start-chrome.sh @@ -0,0 +1 @@ +chromium --disable-web-security --user-data-dir=/chrome-temp \ No newline at end of file diff --git a/MetaLamp/nft-marketplace/client/spago.dhall b/MetaLamp/nft-marketplace/client/spago.dhall new file mode 100644 index 000000000..e7b989c66 --- /dev/null +++ b/MetaLamp/nft-marketplace/client/spago.dhall @@ -0,0 +1,38 @@ +{- +Welcome to a Spago project! +You can edit this file as you like. +-} +{ name = "plutus-pab-client" +, dependencies = + [ "aff" + , "affjax" + , "argonaut-codecs" + , "avar" + , "bigints" + , "concurrent-queues" + , "console" + , "debug" + , "effect" + , "foreign-generic" + , "halogen" + , "matryoshka" + , "newtype" + , "node-fs" + , "prelude" + , "psci-support" + , "remotedata" + , "servant-support" + , "test-unit" + , "transformers" + , "undefinable" + , "uuid" + , "web-socket" + ] +, packages = ./packages.dhall +, sources = + [ "src/**/*.purs" + , "generated/**/*.purs" + , "plutus-purs/web-common/**/*.purs" + , "plutus-purs/web-common-plutus/**/*.purs" + ] +} diff --git a/MetaLamp/nft-marketplace/client/src/AppAff.purs b/MetaLamp/nft-marketplace/client/src/AppAff.purs new file mode 100644 index 000000000..ed0fba455 --- /dev/null +++ b/MetaLamp/nft-marketplace/client/src/AppAff.purs @@ -0,0 +1,90 @@ +module AppAff where + +import Prelude +import Affjax (Response, defaultRequest) +import Affjax.RequestBody (RequestBody, string) +import Capability.Contract (class Contract, ContractId(..), Endpoint(..), APIError(..)) +import Capability.LogMessages (class LogMessages) +import Capability.PollContract (class PollContract) +import Control.Monad.Except (ExceptT, runExceptT) +import Control.Monad.Reader.Trans (class MonadAsk, ReaderT, asks, runReaderT) +import Data.Bifunctor (bimap) +import Data.Either (Either) +import Data.HTTP.Method (fromString) +import Data.Maybe (Maybe(..)) +import Effect.Aff (Aff, Milliseconds(..), delay) +import Effect.Aff.Class (class MonadAff, liftAff) +import Effect.Class (class MonadEffect, liftEffect) +import Effect.Console as Console +import Foreign.Generic (class Decode, decode, encodeJSON) +import Servant.PureScript.Ajax (AjaxError(..), ErrorDescription(..), ajax) +import Type.Equality (class TypeEquals, from) + +type Env + = { host :: String, port :: Int } + +newtype AppM a + = AppM (ReaderT Env Aff a) + +derive newtype instance functorAppM :: Functor AppM + +derive newtype instance applyAppM :: Apply AppM + +derive newtype instance applicativeAppM :: Applicative AppM + +derive newtype instance bindAppM :: Bind AppM + +derive newtype instance monadAppM :: Monad AppM + +derive newtype instance monadEffectAppM :: MonadEffect AppM + +derive newtype instance monadAffAppM :: MonadAff AppM + +instance monadAskAppM :: TypeEquals e Env => MonadAsk e AppM where + ask = AppM $ asks from + +runAppM :: Env -> AppM ~> Aff +runAppM env (AppM m) = runReaderT m env + +instance logMessagesAppM :: LogMessages AppM where + logInfo = Console.log >>> liftEffect + logError = Console.error >>> liftEffect + +errorToString :: AjaxError -> String +errorToString (AjaxError e) = case e.description of + ResponseError _ r -> "Response error: " <> r + ResponseFormatError s -> "Parsing error: " <> s + DecodingError s -> "Decoding error: " <> s + ConnectionError s -> "Connection error: " <> s + NotFound -> "Not found" + +getBaseURL :: Env -> String +getBaseURL { host, port } = "http://" <> host <> ":" <> (show port) + +runAjax :: forall m a. Monad m => ExceptT AjaxError m (Response a) -> m (Either APIError a) +runAjax = (map toCustom) <<< runExceptT + where + toCustom = bimap (AjaxCallError <<< errorToString) (\r -> r.body) + +get :: forall m a. MonadAsk Env m => MonadAff m => Decode a => String -> m (Either APIError a) +get path = do + url <- asks ((_ <> path) <<< getBaseURL) + let + affReq = defaultRequest { method = fromString "GET", url = url } + runAjax $ ajax decode affReq + +post :: forall m a. MonadAsk Env m => MonadAff m => Decode a => String -> RequestBody -> m (Either APIError a) +post path body = do + url <- asks ((_ <> path) <<< getBaseURL) + let + affReq = defaultRequest { method = fromString "POST", url = url, content = Just body } + runAjax $ ajax decode affReq + +instance contractAppM :: Contract AppM where + getContracts = get "/api/new/contract/instances" + getContractStatus (ContractId cid) = get $ "/api/new/contract/instance/" <> cid <> "/status" + callEndpoint (Endpoint endpoint) (ContractId cid) params = post ("/api/new/contract/instance/" <> cid <> "/endpoint/" <> endpoint) (string <<< encodeJSON $ params) + +instance pollContractAppM :: PollContract AppM where + pollDelay = liftAff <<< delay <<< Milliseconds $ 1000.0 + tooManyRetries retryCount = pure $ retryCount > 20 diff --git a/MetaLamp/nft-marketplace/client/src/Business/Marketplace.purs b/MetaLamp/nft-marketplace/client/src/Business/Marketplace.purs new file mode 100644 index 000000000..4737f4e6c --- /dev/null +++ b/MetaLamp/nft-marketplace/client/src/Business/Marketplace.purs @@ -0,0 +1,54 @@ +module Business.Marketplace where + +import Prelude +import Capability.Contract (class Contract, APIError, ContractId(..), Endpoint, getContracts) +import Capability.PollContract (class PollContract, LeftPoll(..), PollError, PollResponse, pollEndpoint) +import Control.Monad.Except (runExcept, throwError, withExcept) +import Data.Either (Either) +import Data.Json.JsonUUID (JsonUUID(..)) +import Data.Lens (Prism', preview) +import Data.Maybe (Maybe, maybe) +import Data.RawJson (RawJson(..)) +import Foreign.Generic (class Decode, class Encode, decodeJSON) +import Plutus.Abstract.ContractResponse (ContractResponse(..)) +import Plutus.PAB.Events.ContractInstanceState (PartiallyDecodedResponse(..)) +import Plutus.PAB.Simulation (MarketplaceContracts) +import Plutus.PAB.Webserver.Types (ContractInstanceClientState(..)) +import Wallet.Types (ContractInstanceId(..)) +import Data.UUID (toString) as UUID + +getMarketplaceContracts :: forall m. Contract m => m (Either APIError (Array (ContractInstanceClientState MarketplaceContracts))) +getMarketplaceContracts = getContracts + +getMarketplaceResponseWith :: + forall m a p s. + PollContract m => + Encode p => + Decode s => + Show s => + Endpoint -> + Prism' s a -> + ContractId -> + p -> + m (Either PollError a) +getMarketplaceResponseWith endpoint pick cid param = pollEndpoint getNext endpoint param cid + where + getNext :: ContractInstanceClientState MarketplaceContracts -> PollResponse a + getNext (ContractInstanceClientState { cicCurrentState: PartiallyDecodedResponse { observableState: RawJson s } }) = + runExcept + $ do + (contractResponse :: ContractResponse String s) <- withExcept (ResponseError <<< show) (decodeJSON s) + case contractResponse of + CrPending -> throwError Continue + CrError e -> throwError <<< ResponseError $ e + CrSuccess state -> + maybe + (throwError <<< ResponseError $ "Invalid state: " <> (show state)) + pure + (preview pick state) + +getMarketplaceContractId :: forall a. Prism' MarketplaceContracts a -> ContractInstanceClientState MarketplaceContracts -> Maybe ContractId +getMarketplaceContractId pick (ContractInstanceClientState { cicContract, cicDefintion }) = (const $ toContractIdParam cicContract) <$> (preview pick cicDefintion) + +toContractIdParam :: ContractInstanceId -> ContractId +toContractIdParam (ContractInstanceId { unContractInstanceId: JsonUUID uuid }) = ContractId <<< UUID.toString $ uuid diff --git a/MetaLamp/nft-marketplace/client/src/Business/MarketplaceInfo.purs b/MetaLamp/nft-marketplace/client/src/Business/MarketplaceInfo.purs new file mode 100644 index 000000000..108c7b918 --- /dev/null +++ b/MetaLamp/nft-marketplace/client/src/Business/MarketplaceInfo.purs @@ -0,0 +1,39 @@ +module Business.MarketplaceInfo where + +import Prelude +import Business.Marketplace (getMarketplaceContractId, getMarketplaceResponseWith) +import Capability.Contract (ContractId, ContractUnit(..), Endpoint(..)) +import Capability.PollContract (class PollContract, PollError) +import Data.Either (Either) +import Data.Json.JsonTuple (JsonTuple) +import Data.Maybe (Maybe) +import Data.Newtype (class Newtype, unwrap) +import Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine +import Plutus.Contracts.NftMarketplace.OffChain.ID +import Plutus.Contracts.NftMarketplace.OffChain.Info +import Ext.Plutus.Contracts.Auction +import Plutus.PAB.Simulation (MarketplaceContracts, _MarketplaceInfo) +import Plutus.PAB.Webserver.Types (ContractInstanceClientState) +import Plutus.V1.Ledger.Crypto (PubKeyHash) +import Plutus.V1.Ledger.Value (AssetClass, Value) +import PlutusTx.AssocMap (Map) + +newtype InfoContractId + = InfoContractId ContractId + +derive instance newtypeInfoContractId :: Newtype InfoContractId _ + +getInfoContractId :: ContractInstanceClientState MarketplaceContracts -> Maybe InfoContractId +getInfoContractId = map InfoContractId <<< getMarketplaceContractId _MarketplaceInfo + +fundsAt :: forall m. PollContract m => InfoContractId -> PubKeyHash -> m (Either PollError Value) +fundsAt = getMarketplaceResponseWith (Endpoint "fundsAt") _FundsAt <<< unwrap + +marketplaceFunds :: forall m. PollContract m => InfoContractId -> m (Either PollError Value) +marketplaceFunds cid = getMarketplaceResponseWith (Endpoint "marketplaceFunds") _MarketplaceFunds (unwrap cid) ContractUnit + +marketplaceStore :: forall m. PollContract m => InfoContractId -> m (Either PollError MarketplaceDatum) +marketplaceStore cid = getMarketplaceResponseWith (Endpoint "marketplaceStore") _MarketplaceStore (unwrap cid) ContractUnit + +auctionState :: forall m. PollContract m => InfoContractId -> UserItemId -> m (Either PollError AuctionState) +auctionState = getMarketplaceResponseWith (Endpoint "getAuctionState") _AuctionState <<< unwrap diff --git a/MetaLamp/nft-marketplace/client/src/Capability/Contract.purs b/MetaLamp/nft-marketplace/client/src/Capability/Contract.purs new file mode 100644 index 000000000..2c35e2310 --- /dev/null +++ b/MetaLamp/nft-marketplace/client/src/Capability/Contract.purs @@ -0,0 +1,45 @@ +module Capability.Contract where + +import Prelude +import Data.Either (Either) +import Data.Generic.Rep (class Generic) +import Data.Generic.Rep.Show (genericShow) +import Foreign (unsafeToForeign) +import Foreign.Generic (class Decode, class Encode) +import Halogen (HalogenM, lift) +import Plutus.PAB.Webserver.Types (ContractInstanceClientState) + +data APIError + = AjaxCallError String + +derive instance genericAPIError :: Generic APIError _ + +instance showAPIError :: Show APIError where + show = genericShow + +newtype ContractId + = ContractId String + +derive newtype instance showContractId :: Show ContractId + +newtype Endpoint + = Endpoint String + +derive newtype instance showEndpoint :: Show Endpoint + +data ContractUnit + = ContractUnit + +instance encodeContractUnit :: Encode ContractUnit where + encode value = unsafeToForeign [] + +class + Monad m <= Contract m where + getContracts :: forall a. Decode a => m (Either APIError (Array (ContractInstanceClientState a))) + callEndpoint :: forall a. Encode a => Endpoint -> ContractId -> a -> m (Either APIError Unit) + getContractStatus :: forall a. Decode a => ContractId -> m (Either APIError (ContractInstanceClientState a)) + +instance contractHalogenM :: Contract m => Contract (HalogenM st act slots msg m) where + getContracts = lift getContracts + callEndpoint endpoint cid params = lift $ callEndpoint endpoint cid params + getContractStatus = getContractStatus >>> lift diff --git a/MetaLamp/nft-marketplace/client/src/Capability/LogMessages.purs b/MetaLamp/nft-marketplace/client/src/Capability/LogMessages.purs new file mode 100644 index 000000000..dd4495b71 --- /dev/null +++ b/MetaLamp/nft-marketplace/client/src/Capability/LogMessages.purs @@ -0,0 +1,13 @@ +module Capability.LogMessages where + +import Prelude +import Halogen (HalogenM, lift) + +class + Monad m <= LogMessages m where + logInfo :: String -> m Unit + logError :: String -> m Unit + +instance logMessagesHalogenM :: LogMessages m => LogMessages (HalogenM st act slots msg m) where + logInfo = logInfo >>> lift + logError = logError >>> lift diff --git a/MetaLamp/nft-marketplace/client/src/Capability/PollContract.purs b/MetaLamp/nft-marketplace/client/src/Capability/PollContract.purs new file mode 100644 index 000000000..00e5aad0b --- /dev/null +++ b/MetaLamp/nft-marketplace/client/src/Capability/PollContract.purs @@ -0,0 +1,76 @@ +module Capability.PollContract where + +import Prelude +import Capability.Contract (class Contract, APIError(..), ContractId, Endpoint, callEndpoint, getContractStatus) +import Control.Monad.Except (runExceptT, throwError) +import Data.Either (Either(..), either) +import Data.Generic.Rep (class Generic) +import Data.Generic.Rep.Show (genericShow) +import Foreign.Generic (class Decode, class Encode) +import Halogen (HalogenM, lift) +import Plutus.PAB.Webserver.Types (ContractInstanceClientState) + +class + Contract m <= PollContract m where + pollDelay :: m Unit + tooManyRetries :: Int -> m Boolean + +instance pollContractHalogenM :: PollContract m => PollContract (HalogenM st act slots msg m) where + pollDelay = lift pollDelay + tooManyRetries = lift <<< tooManyRetries + +data PollError + = TooManyRetries + | PollAPIError String + | PollResponseError String + +derive instance genericPollError :: Generic PollError _ + +instance showPollError :: Show PollError where + show = genericShow + +data LeftPoll + = Continue + | ResponseError String + +type PollResponse a + = Either LeftPoll a + +pollStatus :: + forall m a c. + PollContract m => + Decode c => + (ContractInstanceClientState c -> PollResponse a) -> + Endpoint -> + ContractId -> + m (Either PollError a) +pollStatus = worker 0 + where + worker retryCount getNext endpoint cid = + runExceptT + $ do + limitExceeded <- lift $ tooManyRetries retryCount + when limitExceeded $ throwError TooManyRetries + _ <- lift pollDelay + status <- lift (getContractStatus cid) >>= either (throwError <<< toPollError) pure + case getNext status of + Left Continue -> lift (worker (retryCount + 1) getNext endpoint cid) >>= either throwError pure + Left (ResponseError e) -> throwError <<< PollResponseError $ e + Right s -> pure s + +pollEndpoint :: + forall m a c p. + PollContract m => + Encode p => + Decode c => + (ContractInstanceClientState c -> PollResponse a) -> + Endpoint -> + p -> + ContractId -> + m (Either PollError a) +pollEndpoint getNext endpoint param cid = + callEndpoint endpoint cid param + >>= either (pure <<< Left <<< toPollError) (const $ pollStatus getNext endpoint cid) + +toPollError :: APIError -> PollError +toPollError (AjaxCallError e) = PollAPIError e diff --git a/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs b/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs new file mode 100644 index 000000000..ec8f3be52 --- /dev/null +++ b/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs @@ -0,0 +1,53 @@ +module Component.MainPage where + +import Prelude +import Business.Marketplace as Marketplace +import Business.MarketplaceInfo as MarketplaceInfo +import Capability.LogMessages (class LogMessages) +import Capability.PollContract (class PollContract) +import Component.Utils (runRD) +import Control.Monad.Except (lift, runExceptT, throwError) +import Control.Parallel (parTraverse) +import Data.Array (catMaybes, findMap, groupBy, mapWithIndex, take) +import Data.Array.NonEmpty as NEA +import Data.BigInteger (BigInteger) +import Data.Either (either) +import Data.Json.JsonTuple (JsonTuple(..)) +import Data.Lens (Lens') +import Data.Lens.Record (prop) +import Data.Maybe (Maybe(..)) +import Data.Symbol (SProxy(..)) +import Data.Tuple (Tuple(..)) +import Halogen as H +import Halogen.HTML as HH +import Halogen.HTML.Properties (classes) +import Network.RemoteData (RemoteData(..)) +import Network.RemoteData as RD +import Network.RemoteData as RemoteData +import Plutus.PAB.Simulation (MarketplaceContracts) +import Plutus.PAB.Webserver.Types (ContractInstanceClientState) +import Plutus.V1.Ledger.Crypto (PubKeyHash) +import Plutus.V1.Ledger.Value (AssetClass, Value) +import PlutusTx.AssocMap as Map +import View.RemoteDataState (remoteDataState) +import Utils.BEM as BEM +import Data.Unit + +component :: + forall m query input output. + LogMessages m => + PollContract m => + H.Component HH.HTML query input output m +component = + H.mkComponent + { initialState + , render + , eval: H.mkEval $ H.defaultEval { handleAction = handleAction } + } + where + initialState _ = unit + + render _ = + HH.text "HIII!!!!!!!!!!!!!!!!!!!!!!" + + handleAction _ = pure unit diff --git a/MetaLamp/nft-marketplace/client/src/Component/MainPage.scss b/MetaLamp/nft-marketplace/client/src/Component/MainPage.scss new file mode 100644 index 000000000..424097994 --- /dev/null +++ b/MetaLamp/nft-marketplace/client/src/Component/MainPage.scss @@ -0,0 +1,17 @@ +.content { + display: flex; + max-width: 1300px; + + &__statistics { + width: 40%; + } + + &__contracts { + width: 60%; + } + + &__contracts-list { + display: flex; + justify-content: space-evenly; + } +} diff --git a/MetaLamp/nft-marketplace/client/src/Component/Utils.purs b/MetaLamp/nft-marketplace/client/src/Component/Utils.purs new file mode 100644 index 000000000..a4a36ce49 --- /dev/null +++ b/MetaLamp/nft-marketplace/client/src/Component/Utils.purs @@ -0,0 +1,22 @@ +module Component.Utils where + +import Prelude +import Capability.LogMessages (class LogMessages, logError) +import Data.Either (Either) +import Data.Lens (Lens') +import Halogen as H +import Network.RemoteData (RemoteData(..)) +import Utils.WithRemoteData (runRDWith) + +runRD :: + forall e a s action slots output m. + LogMessages m => + Show e => + (Lens' s (RemoteData e a)) -> + H.HalogenM s action slots output m (Either e a) -> + H.HalogenM s action slots output m Unit +runRD selector action = + (runRDWith selector $ action) + >>= case _ of + Failure e -> logError <<< show $ e + _ -> pure unit diff --git a/MetaLamp/nft-marketplace/client/src/Main.purs b/MetaLamp/nft-marketplace/client/src/Main.purs new file mode 100644 index 000000000..1b3ce2ac2 --- /dev/null +++ b/MetaLamp/nft-marketplace/client/src/Main.purs @@ -0,0 +1,21 @@ +module Main where + +import Prelude +import Component.MainPage as App +import AppAff (runAppM) +import Effect (Effect) +import Effect.Unsafe (unsafePerformEffect) +import Halogen as H +import Halogen.Aff (awaitBody, runHalogenAff) +import Halogen.VDom.Driver (runUI) + +main :: Effect Unit +main = + runHalogenAff do + let + rootComponent = H.hoist (runAppM { host: "localhost", port: 8080 }) App.component + body <- awaitBody + runUI rootComponent unit body + +onLoad :: Unit +onLoad = unsafePerformEffect main diff --git a/MetaLamp/nft-marketplace/client/src/Utils/BEM.purs b/MetaLamp/nft-marketplace/client/src/Utils/BEM.purs new file mode 100644 index 000000000..247286d76 --- /dev/null +++ b/MetaLamp/nft-marketplace/client/src/Utils/BEM.purs @@ -0,0 +1,9 @@ +module Utils.BEM where + +import Prelude +import Halogen (ClassName(..)) + +block :: String -> String -> ClassName +block base next = case next of + "" -> ClassName base + s -> ClassName $ base <> "__" <> s diff --git a/MetaLamp/nft-marketplace/client/src/Utils/WithRemoteData.purs b/MetaLamp/nft-marketplace/client/src/Utils/WithRemoteData.purs new file mode 100644 index 000000000..40769eb29 --- /dev/null +++ b/MetaLamp/nft-marketplace/client/src/Utils/WithRemoteData.purs @@ -0,0 +1,18 @@ +module Utils.WithRemoteData where + +import Prelude +import Data.Either (Either, either) +import Data.Lens (Lens', over) +import Halogen as H +import Network.RemoteData (RemoteData(..)) + +runRDWith :: + forall e a s action slots output m. + (Lens' s (RemoteData e a)) -> + H.HalogenM s action slots output m (Either e a) -> + H.HalogenM s action slots output m (RemoteData e a) +runRDWith l action = do + H.modify_ $ over l (const Loading) + result <- either Failure Success <$> action + H.modify_ $ over l (const result) + pure result diff --git a/MetaLamp/nft-marketplace/client/src/View/FundsTable.purs b/MetaLamp/nft-marketplace/client/src/View/FundsTable.purs new file mode 100644 index 000000000..0396fffab --- /dev/null +++ b/MetaLamp/nft-marketplace/client/src/View/FundsTable.purs @@ -0,0 +1,26 @@ +module View.FundsTable where + +import Prelude +import Data.BigInteger (BigInteger, fromInt) +import Data.Tuple (Tuple(..)) +import Halogen.HTML as HH +import Plutus.V1.Ledger.Value (TokenName(..), Value(..)) +import PlutusTx.AssocMap as Map + +fundsTable :: forall props act. Value -> HH.HTML props act +fundsTable (Value ({ getValue: m })) = + HH.div_ + $ do + (Tuple _ amounts) <- Map.toTuples m + (Tuple name amount) <- Map.toTuples amounts + if amount > (fromInt 0) then + pure $ amountTab name amount + else + [] + +amountTab :: forall props act. TokenName -> BigInteger -> HH.HTML props act +amountTab (TokenName { unTokenName: name }) amount = HH.div_ $ [ HH.text (showName name <> " " <> show amount) ] + where + showName "" = "ADA" + + showName n = n diff --git a/MetaLamp/nft-marketplace/client/src/View/RemoteDataState.purs b/MetaLamp/nft-marketplace/client/src/View/RemoteDataState.purs new file mode 100644 index 000000000..9b35ee0fc --- /dev/null +++ b/MetaLamp/nft-marketplace/client/src/View/RemoteDataState.purs @@ -0,0 +1,12 @@ +module View.RemoteDataState where + +import Prelude +import Halogen.HTML as HH +import Network.RemoteData (RemoteData(..)) + +remoteDataState :: forall props act e a. Show e => (a -> HH.HTML props act) -> RemoteData e a -> HH.HTML props act +remoteDataState onSuccess = case _ of + NotAsked -> HH.div_ [ HH.text "" ] + Loading -> HH.div_ [ HH.text "Loading..." ] + Failure e -> HH.div_ [ HH.text "Something went wrong" ] + Success a -> onSuccess a diff --git a/MetaLamp/nft-marketplace/client/src/View/Utils.purs b/MetaLamp/nft-marketplace/client/src/View/Utils.purs new file mode 100644 index 000000000..0c8b1ac4f --- /dev/null +++ b/MetaLamp/nft-marketplace/client/src/View/Utils.purs @@ -0,0 +1,8 @@ +module View.Utils where + +import Data.Json.JsonTuple (JsonTuple(..)) +import Data.Tuple (Tuple(..)) +import Plutus.V1.Ledger.Value (AssetClass(..), TokenName(..)) + +assetName :: AssetClass -> String +assetName (AssetClass { unAssetClass: JsonTuple (Tuple _ (TokenName { unTokenName: name })) }) = name diff --git a/MetaLamp/nft-marketplace/client/static/favicon.ico b/MetaLamp/nft-marketplace/client/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c65686613290bb4b637c78a6495133833369ec1e GIT binary patch literal 1406 zcmeH{Jyg?B7>3`f)GAe@RjmKCV4)yjEU59{sG#u&EU5Hit5_5?%962b$BY@XX6%?T z>m4&@?3%G-$NIK9cpP;lIXB+i{3YbG=a~h&fha5t+ zJXVRa8^9tPZiu+kg+{Mi4h%1X-;U`e=OqN!Wpg=GLE4;0ZjTR?xLt^Fx?i}hvrBaoU=Gr)C`!GMfN$z@8p`M>!M6{!Zz%% z164O5$S&Cm+N=>8tePFbqiX~aUChir&=*w=sxX<^uU-!=dfn#pJdZAfP2Z!}L=%?d z!IpdvyU)k-3tpHJ5c^D|o$8HlAy;Y2o{FVqVY zrQIA-S3W;EbmvC?SS_6_mF95j+3V@|H*X&u9!{sv+*-lqH{->3JKy##o3+AybpdOm z@)5fIl5W98}L=?B~UlT%aVt4r56zc1b`t8AIU!+A9z`+tWL}+ z=eAO0@oBS~8Ji!Qd3*An=<|8S$Y@i_?uCtwwLS}RX=`Ck2=&9o-~8oZ--?mbgY`me N4v=?}`E&fQ@e|8(0FeLy literal 0 HcmV?d00001 diff --git a/MetaLamp/nft-marketplace/client/static/index.html b/MetaLamp/nft-marketplace/client/static/index.html new file mode 100644 index 000000000..7e5db2166 --- /dev/null +++ b/MetaLamp/nft-marketplace/client/static/index.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/MetaLamp/nft-marketplace/client/webpack.config.js b/MetaLamp/nft-marketplace/client/webpack.config.js new file mode 100644 index 000000000..0d77c0e52 --- /dev/null +++ b/MetaLamp/nft-marketplace/client/webpack.config.js @@ -0,0 +1,118 @@ +'use strict'; + +const ExtractTextPlugin = require("extract-text-webpack-plugin"); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const path = require('path'); +const webpack = require('webpack'); + +const isWebpackDevServer = process.argv.some(a => path.basename(a) === 'webpack-dev-server'); + +const isWatch = process.argv.some(a => a === '--watch'); + +const plugins = + isWebpackDevServer || !isWatch ? [] : [ + function(){ + this.plugin('done', function(stats){ + process.stderr.write(stats.toString('errors-only')); + }); + } + ] +; + +// source map adds many Mb to the output! +const devtool = isWebpackDevServer ? 'eval-source-map' : false; + +module.exports = { + devtool, + + devServer: { + contentBase: path.join(__dirname, "dist"), + compress: true, + port: 8009, + https: true, + proxy: { + "/api": { + target: 'http://localhost:8080' + }, + "/ws": { + target: 'ws://localhost:8080', + ws: true, + onError(err) { + console.log('Error with the WebSocket:', err); + } + } + } + }, + + entry: './entry.js', + + output: { + path: path.join(__dirname, 'dist'), + pathinfo: true, + filename: 'app.[hash].js' + }, + + module: { + rules: [ + { test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "url-loader?limit=10000&mimetype=application/font-woff" }, + { test: /fontawesome-.*\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "file-loader" }, + { + test: /\.purs$/, + use: [ + { + loader: 'purs-loader', + options: { + src: [ + 'src/**/*.purs', + 'generated/**/*.purs', + '.spago/*/*/src/**/*.purs', + 'plutus-purs/web-common-plutus/**/*.purs', + 'plutus-purs/web-common/**/*.purs' + ], + psc: null, + bundle: !(isWebpackDevServer || isWatch), + warnings: true, + watch: isWebpackDevServer || isWatch, + pscPackage: false, + pscIde: false + } + } + ] + }, + { + test: /\.css$/, + use: ['style-loader', 'css-loader'] + }, + { + test: /\.scss$/, + use: ['style-loader', 'css-loader', 'sass-loader'] + }, + { + test: /\.(gif|png|jpe?g|svg)$/i, + use: 'url-loader' + } + ] + }, + + resolve: { + modules: [ + // We need the second entry for node to be able to + // locate `node_modules` from client directory when + // modules are referenced from inside `web-common`. + 'node_modules', path.resolve(__dirname, './node_modules') + ], + extensions: [ '.purs', '.js'] + }, + + plugins: [ + new webpack.LoaderOptionsPlugin({ + debug: true + }), + new HtmlWebpackPlugin({ + template: 'static/index.html', + favicon: 'static/favicon.ico', + title: 'SCB', + productName: 'plutus-pab' + }) + ].concat(plugins) +}; diff --git a/MetaLamp/nft-marketplace/generate-purs/MarketplaceTypes.hs b/MetaLamp/nft-marketplace/generate-purs/MarketplaceTypes.hs index fe6aa2408..f9ebe6b33 100644 --- a/MetaLamp/nft-marketplace/generate-purs/MarketplaceTypes.hs +++ b/MetaLamp/nft-marketplace/generate-purs/MarketplaceTypes.hs @@ -33,6 +33,8 @@ import qualified PSGenerator.Common import Plutus.Abstract.ContractResponse (ContractResponse) import qualified Plutus.Contracts.NftMarketplace.Endpoints as Marketplace import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace +import qualified Ext.Plutus.Contracts.Auction as Auction +import qualified Plutus.Contracts.Services.Sale as Sale import Plutus.PAB.Simulation (MarketplaceContracts (..)) import Plutus.V1.Ledger.Value (AssetClass) @@ -52,15 +54,18 @@ marketplaceTypes = [ (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.Marketplace) , (equal <*> (genericShow <*> mkSumType)) (Proxy @(ContractResponse E A)) , (order <*> (equal <*> (genericShow <*> mkSumType))) (Proxy @AssetClass) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.MarketplaceDatum) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.UserItemId) , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.UserContractState) , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.InfoContractState) - , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.MarketplaceDatum) , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.NftInfo) , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.NFT) , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.Bundle) , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.BundleInfo) , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.NftBundle) - , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.UserItemId) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @Auction.AuctionState) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @Auction.HighestBid) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @Sale.Sale) , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.CreateNftParams) , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.OpenSaleParams) , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.CloseLotParams) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs index 3120a542a..2fa22d1a7 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs @@ -7,6 +7,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeOperators #-} module Plutus.Contracts.NftMarketplace.OffChain.Info where @@ -111,6 +112,8 @@ data InfoContractState = deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON) +Lens.makeClassyPrisms ''InfoContractState + infoEndpoints :: Core.Marketplace -> Contract (ContractResponse Text InfoContractState) MarketplaceInfoSchema Void () infoEndpoints marketplace = forever $ withContractResponse (Proxy @"fundsAt") FundsAt fundsAt From 883103201e491a111c5a0355efd2169abe97be83 Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Mon, 23 Aug 2021 21:49:05 +0700 Subject: [PATCH 063/217] add wallet selector --- .../client/src/Component/WalletSelector.purs | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 MetaLamp/nft-marketplace/client/src/Component/WalletSelector.purs diff --git a/MetaLamp/nft-marketplace/client/src/Component/WalletSelector.purs b/MetaLamp/nft-marketplace/client/src/Component/WalletSelector.purs new file mode 100644 index 000000000..8616ef67c --- /dev/null +++ b/MetaLamp/nft-marketplace/client/src/Component/WalletSelector.purs @@ -0,0 +1,68 @@ +module Component.WalletSelector where + +import Prelude + +import Data.Maybe (Maybe(..), fromMaybe, maybe) +import Halogen as H +import Halogen.HTML as HH +import Halogen.HTML.Events as HE +import Halogen.HTML.Properties as HP +import Data.Generic.Rep (class Generic) +import Data.Generic.Rep.Show (genericShow) + +data UserWallet = WalletA | WalletB | WalletC + +derive instance eqUserWallet :: Eq UserWallet +derive instance ordUserWallet :: Ord UserWallet +derive instance genericUserWallet :: Generic UserWallet _ + +instance showUserWallet :: Show UserWallet where + show = genericShow + +userWallets :: Array UserWallet +userWallets = [WalletA , WalletB , WalletC] + +fromString :: String -> Maybe UserWallet +fromString "WalletA" = Just WalletA +fromString "WalletB" = Just WalletB +fromString "WalletC" = Just WalletC +fromString _ = Nothing + +data Output + = Submit UserWallet + +data Action + = Choose UserWallet + +type State + = { activeWallet :: UserWallet } + +initialState :: forall i. i -> State +initialState _ = { activeWallet: WalletA } + +component :: forall query m i. H.Component HH.HTML query i Output m +component = + H.mkComponent + { initialState: initialState + , render: render + , eval: H.mkEval $ H.defaultEval { handleAction = handleAction } + } + where + render :: forall w. State -> HH.HTML w Action + render state = + HH.div_ + [ HH.select + [ HP.value $ show state.activeWallet, HE.onValueChange (map Choose <<< fromString) ] + ( map + ( \w -> + HH.option [ HP.value $ show w, HP.selected (w == state.activeWallet) ] [ HH.text $ show w ] + ) + userWallets + ) + ] + + handleAction :: Action -> H.HalogenM State Action () Output m Unit + handleAction = case _ of + Choose wallet -> do + H.modify_ _ { activeWallet = wallet } + H.raise $ Submit wallet From 00974a2f928edfb6bbd44f6890daa144658ea4cd Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Thu, 26 Aug 2021 14:04:27 +0700 Subject: [PATCH 064/217] add wallet selector to main --- .../client/src/Component/MainPage.purs | 40 +++++++++++++------ .../client/src/Component/WalletSelector.purs | 19 +++++++-- 2 files changed, 44 insertions(+), 15 deletions(-) diff --git a/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs b/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs index ec8f3be52..2cbb2b0b8 100644 --- a/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs +++ b/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs @@ -1,11 +1,13 @@ module Component.MainPage where +import Data.Unit import Prelude import Business.Marketplace as Marketplace import Business.MarketplaceInfo as MarketplaceInfo -import Capability.LogMessages (class LogMessages) +import Capability.LogMessages (class LogMessages, logInfo) import Capability.PollContract (class PollContract) import Component.Utils (runRD) +import Component.WalletSelector as WalletSelector import Control.Monad.Except (lift, runExceptT, throwError) import Control.Parallel (parTraverse) import Data.Array (catMaybes, findMap, groupBy, mapWithIndex, take) @@ -29,9 +31,14 @@ import Plutus.PAB.Webserver.Types (ContractInstanceClientState) import Plutus.V1.Ledger.Crypto (PubKeyHash) import Plutus.V1.Ledger.Value (AssetClass, Value) import PlutusTx.AssocMap as Map -import View.RemoteDataState (remoteDataState) import Utils.BEM as BEM -import Data.Unit +import View.RemoteDataState (remoteDataState) + +type Slots + = ( walletSelector :: WalletSelector.ButtonSlot Unit ) + +data Action + = ChooseWallet WalletSelector.Output component :: forall m query input output. @@ -40,14 +47,23 @@ component :: H.Component HH.HTML query input output m component = H.mkComponent - { initialState - , render - , eval: H.mkEval $ H.defaultEval { handleAction = handleAction } - } - where - initialState _ = unit + { initialState + , render + , eval: H.mkEval $ H.defaultEval { handleAction = handleAction } + } + where + initialState :: forall i. i -> Unit + initialState _ = unit - render _ = - HH.text "HIII!!!!!!!!!!!!!!!!!!!!!!" + render :: forall state. state -> H.ComponentHTML Action Slots m + render _ = + HH.div_ + [ HH.text "Choose wallet: " + , HH.slot WalletSelector._walletSelector unit WalletSelector.component unit (Just <<< ChooseWallet) + ] - handleAction _ = pure unit + handleAction :: Action -> H.HalogenM Unit Action Slots output m Unit + handleAction = case _ of + ChooseWallet (WalletSelector.Submit w) -> do + logInfo $ show w + pure unit diff --git a/MetaLamp/nft-marketplace/client/src/Component/WalletSelector.purs b/MetaLamp/nft-marketplace/client/src/Component/WalletSelector.purs index 8616ef67c..30b5d836d 100644 --- a/MetaLamp/nft-marketplace/client/src/Component/WalletSelector.purs +++ b/MetaLamp/nft-marketplace/client/src/Component/WalletSelector.purs @@ -1,7 +1,6 @@ module Component.WalletSelector where import Prelude - import Data.Maybe (Maybe(..), fromMaybe, maybe) import Halogen as H import Halogen.HTML as HH @@ -9,23 +8,37 @@ import Halogen.HTML.Events as HE import Halogen.HTML.Properties as HP import Data.Generic.Rep (class Generic) import Data.Generic.Rep.Show (genericShow) +import Data.Symbol (SProxy(..)) + +type ButtonSlot id + = forall query. H.Slot query Output id -data UserWallet = WalletA | WalletB | WalletC +_walletSelector = SProxy :: SProxy "walletSelector" + +data UserWallet + = WalletA + | WalletB + | WalletC derive instance eqUserWallet :: Eq UserWallet + derive instance ordUserWallet :: Ord UserWallet + derive instance genericUserWallet :: Generic UserWallet _ instance showUserWallet :: Show UserWallet where show = genericShow userWallets :: Array UserWallet -userWallets = [WalletA , WalletB , WalletC] +userWallets = [ WalletA, WalletB, WalletC ] fromString :: String -> Maybe UserWallet fromString "WalletA" = Just WalletA + fromString "WalletB" = Just WalletB + fromString "WalletC" = Just WalletC + fromString _ = Nothing data Output From ff5c9eee602a62d41fa12ce36475fa2f23677cf1 Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Thu, 26 Aug 2021 17:55:29 +0700 Subject: [PATCH 065/217] rename wallet selector --- MetaLamp/nft-marketplace/client/src/Component/MainPage.purs | 2 +- .../nft-marketplace/client/src/Component/WalletSelector.purs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs b/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs index 2cbb2b0b8..6c698b111 100644 --- a/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs +++ b/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs @@ -35,7 +35,7 @@ import Utils.BEM as BEM import View.RemoteDataState (remoteDataState) type Slots - = ( walletSelector :: WalletSelector.ButtonSlot Unit ) + = ( walletSelector :: WalletSelector.WalletSelectorSlot Unit ) data Action = ChooseWallet WalletSelector.Output diff --git a/MetaLamp/nft-marketplace/client/src/Component/WalletSelector.purs b/MetaLamp/nft-marketplace/client/src/Component/WalletSelector.purs index 30b5d836d..75bda581d 100644 --- a/MetaLamp/nft-marketplace/client/src/Component/WalletSelector.purs +++ b/MetaLamp/nft-marketplace/client/src/Component/WalletSelector.purs @@ -10,7 +10,7 @@ import Data.Generic.Rep (class Generic) import Data.Generic.Rep.Show (genericShow) import Data.Symbol (SProxy(..)) -type ButtonSlot id +type WalletSelectorSlot id = forall query. H.Slot query Output id _walletSelector = SProxy :: SProxy "walletSelector" From 7b4e4f5ec512ef48a73d08350b34249c53a0f6b2 Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Thu, 26 Aug 2021 18:27:46 +0700 Subject: [PATCH 066/217] add routing infrastructure --- MetaLamp/nft-marketplace/client/spago.dhall | 2 ++ .../nft-marketplace/client/src/AppAff.purs | 7 +++++ .../client/src/Capability/Navigate.purs | 12 ++++++++ .../client/src/Data/Route.purs | 30 +++++++++++++++++++ 4 files changed, 51 insertions(+) create mode 100644 MetaLamp/nft-marketplace/client/src/Capability/Navigate.purs create mode 100644 MetaLamp/nft-marketplace/client/src/Data/Route.purs diff --git a/MetaLamp/nft-marketplace/client/spago.dhall b/MetaLamp/nft-marketplace/client/spago.dhall index e7b989c66..c1041dcf2 100644 --- a/MetaLamp/nft-marketplace/client/spago.dhall +++ b/MetaLamp/nft-marketplace/client/spago.dhall @@ -27,6 +27,8 @@ You can edit this file as you like. , "undefinable" , "uuid" , "web-socket" + , "routing" + , "routing-duplex" ] , packages = ./packages.dhall , sources = diff --git a/MetaLamp/nft-marketplace/client/src/AppAff.purs b/MetaLamp/nft-marketplace/client/src/AppAff.purs index ed0fba455..f78dfc9cf 100644 --- a/MetaLamp/nft-marketplace/client/src/AppAff.purs +++ b/MetaLamp/nft-marketplace/client/src/AppAff.purs @@ -19,6 +19,10 @@ import Effect.Console as Console import Foreign.Generic (class Decode, decode, encodeJSON) import Servant.PureScript.Ajax (AjaxError(..), ErrorDescription(..), ajax) import Type.Equality (class TypeEquals, from) +import Capability.Navigate +import Data.Route +import Routing.Hash as Routing +import Routing.Duplex as Routing type Env = { host :: String, port :: Int } @@ -88,3 +92,6 @@ instance contractAppM :: Contract AppM where instance pollContractAppM :: PollContract AppM where pollDelay = liftAff <<< delay <<< Milliseconds $ 1000.0 tooManyRetries retryCount = pure $ retryCount > 20 + +instance navigateAppM :: Navigate AppM where + navigate = liftEffect <<< Routing.setHash <<< Routing.print routeCodec diff --git a/MetaLamp/nft-marketplace/client/src/Capability/Navigate.purs b/MetaLamp/nft-marketplace/client/src/Capability/Navigate.purs new file mode 100644 index 000000000..4d54cec6c --- /dev/null +++ b/MetaLamp/nft-marketplace/client/src/Capability/Navigate.purs @@ -0,0 +1,12 @@ +module Capability.Navigate where + +import Prelude +import Data.Route +import Halogen + +class + Monad m <= Navigate m where + navigate :: Route -> m Unit + +instance navigateHalogenM :: Navigate m => Navigate (HalogenM state action slots msg m) where + navigate = lift <<< navigate diff --git a/MetaLamp/nft-marketplace/client/src/Data/Route.purs b/MetaLamp/nft-marketplace/client/src/Data/Route.purs new file mode 100644 index 000000000..34e49ce38 --- /dev/null +++ b/MetaLamp/nft-marketplace/client/src/Data/Route.purs @@ -0,0 +1,30 @@ +module Data.Route where + +import Prelude +import Data.Generic.Rep (class Generic) +import Data.Generic.Rep.Show (genericShow) +-- Routing +import Routing.Duplex (RouteDuplex', root) +import Routing.Duplex.Generic (noArgs, sum) +import Routing.Duplex.Generic.Syntax ((/)) + +data Route + = UserPage + | Marketplace + +derive instance genericRoute :: Generic Route _ + +derive instance eqRoute :: Eq Route + +derive instance ordRoute :: Ord Route + +instance showRoute :: Show Route where + show = genericShow + +routeCodec :: RouteDuplex' Route +routeCodec = + root + $ sum + { "UserPage": noArgs + , "Marketplace": "marketplace" / noArgs + } From a6e7207507b9bde87999a34658f9dfe8f00dba09 Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Thu, 26 Aug 2021 20:49:45 +0700 Subject: [PATCH 067/217] add routing --- .../client/src/Component/MainPage.purs | 102 +++++++++++++++--- .../client/src/Component/MarketPage.purs | 24 +++++ .../client/src/Component/UserPage.purs | 24 +++++ .../client/src/Data/Route.purs | 4 +- MetaLamp/nft-marketplace/client/src/Main.purs | 15 ++- 5 files changed, 153 insertions(+), 16 deletions(-) create mode 100644 MetaLamp/nft-marketplace/client/src/Component/MarketPage.purs create mode 100644 MetaLamp/nft-marketplace/client/src/Component/UserPage.purs diff --git a/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs b/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs index 6c698b111..4e549cd5f 100644 --- a/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs +++ b/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs @@ -1,11 +1,15 @@ module Component.MainPage where +import Data.Route import Data.Unit import Prelude import Business.Marketplace as Marketplace import Business.MarketplaceInfo as MarketplaceInfo import Capability.LogMessages (class LogMessages, logInfo) +import Capability.Navigate (class Navigate, navigate) import Capability.PollContract (class PollContract) +import Component.MarketPage as Market +import Component.UserPage as User import Component.Utils (runRD) import Component.WalletSelector as WalletSelector import Control.Monad.Except (lift, runExceptT, throwError) @@ -13,13 +17,14 @@ import Control.Parallel (parTraverse) import Data.Array (catMaybes, findMap, groupBy, mapWithIndex, take) import Data.Array.NonEmpty as NEA import Data.BigInteger (BigInteger) -import Data.Either (either) +import Data.Either (either, hush) import Data.Json.JsonTuple (JsonTuple(..)) import Data.Lens (Lens') import Data.Lens.Record (prop) -import Data.Maybe (Maybe(..)) +import Data.Maybe (Maybe(..), fromMaybe) import Data.Symbol (SProxy(..)) import Data.Tuple (Tuple(..)) +import Effect.Class (class MonadEffect) import Halogen as H import Halogen.HTML as HH import Halogen.HTML.Properties (classes) @@ -31,39 +36,112 @@ import Plutus.PAB.Webserver.Types (ContractInstanceClientState) import Plutus.V1.Ledger.Crypto (PubKeyHash) import Plutus.V1.Ledger.Value (AssetClass, Value) import PlutusTx.AssocMap as Map +import Routing.Duplex as Routing +import Routing.Hash as Routing import Utils.BEM as BEM import View.RemoteDataState (remoteDataState) +import Web.Event.Event (preventDefault) +import Web.UIEvent.MouseEvent (MouseEvent, toEvent) +import Halogen.HTML.Properties as HP +import Halogen.HTML.Events as HE + +type State + = { route :: Maybe Route + } + +data Query a + = Navigate Route a type Slots - = ( walletSelector :: WalletSelector.WalletSelectorSlot Unit ) + = ( walletSelector :: WalletSelector.WalletSelectorSlot Unit + , userPage :: User.Slot Unit + , marketPage :: Market.Slot Unit + ) data Action - = ChooseWallet WalletSelector.Output + = Initialize + | GoTo Route MouseEvent + | ChooseWallet WalletSelector.Output component :: - forall m query input output. + forall m input output. + MonadEffect m => + Navigate m => LogMessages m => PollContract m => - H.Component HH.HTML query input output m + H.Component HH.HTML Query input output m component = H.mkComponent { initialState , render - , eval: H.mkEval $ H.defaultEval { handleAction = handleAction } + , eval: + H.mkEval + $ H.defaultEval + { handleAction = handleAction + , handleQuery = handleQuery + , initialize = Just Initialize + } } where - initialState :: forall i. i -> Unit - initialState _ = unit + initialState :: input -> State + initialState _ = { route: Nothing } - render :: forall state. state -> H.ComponentHTML Action Slots m - render _ = + render :: State -> H.ComponentHTML Action Slots m + render st = HH.div_ [ HH.text "Choose wallet: " , HH.slot WalletSelector._walletSelector unit WalletSelector.component unit (Just <<< ChooseWallet) + , pages st ] - handleAction :: Action -> H.HalogenM Unit Action Slots output m Unit + handleAction :: Action -> H.HalogenM State Action Slots output m Unit handleAction = case _ of + Initialize -> do + initialRoute <- hush <<< (Routing.parse routeCodec) <$> H.liftEffect Routing.getHash + navigate $ fromMaybe UserPage initialRoute + GoTo route e -> do + H.liftEffect $ preventDefault (toEvent e) + oldRoute <- H.gets _.route + when (oldRoute /= Just route) $ navigate route ChooseWallet (WalletSelector.Submit w) -> do logInfo $ show w pure unit + + handleQuery :: forall a. Query a -> H.HalogenM State Action Slots output m (Maybe a) + handleQuery = case _ of + Navigate route a -> do + oldRoute <- H.gets _.route + when (oldRoute /= Just route) + $ H.modify_ _ { route = Just route } + pure (Just a) + +pages :: forall m. State -> H.ComponentHTML Action Slots m +pages st = + navbar + $ case st.route of + Nothing -> HH.h1_ [ HH.text "Page wasn't found" ] + Just route -> case route of + UserPage -> HH.slot User._userPage unit User.component unit absurd + MarketPage -> HH.slot Market._marketPage unit Market.component unit absurd + +navbar :: forall w. HH.HTML w Action -> HH.HTML w Action +navbar html = + HH.div_ + [ HH.ul_ + [ HH.li_ + [ HH.a + [ HP.href "#" + , HE.onClick (Just <<< GoTo UserPage) + ] + [ HH.text "Personal" ] + ] + , HH.li_ + [ HH.a + [ HP.href "#" + , HE.onClick (Just <<< GoTo MarketPage) + ] + [ HH.text "Market" ] + ] + ] + , html + ] diff --git a/MetaLamp/nft-marketplace/client/src/Component/MarketPage.purs b/MetaLamp/nft-marketplace/client/src/Component/MarketPage.purs new file mode 100644 index 000000000..6ec0ac469 --- /dev/null +++ b/MetaLamp/nft-marketplace/client/src/Component/MarketPage.purs @@ -0,0 +1,24 @@ +module Component.MarketPage where + +import Prelude +import Data.Symbol (SProxy(..)) +import Halogen (Component) +import Halogen as H +import Halogen.HTML as HH + +type Slot id + = forall query. H.Slot query Void id + +_marketPage :: SProxy "marketPage" +_marketPage = SProxy + +component :: forall q i o m. H.Component HH.HTML q i o m +component = + H.mkComponent + { initialState: identity + , render + , eval: H.mkEval H.defaultEval + } + +render :: forall state action m. state -> H.ComponentHTML action () m +render _ = HH.h1_ [ HH.text "MarketPage" ] diff --git a/MetaLamp/nft-marketplace/client/src/Component/UserPage.purs b/MetaLamp/nft-marketplace/client/src/Component/UserPage.purs new file mode 100644 index 000000000..6abf464d0 --- /dev/null +++ b/MetaLamp/nft-marketplace/client/src/Component/UserPage.purs @@ -0,0 +1,24 @@ +module Component.UserPage where + +import Prelude +import Data.Symbol (SProxy(..)) +import Halogen (Component) +import Halogen as H +import Halogen.HTML as HH + +type Slot id + = forall query. H.Slot query Void id + +_userPage :: SProxy "userPage" +_userPage = SProxy + +component :: forall q i o m. H.Component HH.HTML q i o m +component = + H.mkComponent + { initialState: identity + , render + , eval: H.mkEval H.defaultEval + } + +render :: forall state action m. state -> H.ComponentHTML action () m +render _ = HH.h1_ [ HH.text "UserPage" ] diff --git a/MetaLamp/nft-marketplace/client/src/Data/Route.purs b/MetaLamp/nft-marketplace/client/src/Data/Route.purs index 34e49ce38..684dffcbc 100644 --- a/MetaLamp/nft-marketplace/client/src/Data/Route.purs +++ b/MetaLamp/nft-marketplace/client/src/Data/Route.purs @@ -10,7 +10,7 @@ import Routing.Duplex.Generic.Syntax ((/)) data Route = UserPage - | Marketplace + | MarketPage derive instance genericRoute :: Generic Route _ @@ -26,5 +26,5 @@ routeCodec = root $ sum { "UserPage": noArgs - , "Marketplace": "marketplace" / noArgs + , "MarketPage": "market" / noArgs } diff --git a/MetaLamp/nft-marketplace/client/src/Main.purs b/MetaLamp/nft-marketplace/client/src/Main.purs index 1b3ce2ac2..0000f7673 100644 --- a/MetaLamp/nft-marketplace/client/src/Main.purs +++ b/MetaLamp/nft-marketplace/client/src/Main.purs @@ -1,13 +1,19 @@ module Main where import Prelude -import Component.MainPage as App +import Data.Maybe (Maybe(..)) import AppAff (runAppM) +import Component.MainPage as App +import Data.Route (routeCodec) import Effect (Effect) +import Effect.Class (liftEffect) import Effect.Unsafe (unsafePerformEffect) import Halogen as H import Halogen.Aff (awaitBody, runHalogenAff) import Halogen.VDom.Driver (runUI) +import Routing.Duplex as Routing +import Routing.Hash as Routing +import Effect.Aff (Aff, launchAff_) main :: Effect Unit main = @@ -15,7 +21,12 @@ main = let rootComponent = H.hoist (runAppM { host: "localhost", port: 8080 }) App.component body <- awaitBody - runUI rootComponent unit body + halogenIO <- runUI rootComponent unit body + void $ liftEffect + $ Routing.matchesWith (Routing.parse routeCodec) \oldRoute newRoute -> + when (oldRoute /= Just newRoute) do + launchAff_ $ halogenIO.query $ H.tell $ App.Navigate newRoute + pure unit onLoad :: Unit onLoad = unsafePerformEffect main From 8c1310dd09f070bfa9113db4e671cfc8e6b9aca9 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Fri, 27 Aug 2021 15:48:29 +0700 Subject: [PATCH 068/217] add pab contracts initialization --- .../client/scripts/start-chrome.sh | 2 +- .../client/src/Business/MarketplaceInfo.purs | 15 +++-- .../client/src/Business/MarketplaceUser.purs | 35 ++++++++++++ .../client/src/Component/MainPage.purs | 57 +++++++++++++++++-- .../client/src/Component/WalletSelector.purs | 6 +- 5 files changed, 101 insertions(+), 14 deletions(-) create mode 100644 MetaLamp/nft-marketplace/client/src/Business/MarketplaceUser.purs diff --git a/MetaLamp/nft-marketplace/client/scripts/start-chrome.sh b/MetaLamp/nft-marketplace/client/scripts/start-chrome.sh index 776df5378..15694ecd5 100755 --- a/MetaLamp/nft-marketplace/client/scripts/start-chrome.sh +++ b/MetaLamp/nft-marketplace/client/scripts/start-chrome.sh @@ -1 +1 @@ -chromium --disable-web-security --user-data-dir=/chrome-temp \ No newline at end of file +google-chrome --disable-web-security --user-data-dir=/chrome-temp \ No newline at end of file diff --git a/MetaLamp/nft-marketplace/client/src/Business/MarketplaceInfo.purs b/MetaLamp/nft-marketplace/client/src/Business/MarketplaceInfo.purs index 108c7b918..013eae755 100644 --- a/MetaLamp/nft-marketplace/client/src/Business/MarketplaceInfo.purs +++ b/MetaLamp/nft-marketplace/client/src/Business/MarketplaceInfo.purs @@ -1,17 +1,19 @@ module Business.MarketplaceInfo where +import Ext.Plutus.Contracts.Auction +import Plutus.Contracts.NftMarketplace.OffChain.ID +import Plutus.Contracts.NftMarketplace.OffChain.Info +import Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine import Prelude import Business.Marketplace (getMarketplaceContractId, getMarketplaceResponseWith) import Capability.Contract (ContractId, ContractUnit(..), Endpoint(..)) import Capability.PollContract (class PollContract, PollError) import Data.Either (Either) +import Data.Generic.Rep (class Generic) +import Data.Generic.Rep.Show (genericShow) import Data.Json.JsonTuple (JsonTuple) import Data.Maybe (Maybe) import Data.Newtype (class Newtype, unwrap) -import Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine -import Plutus.Contracts.NftMarketplace.OffChain.ID -import Plutus.Contracts.NftMarketplace.OffChain.Info -import Ext.Plutus.Contracts.Auction import Plutus.PAB.Simulation (MarketplaceContracts, _MarketplaceInfo) import Plutus.PAB.Webserver.Types (ContractInstanceClientState) import Plutus.V1.Ledger.Crypto (PubKeyHash) @@ -23,6 +25,11 @@ newtype InfoContractId derive instance newtypeInfoContractId :: Newtype InfoContractId _ +derive instance genericInfoContractId :: Generic InfoContractId _ + +instance showInfoContractId :: Show InfoContractId where + show = genericShow + getInfoContractId :: ContractInstanceClientState MarketplaceContracts -> Maybe InfoContractId getInfoContractId = map InfoContractId <<< getMarketplaceContractId _MarketplaceInfo diff --git a/MetaLamp/nft-marketplace/client/src/Business/MarketplaceUser.purs b/MetaLamp/nft-marketplace/client/src/Business/MarketplaceUser.purs new file mode 100644 index 000000000..c53665f76 --- /dev/null +++ b/MetaLamp/nft-marketplace/client/src/Business/MarketplaceUser.purs @@ -0,0 +1,35 @@ +module Business.MarketplaceUser where + +import Prelude +import Business.Marketplace (getMarketplaceContractId, getMarketplaceResponseWith) +import Capability.Contract (ContractId, ContractUnit(..), Endpoint(..)) +import Capability.PollContract (class PollContract, PollError) +import Data.Either (Either) +import Data.Generic.Rep (class Generic) +import Data.Generic.Rep.Show (genericShow) +import Data.Maybe (Maybe) +import Data.Newtype (class Newtype, unwrap) +import Plutus.Contracts.NftMarketplace.OffChain.User (_GetPubKey, _GetPubKeyBalance) +import Plutus.PAB.Simulation (MarketplaceContracts, _MarketplaceUser) +import Plutus.PAB.Webserver.Types (ContractInstanceClientState) +import Plutus.V1.Ledger.Crypto (PubKeyHash) +import Plutus.V1.Ledger.Value (Value) + +newtype UserContractId + = UserContractId ContractId + +derive instance newtypeUserContractId :: Newtype UserContractId _ + +derive instance genericUserContractId :: Generic UserContractId _ + +instance showUserContractId :: Show UserContractId where + show = genericShow + +getUserContractId :: ContractInstanceClientState MarketplaceContracts -> Maybe UserContractId +getUserContractId = map UserContractId <<< getMarketplaceContractId _MarketplaceUser + +ownPubKey :: forall m. PollContract m => UserContractId -> m (Either PollError PubKeyHash) +ownPubKey cid = getMarketplaceResponseWith (Endpoint "ownPubKey") _GetPubKey (unwrap cid) ContractUnit + +ownPubKeyBalance :: forall m. PollContract m => UserContractId -> m (Either PollError Value) +ownPubKeyBalance cid = getMarketplaceResponseWith (Endpoint "ownPubKeyBalance") _GetPubKeyBalance (unwrap cid) ContractUnit diff --git a/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs b/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs index 4e549cd5f..f4dd557b6 100644 --- a/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs +++ b/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs @@ -3,11 +3,15 @@ module Component.MainPage where import Data.Route import Data.Unit import Prelude +import Business.Marketplace (getMarketplaceContracts) import Business.Marketplace as Marketplace +import Business.MarketplaceInfo (InfoContractId, getInfoContractId) import Business.MarketplaceInfo as MarketplaceInfo +import Business.MarketplaceUser (UserContractId, getUserContractId, ownPubKey) import Capability.LogMessages (class LogMessages, logInfo) import Capability.Navigate (class Navigate, navigate) import Capability.PollContract (class PollContract) +import Clipboard (handleAction) import Component.MarketPage as Market import Component.UserPage as User import Component.Utils (runRD) @@ -27,7 +31,9 @@ import Data.Tuple (Tuple(..)) import Effect.Class (class MonadEffect) import Halogen as H import Halogen.HTML as HH +import Halogen.HTML.Events as HE import Halogen.HTML.Properties (classes) +import Halogen.HTML.Properties as HP import Network.RemoteData (RemoteData(..)) import Network.RemoteData as RD import Network.RemoteData as RemoteData @@ -42,13 +48,21 @@ import Utils.BEM as BEM import View.RemoteDataState (remoteDataState) import Web.Event.Event (preventDefault) import Web.UIEvent.MouseEvent (MouseEvent, toEvent) -import Halogen.HTML.Properties as HP -import Halogen.HTML.Events as HE type State = { route :: Maybe Route + , contracts :: RemoteData String (Array (ContractInstanceClientState MarketplaceContracts)) + , userInstances :: RemoteData String (Array ({ pubKey :: PubKeyHash, contractId :: UserContractId })) + , currentInstance :: WalletSelector.UserWallet + , infoInstance :: Maybe InfoContractId } +_contracts :: Lens' State (RemoteData String (Array (ContractInstanceClientState MarketplaceContracts))) +_contracts = prop (SProxy :: SProxy "contracts") + +_userInstances :: Lens' State (RemoteData String (Array ({ pubKey :: PubKeyHash, contractId :: UserContractId }))) +_userInstances = prop (SProxy :: SProxy "userInstances") + data Query a = Navigate Route a @@ -62,6 +76,8 @@ data Action = Initialize | GoTo Route MouseEvent | ChooseWallet WalletSelector.Output + | GetContracts + | GetInstances component :: forall m input output. @@ -84,19 +100,27 @@ component = } where initialState :: input -> State - initialState _ = { route: Nothing } + initialState _ = + { route: Nothing + , contracts: NotAsked + , userInstances: NotAsked + , currentInstance: WalletSelector.WalletA + , infoInstance: Nothing + } render :: State -> H.ComponentHTML Action Slots m render st = HH.div_ [ HH.text "Choose wallet: " - , HH.slot WalletSelector._walletSelector unit WalletSelector.component unit (Just <<< ChooseWallet) + , HH.slot WalletSelector._walletSelector unit WalletSelector.component st.currentInstance (Just <<< ChooseWallet) , pages st ] handleAction :: Action -> H.HalogenM State Action Slots output m Unit handleAction = case _ of Initialize -> do + handleAction GetContracts + handleAction GetInstances initialRoute <- hush <<< (Routing.parse routeCodec) <$> H.liftEffect Routing.getHash navigate $ fromMaybe UserPage initialRoute GoTo route e -> do @@ -104,8 +128,29 @@ component = oldRoute <- H.gets _.route when (oldRoute /= Just route) $ navigate route ChooseWallet (WalletSelector.Submit w) -> do - logInfo $ show w - pure unit + logInfo $ "Choose new wallet: " <> show w + H.modify_ _ { currentInstance = w } + GetContracts -> + runRD _contracts <<< runExceptT + $ lift getMarketplaceContracts + >>= either (throwError <<< show) pure + GetInstances -> + runRD _userInstances <<< runExceptT + $ do + state <- lift H.get + contracts <- RemoteData.maybe (throwError "No contracts found") pure state.contracts + case catMaybes (getInfoContractId <$> contracts) of + [ cid ] -> do + lift $ logInfo $ "Found info instance: " <> show cid + lift $ H.modify_ _ { infoInstance = Just cid } + _ -> throwError "Info contract not found" + parTraverse + ( \contractId -> do + lift $ logInfo $ "Found user instance: " <> show contractId + pubKey <- lift (ownPubKey contractId) >>= either (throwError <<< show) pure + pure $ { pubKey, contractId } + ) + (catMaybes <<< map getUserContractId $ contracts) handleQuery :: forall a. Query a -> H.HalogenM State Action Slots output m (Maybe a) handleQuery = case _ of diff --git a/MetaLamp/nft-marketplace/client/src/Component/WalletSelector.purs b/MetaLamp/nft-marketplace/client/src/Component/WalletSelector.purs index 75bda581d..5bc5e1d3b 100644 --- a/MetaLamp/nft-marketplace/client/src/Component/WalletSelector.purs +++ b/MetaLamp/nft-marketplace/client/src/Component/WalletSelector.purs @@ -50,10 +50,10 @@ data Action type State = { activeWallet :: UserWallet } -initialState :: forall i. i -> State -initialState _ = { activeWallet: WalletA } +initialState :: UserWallet -> State +initialState w = { activeWallet: w } -component :: forall query m i. H.Component HH.HTML query i Output m +component :: forall query m. H.Component HH.HTML query UserWallet Output m component = H.mkComponent { initialState: initialState From 14d870e65a2fe63d19da2903c1ee4c651680c99a Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Fri, 27 Aug 2021 17:13:05 +0700 Subject: [PATCH 069/217] rename navigate query --- MetaLamp/nft-marketplace/client/src/Component/MainPage.purs | 6 +++--- MetaLamp/nft-marketplace/client/src/Main.purs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs b/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs index f4dd557b6..df5154617 100644 --- a/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs +++ b/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs @@ -64,7 +64,7 @@ _userInstances :: Lens' State (RemoteData String (Array ({ pubKey :: PubKeyHash, _userInstances = prop (SProxy :: SProxy "userInstances") data Query a - = Navigate Route a + = NavigateTo Route a type Slots = ( walletSelector :: WalletSelector.WalletSelectorSlot Unit @@ -154,7 +154,7 @@ component = handleQuery :: forall a. Query a -> H.HalogenM State Action Slots output m (Maybe a) handleQuery = case _ of - Navigate route a -> do + NavigateTo route a -> do oldRoute <- H.gets _.route when (oldRoute /= Just route) $ H.modify_ _ { route = Just route } @@ -164,7 +164,7 @@ pages :: forall m. State -> H.ComponentHTML Action Slots m pages st = navbar $ case st.route of - Nothing -> HH.h1_ [ HH.text "Page wasn't found" ] + Nothing -> HH.h1_ [ HH.text "Loading page..." ] Just route -> case route of UserPage -> HH.slot User._userPage unit User.component unit absurd MarketPage -> HH.slot Market._marketPage unit Market.component unit absurd diff --git a/MetaLamp/nft-marketplace/client/src/Main.purs b/MetaLamp/nft-marketplace/client/src/Main.purs index 0000f7673..4c628edea 100644 --- a/MetaLamp/nft-marketplace/client/src/Main.purs +++ b/MetaLamp/nft-marketplace/client/src/Main.purs @@ -25,7 +25,7 @@ main = void $ liftEffect $ Routing.matchesWith (Routing.parse routeCodec) \oldRoute newRoute -> when (oldRoute /= Just newRoute) do - launchAff_ $ halogenIO.query $ H.tell $ App.Navigate newRoute + launchAff_ $ halogenIO.query $ H.tell $ App.NavigateTo newRoute pure unit onLoad :: Unit From 0a67c81fc4ddf8918fe81868b641d63330f31690 Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Mon, 30 Aug 2021 20:02:23 +0700 Subject: [PATCH 070/217] add ipfs capability --- .../client/src/Business/Marketplace.purs | 3 ++- .../client/src/Capability/Contract.purs | 9 +------ .../client/src/Capability/IPFS.purs | 25 +++++++++++++++++++ .../client/src/Capability/PollContract.purs | 3 ++- .../client/src/Utils/APIError.purs | 13 ++++++++++ 5 files changed, 43 insertions(+), 10 deletions(-) create mode 100644 MetaLamp/nft-marketplace/client/src/Capability/IPFS.purs create mode 100644 MetaLamp/nft-marketplace/client/src/Utils/APIError.purs diff --git a/MetaLamp/nft-marketplace/client/src/Business/Marketplace.purs b/MetaLamp/nft-marketplace/client/src/Business/Marketplace.purs index 4737f4e6c..76cb40925 100644 --- a/MetaLamp/nft-marketplace/client/src/Business/Marketplace.purs +++ b/MetaLamp/nft-marketplace/client/src/Business/Marketplace.purs @@ -1,7 +1,7 @@ module Business.Marketplace where import Prelude -import Capability.Contract (class Contract, APIError, ContractId(..), Endpoint, getContracts) +import Capability.Contract (class Contract, ContractId(..), Endpoint, getContracts) import Capability.PollContract (class PollContract, LeftPoll(..), PollError, PollResponse, pollEndpoint) import Control.Monad.Except (runExcept, throwError, withExcept) import Data.Either (Either) @@ -16,6 +16,7 @@ import Plutus.PAB.Simulation (MarketplaceContracts) import Plutus.PAB.Webserver.Types (ContractInstanceClientState(..)) import Wallet.Types (ContractInstanceId(..)) import Data.UUID (toString) as UUID +import Utils.APIError getMarketplaceContracts :: forall m. Contract m => m (Either APIError (Array (ContractInstanceClientState MarketplaceContracts))) getMarketplaceContracts = getContracts diff --git a/MetaLamp/nft-marketplace/client/src/Capability/Contract.purs b/MetaLamp/nft-marketplace/client/src/Capability/Contract.purs index 2c35e2310..932d4761d 100644 --- a/MetaLamp/nft-marketplace/client/src/Capability/Contract.purs +++ b/MetaLamp/nft-marketplace/client/src/Capability/Contract.purs @@ -8,14 +8,7 @@ import Foreign (unsafeToForeign) import Foreign.Generic (class Decode, class Encode) import Halogen (HalogenM, lift) import Plutus.PAB.Webserver.Types (ContractInstanceClientState) - -data APIError - = AjaxCallError String - -derive instance genericAPIError :: Generic APIError _ - -instance showAPIError :: Show APIError where - show = genericShow +import Utils.APIError newtype ContractId = ContractId String diff --git a/MetaLamp/nft-marketplace/client/src/Capability/IPFS.purs b/MetaLamp/nft-marketplace/client/src/Capability/IPFS.purs new file mode 100644 index 000000000..2da38fcc4 --- /dev/null +++ b/MetaLamp/nft-marketplace/client/src/Capability/IPFS.purs @@ -0,0 +1,25 @@ +module Capability.IPFS where + +import Prelude +import Utils.APIError + +import Data.Either (Either) +import Data.Generic.Rep (class Generic) +import Data.Generic.Rep.Show (genericShow) +import Foreign (unsafeToForeign) +import Foreign.Generic (class Decode, class Encode) +import Halogen (HalogenM, lift) +import Plutus.PAB.Webserver.Types (ContractInstanceClientState) +import Web.File.File (File) + +type IpfsCid + = String + +class + Monad m <= IPFS m where + pinFile :: File -> m (Either APIError IpfsCid) + catFile :: IpfsCid -> m (Either APIError String) + +instance contractHalogenM :: IPFS m => IPFS (HalogenM st act slots msg m) where + pinFile = pinFile >>> lift + catFile = catFile >>> lift diff --git a/MetaLamp/nft-marketplace/client/src/Capability/PollContract.purs b/MetaLamp/nft-marketplace/client/src/Capability/PollContract.purs index 00e5aad0b..618039315 100644 --- a/MetaLamp/nft-marketplace/client/src/Capability/PollContract.purs +++ b/MetaLamp/nft-marketplace/client/src/Capability/PollContract.purs @@ -1,7 +1,7 @@ module Capability.PollContract where import Prelude -import Capability.Contract (class Contract, APIError(..), ContractId, Endpoint, callEndpoint, getContractStatus) +import Capability.Contract (class Contract, ContractId, Endpoint, callEndpoint, getContractStatus) import Control.Monad.Except (runExceptT, throwError) import Data.Either (Either(..), either) import Data.Generic.Rep (class Generic) @@ -9,6 +9,7 @@ import Data.Generic.Rep.Show (genericShow) import Foreign.Generic (class Decode, class Encode) import Halogen (HalogenM, lift) import Plutus.PAB.Webserver.Types (ContractInstanceClientState) +import Utils.APIError class Contract m <= PollContract m where diff --git a/MetaLamp/nft-marketplace/client/src/Utils/APIError.purs b/MetaLamp/nft-marketplace/client/src/Utils/APIError.purs new file mode 100644 index 000000000..32fac02da --- /dev/null +++ b/MetaLamp/nft-marketplace/client/src/Utils/APIError.purs @@ -0,0 +1,13 @@ +module Utils.APIError where + +import Prelude +import Data.Generic.Rep (class Generic) +import Data.Generic.Rep.Show (genericShow) + +data APIError + = AjaxCallError String + +derive instance genericAPIError :: Generic APIError _ + +instance showAPIError :: Show APIError where + show = genericShow From 740b21df9b8def587f0f8892377819fb05a100fc Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Mon, 30 Aug 2021 21:48:52 +0700 Subject: [PATCH 071/217] wip add ipfs impl --- .../nft-marketplace/client/src/AppAff.purs | 90 ++++++++++++++----- .../client/src/Capability/IPFS.purs | 1 - .../client/src/Component/MainPage.purs | 14 +++ MetaLamp/nft-marketplace/client/src/Main.purs | 7 +- 4 files changed, 89 insertions(+), 23 deletions(-) diff --git a/MetaLamp/nft-marketplace/client/src/AppAff.purs b/MetaLamp/nft-marketplace/client/src/AppAff.purs index f78dfc9cf..95af495f2 100644 --- a/MetaLamp/nft-marketplace/client/src/AppAff.purs +++ b/MetaLamp/nft-marketplace/client/src/AppAff.purs @@ -1,30 +1,45 @@ module AppAff where +import Capability.Navigate +import Data.Route import Prelude -import Affjax (Response, defaultRequest) -import Affjax.RequestBody (RequestBody, string) -import Capability.Contract (class Contract, ContractId(..), Endpoint(..), APIError(..)) +import Utils.APIError +import Affjax (Error, Response, defaultRequest, printError, request) +import Affjax.RequestBody (RequestBody, formData, string) +import Affjax.RequestHeader (RequestHeader(..)) +import Affjax.ResponseFormat as ResponseFormat +import Capability.Contract (class Contract, ContractId(..), Endpoint(..)) +import Capability.IPFS as IPFS import Capability.LogMessages (class LogMessages) import Capability.PollContract (class PollContract) import Control.Monad.Except (ExceptT, runExceptT) import Control.Monad.Reader.Trans (class MonadAsk, ReaderT, asks, runReaderT) +import Data.Argonaut.Core (stringify) import Data.Bifunctor (bimap) -import Data.Either (Either) +import Data.Either (Either(..)) import Data.HTTP.Method (fromString) import Data.Maybe (Maybe(..)) -import Effect.Aff (Aff, Milliseconds(..), delay) +import Data.MediaType.Common (multipartFormData) +import Data.Newtype (wrap) +import Effect (Effect) +import Effect.Aff (Aff, Milliseconds(..), delay, launchAff) import Effect.Aff.Class (class MonadAff, liftAff) import Effect.Class (class MonadEffect, liftEffect) import Effect.Console as Console -import Foreign.Generic (class Decode, decode, encodeJSON) +import Foreign.Generic (class Decode, Foreign, F, decode, encodeJSON) +import Foreign.NullOrUndefined (undefined) +import Routing.Duplex as Routing +import Routing.Hash as Routing import Servant.PureScript.Ajax (AjaxError(..), ErrorDescription(..), ajax) import Type.Equality (class TypeEquals, from) -import Capability.Navigate -import Data.Route -import Routing.Hash as Routing -import Routing.Duplex as Routing +import Web.File.File as FIle +import Web.File.File as File +import Web.XHR.FormData as FormData type Env + = { ipfsServer :: ServerInfo, pabServer :: ServerInfo } + +type ServerInfo = { host :: String, port :: Int } newtype AppM a @@ -56,13 +71,13 @@ instance logMessagesAppM :: LogMessages AppM where errorToString :: AjaxError -> String errorToString (AjaxError e) = case e.description of - ResponseError _ r -> "Response error: " <> r + ResponseError s r -> "Response error: " <> r <> ". with status: " <> show s ResponseFormatError s -> "Parsing error: " <> s DecodingError s -> "Decoding error: " <> s ConnectionError s -> "Connection error: " <> s NotFound -> "Not found" -getBaseURL :: Env -> String +getBaseURL :: ServerInfo -> String getBaseURL { host, port } = "http://" <> host <> ":" <> (show port) runAjax :: forall m a. Monad m => ExceptT AjaxError m (Response a) -> m (Either APIError a) @@ -70,24 +85,53 @@ runAjax = (map toCustom) <<< runExceptT where toCustom = bimap (AjaxCallError <<< errorToString) (\r -> r.body) -get :: forall m a. MonadAsk Env m => MonadAff m => Decode a => String -> m (Either APIError a) -get path = do - url <- asks ((_ <> path) <<< getBaseURL) +getC :: forall m a. MonadAsk Env m => MonadAff m => Decode a => String -> m (Either APIError a) +getC path = do + url <- asks ((_ <> path) <<< getBaseURL <<< _.pabServer) let affReq = defaultRequest { method = fromString "GET", url = url } runAjax $ ajax decode affReq -post :: forall m a. MonadAsk Env m => MonadAff m => Decode a => String -> RequestBody -> m (Either APIError a) -post path body = do - url <- asks ((_ <> path) <<< getBaseURL) +postC :: forall m a. MonadAsk Env m => MonadAff m => Decode a => String -> RequestBody -> m (Either APIError a) +postC path body = do + url <- asks ((_ <> path) <<< getBaseURL <<< _.pabServer) let affReq = defaultRequest { method = fromString "POST", url = url, content = Just body } runAjax $ ajax decode affReq +catIpfs :: forall m. MonadAsk Env m => MonadAff m => String -> m (Either APIError String) +catIpfs path = do + url <- asks ((_ <> path) <<< getBaseURL <<< _.ipfsServer) + let + affReq = defaultRequest { method = fromString "POST", url = url, responseFormat = ResponseFormat.string } + map run $ liftAff $ request affReq + where + run :: Either Error (Response String) -> Either APIError String + run = bimap (AjaxCallError <<< printError) _.body + +fileToFormData :: File.File -> Effect FormData.FormData +fileToFormData file = do + fd <- FormData.new + let + fileName = File.name file + FormData.setBlob (wrap fileName) (File.toBlob file) (Just $ wrap fileName) fd + pure fd + +pinIpfs :: forall m. MonadAsk Env m => MonadAff m => String -> File.File -> m (Either APIError String) +pinIpfs path file = do + url <- asks ((_ <> path) <<< getBaseURL <<< _.ipfsServer) + content <- liftEffect $ fileToFormData file + let + affReq = defaultRequest { method = fromString "POST", url = url, content = Just $ formData content, headers = [ ContentType $ wrap "multipart/form-data; boundary=------------------------35e207f1e7c6a4aa" ] } + map (map _."Hash") $ runAjax $ ajax decodeResp affReq + where + decodeResp :: Foreign -> F { "Hash" :: String, "Name" :: String, "Size" :: String } + decodeResp = decode + instance contractAppM :: Contract AppM where - getContracts = get "/api/new/contract/instances" - getContractStatus (ContractId cid) = get $ "/api/new/contract/instance/" <> cid <> "/status" - callEndpoint (Endpoint endpoint) (ContractId cid) params = post ("/api/new/contract/instance/" <> cid <> "/endpoint/" <> endpoint) (string <<< encodeJSON $ params) + getContracts = getC "/api/new/contract/instances" + getContractStatus (ContractId cid) = getC $ "/api/new/contract/instance/" <> cid <> "/status" + callEndpoint (Endpoint endpoint) (ContractId cid) params = postC ("/api/new/contract/instance/" <> cid <> "/endpoint/" <> endpoint) (string <<< encodeJSON $ params) instance pollContractAppM :: PollContract AppM where pollDelay = liftAff <<< delay <<< Milliseconds $ 1000.0 @@ -95,3 +139,7 @@ instance pollContractAppM :: PollContract AppM where instance navigateAppM :: Navigate AppM where navigate = liftEffect <<< Routing.setHash <<< Routing.print routeCodec + +instance ipfsAppM :: IPFS.IPFS AppM where + pinFile = pinIpfs "/api/v0/add" + catFile cid = catIpfs ("/api/v0/cat?arg=" <> cid) diff --git a/MetaLamp/nft-marketplace/client/src/Capability/IPFS.purs b/MetaLamp/nft-marketplace/client/src/Capability/IPFS.purs index 2da38fcc4..048860112 100644 --- a/MetaLamp/nft-marketplace/client/src/Capability/IPFS.purs +++ b/MetaLamp/nft-marketplace/client/src/Capability/IPFS.purs @@ -2,7 +2,6 @@ module Capability.IPFS where import Prelude import Utils.APIError - import Data.Either (Either) import Data.Generic.Rep (class Generic) import Data.Generic.Rep.Show (genericShow) diff --git a/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs b/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs index df5154617..5a27b63ba 100644 --- a/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs +++ b/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs @@ -8,6 +8,7 @@ import Business.Marketplace as Marketplace import Business.MarketplaceInfo (InfoContractId, getInfoContractId) import Business.MarketplaceInfo as MarketplaceInfo import Business.MarketplaceUser (UserContractId, getUserContractId, ownPubKey) +import Capability.IPFS as IPFS import Capability.LogMessages (class LogMessages, logInfo) import Capability.Navigate (class Navigate, navigate) import Capability.PollContract (class PollContract) @@ -47,6 +48,7 @@ import Routing.Hash as Routing import Utils.BEM as BEM import View.RemoteDataState (remoteDataState) import Web.Event.Event (preventDefault) +import Web.File.File as File import Web.UIEvent.MouseEvent (MouseEvent, toEvent) type State @@ -78,6 +80,7 @@ data Action | ChooseWallet WalletSelector.Output | GetContracts | GetInstances + | HandleFile File.File component :: forall m input output. @@ -85,6 +88,7 @@ component :: Navigate m => LogMessages m => PollContract m => + IPFS.IPFS m => H.Component HH.HTML Query input output m component = H.mkComponent @@ -113,14 +117,21 @@ component = HH.div_ [ HH.text "Choose wallet: " , HH.slot WalletSelector._walletSelector unit WalletSelector.component st.currentInstance (Just <<< ChooseWallet) + , HH.input [ HP.type_ HP.InputFile, HE.onFileUpload f ] , pages st ] + f = case _ of + [ file ] -> Just $ HandleFile file + _ -> Nothing + handleAction :: Action -> H.HalogenM State Action Slots output m Unit handleAction = case _ of Initialize -> do handleAction GetContracts handleAction GetInstances + res <- IPFS.catFile "QmPD9JxfSzKBwHEac9m3zbnQiHnRRYRubL9vCnhoC2AdL4" + logInfo $ show res initialRoute <- hush <<< (Routing.parse routeCodec) <$> H.liftEffect Routing.getHash navigate $ fromMaybe UserPage initialRoute GoTo route e -> do @@ -151,6 +162,9 @@ component = pure $ { pubKey, contractId } ) (catMaybes <<< map getUserContractId $ contracts) + HandleFile file -> do + res <- IPFS.pinFile file + logInfo $ "uploaded file : " <> show res handleQuery :: forall a. Query a -> H.HalogenM State Action Slots output m (Maybe a) handleQuery = case _ of diff --git a/MetaLamp/nft-marketplace/client/src/Main.purs b/MetaLamp/nft-marketplace/client/src/Main.purs index 4c628edea..031d8609a 100644 --- a/MetaLamp/nft-marketplace/client/src/Main.purs +++ b/MetaLamp/nft-marketplace/client/src/Main.purs @@ -15,11 +15,16 @@ import Routing.Duplex as Routing import Routing.Hash as Routing import Effect.Aff (Aff, launchAff_) +-- TODO add to readme +-- ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin '["webui://-", "http://localhost:3000", "http://127.0.0.1:5001", "https://webui.ipfs.io", "https://localhost:8009", "http://localhost:8009"]' +-- ipfs config --json API.HTTPHeaders.Access-Control-Allow-Methods '["PUT", "POST"]' main :: Effect Unit main = runHalogenAff do let - rootComponent = H.hoist (runAppM { host: "localhost", port: 8080 }) App.component + env = { ipfsServer: { host: "localhost", port: 5001 }, pabServer: { host: "localhost", port: 8080 } } + let + rootComponent = H.hoist (runAppM env) App.component body <- awaitBody halogenIO <- runUI rootComponent unit body void $ liftEffect From da890fd0ace4fa750c574deb609b662baa1af952 Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Tue, 31 Aug 2021 17:15:31 +0700 Subject: [PATCH 072/217] add pin ipfs iml --- .../nft-marketplace/client/src/AppAff.purs | 59 +++++++++++++++---- .../client/src/Capability/PollContract.purs | 4 ++ .../client/src/Utils/APIError.purs | 2 + 3 files changed, 52 insertions(+), 13 deletions(-) diff --git a/MetaLamp/nft-marketplace/client/src/AppAff.purs b/MetaLamp/nft-marketplace/client/src/AppAff.purs index 95af495f2..0fe73aa10 100644 --- a/MetaLamp/nft-marketplace/client/src/AppAff.purs +++ b/MetaLamp/nft-marketplace/client/src/AppAff.purs @@ -8,16 +8,17 @@ import Affjax (Error, Response, defaultRequest, printError, request) import Affjax.RequestBody (RequestBody, formData, string) import Affjax.RequestHeader (RequestHeader(..)) import Affjax.ResponseFormat as ResponseFormat +import AjaxUtils (renderForeignErrors) import Capability.Contract (class Contract, ContractId(..), Endpoint(..)) import Capability.IPFS as IPFS import Capability.LogMessages (class LogMessages) import Capability.PollContract (class PollContract) -import Control.Monad.Except (ExceptT, runExceptT) +import Control.Monad.Except (ExceptT, runExcept, runExceptT) import Control.Monad.Reader.Trans (class MonadAsk, ReaderT, asks, runReaderT) -import Data.Argonaut.Core (stringify) -import Data.Bifunctor (bimap) -import Data.Either (Either(..)) -import Data.HTTP.Method (fromString) +import Data.Argonaut.Core (Json, stringify) +import Data.Bifunctor (bimap, lmap) +import Data.Either (Either(..), note) +import Data.HTTP.Method (Method(..), fromString) import Data.Maybe (Maybe(..)) import Data.MediaType.Common (multipartFormData) import Data.Newtype (wrap) @@ -26,15 +27,19 @@ import Effect.Aff (Aff, Milliseconds(..), delay, launchAff) import Effect.Aff.Class (class MonadAff, liftAff) import Effect.Class (class MonadEffect, liftEffect) import Effect.Console as Console +import Foreign (renderForeignError) import Foreign.Generic (class Decode, Foreign, F, decode, encodeJSON) +import Foreign.JSON (decodeJSONWith) import Foreign.NullOrUndefined (undefined) import Routing.Duplex as Routing import Routing.Hash as Routing import Servant.PureScript.Ajax (AjaxError(..), ErrorDescription(..), ajax) import Type.Equality (class TypeEquals, from) -import Web.File.File as FIle import Web.File.File as File import Web.XHR.FormData as FormData +import Web.XHR.ReadyState as XHR +import Web.XHR.ResponseType as XHR +import Web.XHR.XMLHttpRequest as XHR type Env = { ipfsServer :: ServerInfo, pabServer :: ServerInfo } @@ -69,8 +74,8 @@ instance logMessagesAppM :: LogMessages AppM where logInfo = Console.log >>> liftEffect logError = Console.error >>> liftEffect -errorToString :: AjaxError -> String -errorToString (AjaxError e) = case e.description of +ajaxErrorToString :: AjaxError -> String +ajaxErrorToString (AjaxError e) = case e.description of ResponseError s r -> "Response error: " <> r <> ". with status: " <> show s ResponseFormatError s -> "Parsing error: " <> s DecodingError s -> "Decoding error: " <> s @@ -83,7 +88,7 @@ getBaseURL { host, port } = "http://" <> host <> ":" <> (show port) runAjax :: forall m a. Monad m => ExceptT AjaxError m (Response a) -> m (Either APIError a) runAjax = (map toCustom) <<< runExceptT where - toCustom = bimap (AjaxCallError <<< errorToString) (\r -> r.body) + toCustom = bimap (AjaxCallError <<< ajaxErrorToString) (\r -> r.body) getC :: forall m a. MonadAsk Env m => MonadAff m => Decode a => String -> m (Either APIError a) getC path = do @@ -117,14 +122,42 @@ fileToFormData file = do FormData.setBlob (wrap fileName) (File.toBlob file) (Just $ wrap fileName) fd pure fd +pollForReadyState :: forall res. XHR.XMLHttpRequest res -> Aff Unit +pollForReadyState xhr = do + st <- liftEffect $ XHR.readyState xhr + case st of + XHR.Done -> pure unit + _ -> delay (Milliseconds 5.0) *> pollForReadyState xhr + pinIpfs :: forall m. MonadAsk Env m => MonadAff m => String -> File.File -> m (Either APIError String) pinIpfs path file = do url <- asks ((_ <> path) <<< getBaseURL <<< _.ipfsServer) - content <- liftEffect $ fileToFormData file - let - affReq = defaultRequest { method = fromString "POST", url = url, content = Just $ formData content, headers = [ ContentType $ wrap "multipart/form-data; boundary=------------------------35e207f1e7c6a4aa" ] } - map (map _."Hash") $ runAjax $ ajax decodeResp affReq + xhr <- + liftEffect + $ do + form <- fileToFormData file + req <- XHR.xmlHttpRequest XHR.string + XHR.open (Left POST) url req + XHR.sendFormData form req + pure req + liftAff $ pollForReadyState xhr + resp <- + liftEffect $ { status: _, statusText: _, body: _ } + <$> XHR.status xhr + <*> XHR.statusText xhr + <*> XHR.response xhr + pure $ run resp where + run :: + { body :: Maybe String + , status :: Int + , statusText :: String + } -> + Either APIError String + run res = do + json <- note (XhrCallError $ "XHR: null response with status " <> show res.status <> " " <> res.statusText) res.body + bimap (ForeignParseError <<< renderForeignErrors) _."Hash" $ runExcept $ decodeJSONWith decodeResp json + decodeResp :: Foreign -> F { "Hash" :: String, "Name" :: String, "Size" :: String } decodeResp = decode diff --git a/MetaLamp/nft-marketplace/client/src/Capability/PollContract.purs b/MetaLamp/nft-marketplace/client/src/Capability/PollContract.purs index 618039315..399227efb 100644 --- a/MetaLamp/nft-marketplace/client/src/Capability/PollContract.purs +++ b/MetaLamp/nft-marketplace/client/src/Capability/PollContract.purs @@ -75,3 +75,7 @@ pollEndpoint getNext endpoint param cid = toPollError :: APIError -> PollError toPollError (AjaxCallError e) = PollAPIError e + +toPollError (ForeignParseError e) = PollAPIError e + +toPollError (XhrCallError e) = PollAPIError e diff --git a/MetaLamp/nft-marketplace/client/src/Utils/APIError.purs b/MetaLamp/nft-marketplace/client/src/Utils/APIError.purs index 32fac02da..f16118be9 100644 --- a/MetaLamp/nft-marketplace/client/src/Utils/APIError.purs +++ b/MetaLamp/nft-marketplace/client/src/Utils/APIError.purs @@ -6,6 +6,8 @@ import Data.Generic.Rep.Show (genericShow) data APIError = AjaxCallError String + | XhrCallError String + | ForeignParseError String derive instance genericAPIError :: Generic APIError _ From 57ec23c0485b0e2fb99c60b1f6c723dcaf9a3297 Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Thu, 2 Sep 2021 17:39:40 +0700 Subject: [PATCH 073/217] update plutus for backend --- MetaLamp/nft-marketplace/cabal.project | 109 ++++++++++++------ MetaLamp/nft-marketplace/nix/pkgs/default.nix | 7 +- .../nix/pkgs/haskell/default.nix | 3 +- .../nix/pkgs/haskell/haskell.nix | 34 +++--- MetaLamp/nft-marketplace/nix/sources.json | 6 +- 5 files changed, 99 insertions(+), 60 deletions(-) diff --git a/MetaLamp/nft-marketplace/cabal.project b/MetaLamp/nft-marketplace/cabal.project index ad1a2aaad..3b86f5217 100644 --- a/MetaLamp/nft-marketplace/cabal.project +++ b/MetaLamp/nft-marketplace/cabal.project @@ -1,4 +1,4 @@ -index-state: 2021-04-13T00:00:00Z +index-state: 2021-08-14T00:00:00Z packages: ./. @@ -9,6 +9,7 @@ write-ghc-environment-files: never tests: true benchmarks: true +-- Plutus revision from 2021/08/16 source-repository-package type: git location: https://github.com/input-output-hk/plutus.git @@ -17,6 +18,7 @@ source-repository-package playground-common plutus-core plutus-contract + plutus-chain-index plutus-ledger plutus-ledger-api plutus-tx @@ -26,41 +28,66 @@ source-repository-package prettyprinter-configurable quickcheck-dynamic word-array - tag: plutus-starter-devcontainer/v1.0.6 + tag: plutus-starter-devcontainer/v1.0.8 + -- The following sections are copied from the 'plutus' repository cabal.project at the revision -- given above. -- This is necessary because the 'plutus' libraries depend on a number of other libraries which are -- not on Hackage, and so need to be pulled in as `source-repository-package`s themselves. Make sure to -- re-update this section from the template when you do an upgrade. -package eventful-sql-common - ghc-options: -XDerivingStrategies -XStandaloneDeriving -XUndecidableInstances -XDataKinds -XFlexibleInstances + +-- We never, ever, want this. +write-ghc-environment-files: never + +-- Always build tests and benchmarks. +tests: true +benchmarks: true + +-- The only sensible test display option +test-show-details: streaming allow-newer: - -- Has a commit to allow newer aeson, not on Hackage yet - monoidal-containers:aeson -- Pins to an old version of Template Haskell, unclear if/when it will be updated - , size-based:template-haskell - - -- The following two dependencies are needed by plutus. - , eventful-sql-common:persistent - , eventful-sql-common:persistent-template + size-based:template-haskell + , ouroboros-consensus-byron:formatting + , beam-core:aeson + , beam-sqlite:aeson + , beam-sqlite:dlist + , beam-migrate:aeson constraints: - -- aws-lambda-haskell-runtime-wai doesn't compile with newer versions - aws-lambda-haskell-runtime <= 3.0.3 -- big breaking change here, inline-r doens't have an upper bound - , singletons < 3.0 - -- breaks eventful even more than it already was - , persistent-template < 2.12 - + singletons < 3.0 + -- bizarre issue: in earlier versions they define their own 'GEq', in newer + -- ones they reuse the one from 'some', but there isn't e.g. a proper version + -- constraint from dependent-sum-template (which is the library we actually use). + , dependent-sum > 0.6.2.0 + +-- See the note on nix/pkgs/default.nix:agdaPackages for why this is here. +-- (NOTE this will change to ieee754 in newer versions of nixpkgs). extra-packages: ieee, filemanip --- Drops an instance breaking our code. Should be released to Hackage eventually. +-- These packages appear in our dependency tree and are very slow to build. +-- Empirically, turning off optimization shaves off ~50% build time. +-- It also mildly improves recompilation avoidance. +-- For deve work we don't care about performance so much, so this is okay. +package cardano-ledger-alonzo + optimization: False +package ouroboros-consensus-shelley + optimization: False +package ouroboros-consensus-cardano + optimization: False +package cardano-api + optimization: False + +-- https://github.com/Quid2/flat/pull/22 fixes a potential exception +-- when decoding invalid (e.g. malicious) text literals. source-repository-package type: git - location: https://github.com/Quid2/flat.git - tag: 95e5d7488451e43062ca84d5376b3adcc465f1cd + -- location: https://github.com/Quid2/flat.git + location: https://github.com/michaelpj/flat.git + tag: ee59880f47ab835dbd73bea0847dab7869fc20d8 -- Needs some patches, but upstream seems to be fairly dead (no activity in > 1 year) source-repository-package @@ -76,25 +103,22 @@ source-repository-package source-repository-package type: git location: https://github.com/input-output-hk/cardano-crypto.git - tag: ce8f1934e4b6252084710975bd9bbc0a4648ece4 - --- Needs a fix (https://github.com/wenkokke/unlit/pull/11) and a Hackage release -source-repository-package - type: git - location: https://github.com/michaelpj/unlit.git - tag: 9ca1112093c5ffd356fc99c7dafa080e686dd748 + tag: 07397f0e50da97eaa0575d93bee7ac4b2b2576ec source-repository-package type: git location: https://github.com/input-output-hk/cardano-base - tag: a715c7f420770b70bbe95ca51d3dec83866cb1bd + tag: cb0f19c85e5bb5299839ad4ed66af6fa61322cc4 subdir: + base-deriving-via binary binary/test - slotting cardano-crypto-class cardano-crypto-praos cardano-crypto-tests + measures + orphans-deriving-via + slotting strict-containers source-repository-package @@ -108,8 +132,9 @@ source-repository-package source-repository-package type: git location: https://github.com/input-output-hk/ouroboros-network - tag: e50613562d6d4a0f933741fcf590b0f69a1eda67 + tag: 877ce057ff6fb086474c8eaad53f2b7f0e0fce6b subdir: + monoidal-synchronisation typed-protocols typed-protocols-examples ouroboros-network @@ -120,23 +145,27 @@ source-repository-package ouroboros-consensus-cardano ouroboros-consensus-shelley io-sim - io-sim-classes + io-classes network-mux source-repository-package type: git location: https://github.com/input-output-hk/iohk-monitoring-framework - tag: 34abfb7f4f5610cabb45396e0496472446a0b2ca + tag: 808724ff8a19a33d0ed06f9ef59fbd900b08553c subdir: iohk-monitoring tracer-transformers contra-tracer + plugins/backend-aggregation plugins/backend-ekg + plugins/backend-monitoring + plugins/backend-trace-forwarder + plugins/scribe-systemd source-repository-package type: git location: https://github.com/input-output-hk/cardano-ledger-specs - tag: a3ef848542961079b7cd53d599e5385198a3035c + tag: d5b184a820853c7ba202efd615b8fadca1acb52c subdir: byron/chain/executable-spec byron/crypto @@ -159,19 +188,27 @@ source-repository-package source-repository-package type: git location: https://github.com/input-output-hk/cardano-node.git - tag: b3cabae6b3bf30a0b1b4e78bc4b67282dabad0a6 + tag: ed11e8b6429d4af1cb2539460e5cb2283a06b2dc subdir: cardano-api + cardano-node + cardano-cli + cardano-config + +source-repository-package + type: git + location: https://github.com/input-output-hk/optparse-applicative + tag: 7497a29cb998721a9068d5725d49461f2bba0e7a source-repository-package type: git location: https://github.com/input-output-hk/Win32-network - tag: 94153b676617f8f33abe8d8182c37377d2784bd1 + tag: 3825d3abf75f83f406c1f7161883c438dac7277d source-repository-package type: git location: https://github.com/input-output-hk/hedgehog-extras - tag: 8bcd3c9dc22cc44f9fcfe161f4638a384fc7a187 + tag: edf6945007177a638fbeb8802397f3a6f4e47c14 source-repository-package type: git diff --git a/MetaLamp/nft-marketplace/nix/pkgs/default.nix b/MetaLamp/nft-marketplace/nix/pkgs/default.nix index 98ffcd0e2..70fbab8ae 100644 --- a/MetaLamp/nft-marketplace/nix/pkgs/default.nix +++ b/MetaLamp/nft-marketplace/nix/pkgs/default.nix @@ -11,6 +11,7 @@ let haskell = pkgs.callPackage ./haskell { inherit gitignore-nix sources haskell-nix; inherit compiler-nix-name; # Use the same GHC version as plutus + inherit (pkgs) libsodium-vrf; }; hlint = plutus.plutus.hlint; @@ -35,8 +36,8 @@ let cardano-repo-tool = plutus.plutus.cardano-repo-tool; in -{ - inherit nodejs purs spago purty fix-purty; - inherit haskell hlint cabal-install stylish-haskell fix-stylish-haskell haskell-language-server; +{ + inherit nodejs purs spago purty fix-purty; + inherit haskell hlint cabal-install stylish-haskell fix-stylish-haskell haskell-language-server; inherit cardano-repo-tool; } diff --git a/MetaLamp/nft-marketplace/nix/pkgs/haskell/default.nix b/MetaLamp/nft-marketplace/nix/pkgs/haskell/default.nix index 67036b9d5..aa36c8b87 100644 --- a/MetaLamp/nft-marketplace/nix/pkgs/haskell/default.nix +++ b/MetaLamp/nft-marketplace/nix/pkgs/haskell/default.nix @@ -3,6 +3,7 @@ , gitignore-nix , sources , compiler-nix-name +, libsodium-vrf }: let # The Hackage index-state from cabal.project @@ -21,7 +22,7 @@ let # The haskell project created by haskell-nix.cabalProject' project = import ./haskell.nix { - inherit haskell-nix compiler-nix-name gitignore-nix; + inherit lib haskell-nix compiler-nix-name gitignore-nix libsodium-vrf; }; # All the packages defined by our project, including dependencies diff --git a/MetaLamp/nft-marketplace/nix/pkgs/haskell/haskell.nix b/MetaLamp/nft-marketplace/nix/pkgs/haskell/haskell.nix index 517deaa42..dc1cb6572 100644 --- a/MetaLamp/nft-marketplace/nix/pkgs/haskell/haskell.nix +++ b/MetaLamp/nft-marketplace/nix/pkgs/haskell/haskell.nix @@ -4,6 +4,8 @@ { haskell-nix , gitignore-nix , compiler-nix-name +, lib +, libsodium-vrf }: let @@ -17,35 +19,33 @@ let inherit compiler-nix-name; sha256map = { - "https://github.com/Quid2/flat.git"."95e5d7488451e43062ca84d5376b3adcc465f1cd" = "06l31x3y93rjpryvlxnpsyq2zyxvb0z6lik6yq2fvh36i5zwvwa3"; - "https://github.com/input-output-hk/plutus.git"."plutus-starter-devcontainer/v1.0.6" = "1jzbcsdrv0b43dj7bwbd1fbk71f7gph6zzb8y29n9cn3j8illnyc"; + "https://github.com/input-output-hk/plutus.git"."plutus-starter-devcontainer/v1.0.8" = "0fas8kv57lyrsn3larvbfgif48d506w73y7g3g0mxfilfsl5nyfz"; + "https://github.com/michaelpj/flat.git"."ee59880f47ab835dbd73bea0847dab7869fc20d8" = "1lrzknw765pz2j97nvv9ip3l1mcpf2zr4n56hwlz0rk7wq7ls4cm"; "https://github.com/shmish111/purescript-bridge.git"."6a92d7853ea514be8b70bab5e72077bf5a510596" = "13j64vv116in3c204qsl1v0ajphac9fqvsjp7x3zzfr7n7g61drb"; "https://github.com/shmish111/servant-purescript.git"."a76104490499aa72d40c2790d10e9383e0dbde63" = "11nxxmi5bw66va7psvrgrw7b7n85fvqgfp58yva99w3v9q3a50v9"; - "https://github.com/input-output-hk/cardano-crypto.git"."ce8f1934e4b6252084710975bd9bbc0a4648ece4" = "1v2laq04piyj511b2m77hxjh9l1yd6k9kc7g6bjala4w3zdwa4ni"; - "https://github.com/michaelpj/unlit.git"."9ca1112093c5ffd356fc99c7dafa080e686dd748" = "145sffn8gbdn6xp9q5b75yd3m46ql5bnc02arzmpfs6wgjslfhff"; - "https://github.com/input-output-hk/cardano-base"."a715c7f420770b70bbe95ca51d3dec83866cb1bd" = "06l06mmb8cd4q37bnvfpgx1c5zgsl4xaf106dqva98738i8asj7j"; + "https://github.com/input-output-hk/cardano-base"."cb0f19c85e5bb5299839ad4ed66af6fa61322cc4" = "0dnkfqcvbifbk3m5pg8kyjqjy0zj1l4vd23p39n6ym4q0bnib1cq"; + "https://github.com/input-output-hk/cardano-crypto.git"."07397f0e50da97eaa0575d93bee7ac4b2b2576ec" = "06sdx5ndn2g722jhpicmg96vsrys89fl81k8290b3lr6b1b0w4m3"; + "https://github.com/input-output-hk/cardano-ledger-specs"."d5b184a820853c7ba202efd615b8fadca1acb52c" = "04k5p6qwmfdza65gl5319r1ahdfwjnyqgzpfxdx0x2g5jcbimar4"; "https://github.com/input-output-hk/cardano-prelude"."fd773f7a58412131512b9f694ab95653ac430852" = "02jddik1yw0222wd6q0vv10f7y8rdgrlqaiy83ph002f9kjx7mh6"; - "https://github.com/input-output-hk/ouroboros-network"."e50613562d6d4a0f933741fcf590b0f69a1eda67" = "0i192ksa69lpzjhzmhd2h1mramkvvikw04pqws18h5dly55f4z3k"; - "https://github.com/input-output-hk/iohk-monitoring-framework"."34abfb7f4f5610cabb45396e0496472446a0b2ca" = "1fdc0a02ipa385dnwa6r6jyc8jlg537i12hflfglkhjs2b7i92gs"; - "https://github.com/input-output-hk/cardano-ledger-specs"."a3ef848542961079b7cd53d599e5385198a3035c" = "02iwn2lcfcfvrnvcqnx586ncdnma23vdqvicxgr4f39vcacalzpd"; - "https://github.com/input-output-hk/cardano-node.git"."b3cabae6b3bf30a0b1b4e78bc4b67282dabad0a6" = "1csmji1bgi45wgrw7kqy19s4bbbpa78kjg3bz7mbiwb8vjgg9kvq"; - "https://github.com/input-output-hk/Win32-network"."94153b676617f8f33abe8d8182c37377d2784bd1" = "0pb7bg0936fldaa5r08nqbxvi2g8pcy4w3c7kdcg7pdgmimr30ss"; - "https://github.com/input-output-hk/hedgehog-extras"."8bcd3c9dc22cc44f9fcfe161f4638a384fc7a187" = "12viwpahjdfvlqpnzdgjp40nw31rvyznnab1hml9afpaxd6ixh70"; "https://github.com/input-output-hk/goblins"."cde90a2b27f79187ca8310b6549331e59595e7ba" = "17c88rbva3iw82yg9srlxjv2ia5wjb9cyqw44hik565f5v9svnyg"; + "https://github.com/input-output-hk/iohk-monitoring-framework"."808724ff8a19a33d0ed06f9ef59fbd900b08553c" = "0298dpl29gxzs9as9ha6y0w18hqwc00ipa3hzkxv7nlfrjjz8hmz"; + "https://github.com/input-output-hk/optparse-applicative"."7497a29cb998721a9068d5725d49461f2bba0e7a" = "1gvsrg925vynwgqwplgjmp53vj953qyh3wbdf34pw21c8r47w35r"; + "https://github.com/input-output-hk/ouroboros-network"."877ce057ff6fb086474c8eaad53f2b7f0e0fce6b" = "1kp0qysfy3hl96a3a61rijascq36f1imh3z4jy0vyiygb6qrv47z"; + "https://github.com/input-output-hk/cardano-node.git"."ed11e8b6429d4af1cb2539460e5cb2283a06b2dc" = "1wvr3zzl37i1fn5y9ni027rqw5bhh25z1bacvcaapxxjgdn38lbq"; + "https://github.com/input-output-hk/Win32-network"."3825d3abf75f83f406c1f7161883c438dac7277d" = "19wahfv726fa3mqajpqdqhnl9ica3xmf68i254q45iyjcpj1psqx"; + "https://github.com/input-output-hk/hedgehog-extras"."edf6945007177a638fbeb8802397f3a6f4e47c14" = "0wc7qzkc7j4ns2rz562h6qrx2f8xyq7yjcb7zidnj7f6j0pcd0i9"; }; modules = [ { packages = { - eventful-sql-common = { - # This is needed so evenful-sql-common will build with a newer version of persistent. - ghcOptions = [ "-XDerivingStrategies -XStandaloneDeriving -XUndecidableInstances -XDataKinds -XFlexibleInstances -XMultiParamTypeClasses" ]; - doHaddock = false; - }; - # Broken due to haddock errors. Refer to https://github.com/input-output-hk/plutus/blob/master/nix/pkgs/haskell/haskell.nix plutus-ledger.doHaddock = false; plutus-use-cases.doHaddock = false; + + # See https://github.com/input-output-hk/iohk-nix/pull/488 + cardano-crypto-praos.components.library.pkgconfig = lib.mkForce [ [ libsodium-vrf ] ]; + cardano-crypto-class.components.library.pkgconfig = lib.mkForce [ [ libsodium-vrf ] ]; }; } ]; diff --git a/MetaLamp/nft-marketplace/nix/sources.json b/MetaLamp/nft-marketplace/nix/sources.json index 94979120f..3f326f62d 100644 --- a/MetaLamp/nft-marketplace/nix/sources.json +++ b/MetaLamp/nft-marketplace/nix/sources.json @@ -5,10 +5,10 @@ "homepage": "", "owner": "input-output-hk", "repo": "plutus", - "rev": "plutus-starter-devcontainer/v1.0.6", - "sha256": "1jzbcsdrv0b43dj7bwbd1fbk71f7gph6zzb8y29n9cn3j8illnyc", + "rev": "plutus-starter-devcontainer/v1.0.8", + "sha256": "0fas8kv57lyrsn3larvbfgif48d506w73y7g3g0mxfilfsl5nyfz", "type": "tarball", - "url": "https://github.com/input-output-hk/plutus/archive/plutus-starter-devcontainer/v1.0.6.tar.gz", + "url": "https://github.com/input-output-hk/plutus/archive/plutus-starter-devcontainer/v1.0.8.tar.gz", "url_template": "https://github.com///archive/.tar.gz" } } From 2c17fa937a72eee52869fa4aa46bf7b0d9d565ed Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Thu, 2 Sep 2021 19:02:04 +0700 Subject: [PATCH 074/217] wip fix update backend conflicts --- MetaLamp/nft-marketplace/plutus-starter.cabal | 2 +- .../src/Ext/Plutus/Contracts/Auction.hs | 85 +++++++++---------- .../src/Ext/PlutusTx/AssocMap.hs | 45 ---------- .../src/Plutus/Abstract/ContractResponse.hs | 18 ++-- .../OnChain/Core/StateMachine.hs | 1 - .../Plutus/Contracts/Services/Sale/Core.hs | 5 +- 6 files changed, 53 insertions(+), 103 deletions(-) delete mode 100644 MetaLamp/nft-marketplace/src/Ext/PlutusTx/AssocMap.hs diff --git a/MetaLamp/nft-marketplace/plutus-starter.cabal b/MetaLamp/nft-marketplace/plutus-starter.cabal index c76e3c78a..5c390c3ba 100644 --- a/MetaLamp/nft-marketplace/plutus-starter.cabal +++ b/MetaLamp/nft-marketplace/plutus-starter.cabal @@ -23,7 +23,7 @@ maintainer: Your email library exposed-modules: - Plutus.Contracts.NftMarketplace.OnChain.Core Plutus.Contracts.NftMarketplace.OffChain.ID Plutus.Contracts.NftMarketplace.OnChain.Core.ID Plutus.Contracts.Services.Sale.Core Ext.PlutusTx.AssocMap Plutus.Contracts.NftMarketplace.OnChain.Core.NFT Ext.Plutus.Contracts.Auction Plutus.Contracts.Services.Sale Plutus.Contracts.Services.Sale.Endpoints Plutus.Contracts.Services.Sale.StateMachine Plutus.Contracts.NftMarketplace.OffChain.Info Ext.Plutus.Ledger.Value Plutus.Contracts.NftMarketplace.OffChain.User Plutus.Abstract.ContractResponse Plutus.Contracts.NftMarketplace.Endpoints Plutus.Contracts.NftMarketplace.OffChain.Owner Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine Plutus.PAB.Simulation + Plutus.Contracts.NftMarketplace.OnChain.Core Plutus.Contracts.NftMarketplace.OffChain.ID Plutus.Contracts.NftMarketplace.OnChain.Core.ID Plutus.Contracts.Services.Sale.Core Plutus.Contracts.NftMarketplace.OnChain.Core.NFT Ext.Plutus.Contracts.Auction Plutus.Contracts.Services.Sale Plutus.Contracts.Services.Sale.Endpoints Plutus.Contracts.Services.Sale.StateMachine Plutus.Contracts.NftMarketplace.OffChain.Info Ext.Plutus.Ledger.Value Plutus.Contracts.NftMarketplace.OffChain.User Plutus.Abstract.ContractResponse Plutus.Contracts.NftMarketplace.Endpoints Plutus.Contracts.NftMarketplace.OffChain.Owner Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine Plutus.PAB.Simulation build-depends: base >= 4.9 && < 5, aeson, diff --git a/MetaLamp/nft-marketplace/src/Ext/Plutus/Contracts/Auction.hs b/MetaLamp/nft-marketplace/src/Ext/Plutus/Contracts/Auction.hs index 4e84fd7ff..7fbfa9a0c 100644 --- a/MetaLamp/nft-marketplace/src/Ext/Plutus/Contracts/Auction.hs +++ b/MetaLamp/nft-marketplace/src/Ext/Plutus/Contracts/Auction.hs @@ -32,10 +32,7 @@ import qualified Ledger.Typed.Scripts as Scripts import Ledger.Typed.Tx (TypedScriptTxOut (..)) import Ledger.Value (AssetClass) import Plutus.Contract -import Plutus.Contract.StateMachine (State (..), - StateMachine (..), - StateMachineClient, Void, - WaitingResult (..)) +import Plutus.Contract.StateMachine hiding (mkValidator,typedValidator) import qualified Plutus.Contract.StateMachine as SM import Plutus.Contract.Util (loopM) import qualified Plutus.Contracts.Currency as Currency @@ -48,24 +45,25 @@ data AuctionParams = AuctionParams { apOwner :: PubKeyHash -- ^ Current owner of the asset. This is where the proceeds of the auction will be sent. , apAsset :: Value -- ^ The asset itself. This value is going to be locked by the auction script output. - , apEndTime :: Slot -- ^ When the time window for bidding ends. + , apEndTime :: Ledger.POSIXTime -- ^ When the time window for bidding ends. } deriving stock (Haskell.Eq, Haskell.Show, Generic) deriving anyclass (ToJSON, FromJSON) PlutusTx.makeLift ''AuctionParams + PlutusTx.unstableMakeIsData ''AuctionParams {-# INLINABLE fromTuple #-} -fromTuple :: (AssetClass, PubKeyHash, Value, Slot) -> AuctionParams +fromTuple :: (SM.ThreadToken, PubKeyHash, Value, Ledger.POSIXTime) -> AuctionParams fromTuple (_, apOwner, apAsset, apEndTime) = AuctionParams {..} {-# INLINABLE toTuple #-} -toTuple :: AssetClass -> AuctionParams -> (AssetClass, PubKeyHash, Value, Slot) +toTuple :: SM.ThreadToken -> AuctionParams -> (SM.ThreadToken, PubKeyHash, Value, Ledger.POSIXTime) toTuple threadToken AuctionParams {..} = (threadToken, apOwner, apAsset, apEndTime) {-# INLINABLE getStateToken #-} -getStateToken :: (AssetClass, PubKeyHash, Value, Slot) -> AssetClass +getStateToken :: (SM.ThreadToken, PubKeyHash, Value, Ledger.POSIXTime) -> SM.ThreadToken getStateToken (token, _, _, _) = token data HighestBid = @@ -89,7 +87,7 @@ data AuctionState data AuctionOutput = AuctionOutput { auctionState :: Last AuctionState - , auctionThreadToken :: Last AssetClass + , auctionThreadToken :: Last SM.ThreadToken } deriving stock (Generic, Haskell.Show, Haskell.Eq) deriving anyclass (ToJSON, FromJSON) @@ -100,7 +98,7 @@ deriving via (GenericSemigroupMonoid AuctionOutput) instance (Haskell.Monoid Auc auctionStateOut :: AuctionState -> AuctionOutput auctionStateOut s = Haskell.mempty { auctionState = Last (Just s) } -threadTokenOut :: AssetClass -> AuctionOutput +threadTokenOut :: SM.ThreadToken -> AuctionOutput threadTokenOut t = Haskell.mempty { auctionThreadToken = Last (Just t) } -- | Initial 'AuctionState'. In the beginning the highest bid is 0 and the @@ -120,17 +118,18 @@ data AuctionInput PlutusTx.unstableMakeIsData ''AuctionInput +type AuctionMachine = StateMachine AuctionState AuctionInput + {-# INLINABLE auctionTransition #-} -- | The transitions of the auction state machine. auctionTransition :: AuctionParams -> State AuctionState -> AuctionInput -> Maybe (TxConstraints Void Void, State AuctionState) - auctionTransition AuctionParams{apOwner, apAsset, apEndTime} State{stateData=oldState} input = case (oldState, input) of (Ongoing HighestBid{highestBid, highestBidder}, Bid{newBid, newBidder}) | newBid > highestBid -> -- if the new bid is higher, let constraints = Constraints.mustPayToPubKey highestBidder (Ada.toValue highestBid) -- we pay back the previous highest bid - <> Constraints.mustValidateIn (Interval.to apEndTime) -- but only if we haven't gone past 'apEndTime' + <> Constraints.mustValidateIn (Interval.to $ apEndTime - 1) -- but only if we haven't gone past 'apEndTime' newState = State { stateData = Ongoing HighestBid{highestBid = newBid, highestBidder = newBidder} @@ -140,7 +139,7 @@ auctionTransition AuctionParams{apOwner, apAsset, apEndTime} State{stateData=old (Ongoing h@HighestBid{highestBidder, highestBid}, Payout) -> let constraints = - Constraints.mustValidateIn (Interval.from (apEndTime + 1)) -- When the auction has ended, + Constraints.mustValidateIn (Interval.from apEndTime) -- When the auction has ended, <> Constraints.mustPayToPubKey apOwner (Ada.toValue highestBid) -- the owner receives the payment <> Constraints.mustPayToPubKey highestBidder apAsset -- and the highest bidder the asset newState = State { stateData = Finished h, stateValue = mempty } @@ -152,38 +151,34 @@ auctionTransition AuctionParams{apOwner, apAsset, apEndTime} State{stateData=old {-# INLINABLE auctionStateMachine #-} -auctionStateMachine :: AssetClass -> AuctionParams -> StateMachine AuctionState AuctionInput -auctionStateMachine threadToken auctionParams = SM.mkStateMachine (Just threadToken) (auctionTransition auctionParams) isFinal where +auctionStateMachine :: (ThreadToken, AuctionParams) -> AuctionMachine +auctionStateMachine (threadToken, auctionParams) = SM.mkStateMachine (Just threadToken) (auctionTransition auctionParams) isFinal where isFinal Finished{} = True isFinal _ = False +{-# INLINABLE mkValidator #-} +mkValidator :: (ThreadToken, AuctionParams) -> Scripts.ValidatorType AuctionMachine +mkValidator = SM.mkValidator . auctionStateMachine -- | The script instance of the auction state machine. It contains the state -- machine compiled to a Plutus core validator script. -typedValidator :: AssetClass -> AuctionParams -> Scripts.TypedValidator (StateMachine AuctionState AuctionInput) -typedValidator currency auctionParams = - let val = $$(PlutusTx.compile [|| validatorParam ||]) - `PlutusTx.applyCode` - PlutusTx.liftCode currency - `PlutusTx.applyCode` - PlutusTx.liftCode auctionParams - validatorParam c f = SM.mkValidator (auctionStateMachine c f) - wrap = Scripts.wrapValidator @AuctionState @AuctionInput - - in Scripts.mkTypedValidator @(StateMachine AuctionState AuctionInput) - val - $$(PlutusTx.compile [|| wrap ||]) +typedValidator :: (ThreadToken, AuctionParams) -> Scripts.TypedValidator AuctionMachine +typedValidator = Scripts.mkTypedValidatorParam @AuctionMachine + $$(PlutusTx.compile [|| mkValidator ||]) + $$(PlutusTx.compile [|| wrap ||]) + where + wrap = Scripts.wrapValidator -- | The machine client of the auction state machine. It contains the script instance -- with the on-chain code, and the Haskell definition of the state machine for -- off-chain use. machineClient - :: Scripts.TypedValidator (StateMachine AuctionState AuctionInput) - -> AssetClass -- ^ Thread token of the instance + :: Scripts.TypedValidator AuctionMachine + -> ThreadToken -- ^ Thread token of the instance -> AuctionParams -> StateMachineClient AuctionState AuctionInput machineClient inst threadToken auctionParams = - let machine = auctionStateMachine threadToken auctionParams + let machine = auctionStateMachine (threadToken, auctionParams) in SM.mkStateMachineClient (SM.StateMachineInstance machine inst) type BuyerSchema = Endpoint "bid" Ada @@ -201,7 +196,6 @@ data AuctionLog = data AuctionError = StateMachineContractError SM.SMContractError -- ^ State machine operation failed - | ThreadTokenError Currency.CurrencyError -- ^ Thread token could not be created | AuctionContractError ContractError -- ^ Endpoint, coin selection, etc. failed deriving stock (Haskell.Eq, Haskell.Show, Generic) deriving anyclass (ToJSON, FromJSON) @@ -214,15 +208,14 @@ instance AsContractError AuctionError where instance SM.AsSMContractError AuctionError where _SMContractError = _StateMachineContractError . SM._SMContractError - -- | Client code for the seller -startAuction :: Value -> Slot -> Contract w s AuctionError (AssetClass, AuctionParams) -startAuction value slot = do - threadToken <- mapError ThreadTokenError Currency.createThreadToken +startAuction :: Value -> Ledger.POSIXTime -> Contract w s AuctionError (SM.ThreadToken, AuctionParams) +startAuction value time = do + threadToken <- SM.getThreadToken logInfo $ "Obtained thread token: " <> Haskell.show threadToken self <- Ledger.pubKeyHash <$> ownPubKey - let params = AuctionParams{apOwner = self, apAsset = value, apEndTime = slot } - inst = typedValidator threadToken params + let params = AuctionParams{apOwner = self, apAsset = value, apEndTime = time } + inst = typedValidator (threadToken, params) client = machineClient inst threadToken params _ <- handleError @@ -233,12 +226,12 @@ startAuction value slot = do pure (threadToken, params) -- | Client code for the seller -payoutAuction :: AssetClass -> AuctionParams -> Contract w s AuctionError () +payoutAuction :: SM.ThreadToken -> AuctionParams -> Contract w s AuctionError () payoutAuction threadToken params = do - let inst = typedValidator threadToken params + let inst = typedValidator (threadToken, params) client = machineClient inst threadToken params - _ <- awaitSlot $ apEndTime params + _ <- awaitTime $ apEndTime params r <- SM.runStep client Payout case r of @@ -247,20 +240,20 @@ payoutAuction threadToken params = do SM.TransitionSuccess s -> logWarn ("Unexpected state after Payout transition: " <> Haskell.show s) -- | Get the current state of the contract and log it. -currentState :: AssetClass -> AuctionParams -> Contract w s AuctionError (Maybe AuctionState) +currentState :: SM.ThreadToken -> AuctionParams -> Contract w s AuctionError (Maybe AuctionState) currentState threadToken params = mapError StateMachineContractError (SM.getOnChainState client) >>= \case - Just ((TypedScriptTxOut{tyTxOutData = s}, _), _) -> do + Just (SM.OnChainState{SM.ocsTxOut=TypedScriptTxOut{tyTxOutData= s}}, _) -> do pure (Just s) _ -> do logWarn CurrentStateNotFound pure Nothing where - inst = typedValidator threadToken params + inst = typedValidator (threadToken, params) client = machineClient inst threadToken params -submitBid :: AssetClass -> AuctionParams -> Ada -> Contract w s AuctionError () +submitBid :: SM.ThreadToken -> AuctionParams -> Ada -> Contract w s AuctionError () submitBid threadToken params ada = do - let inst = typedValidator threadToken params + let inst = typedValidator (threadToken, params) client = machineClient inst threadToken params self <- Ledger.pubKeyHash <$> ownPubKey let bid = Bid{newBid = ada, newBidder = self} diff --git a/MetaLamp/nft-marketplace/src/Ext/PlutusTx/AssocMap.hs b/MetaLamp/nft-marketplace/src/Ext/PlutusTx/AssocMap.hs deleted file mode 100644 index 36914ae50..000000000 --- a/MetaLamp/nft-marketplace/src/Ext/PlutusTx/AssocMap.hs +++ /dev/null @@ -1,45 +0,0 @@ -{-# LANGUAGE DerivingStrategies #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE MonoLocalBinds #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE NoImplicitPrelude #-} -{-# LANGUAGE RankNTypes #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE UndecidableInstances #-} -{-# OPTIONS_GHC -Wno-name-shadowing #-} -{-# OPTIONS_GHC -fno-strictness #-} -{-# OPTIONS_GHC -fno-specialise #-} -{-# OPTIONS_GHC -fno-omit-interface-pragmas #-} - -module Ext.PlutusTx.AssocMap where - -import GHC.Generics (Generic) -import PlutusTx.AssocMap -import PlutusTx.IsData -import PlutusTx.Lift (makeLift) -import PlutusTx.Prelude hiding (all, null, toList) -import qualified PlutusTx.Prelude as P -import PlutusTx.These -import qualified Prelude as Haskell - -{-# INLINABLE unionWith #-} --- | Combine two 'Map's with the given combination function. -unionWith :: - forall k a. (Eq k) - => (a -> a -> a) - -> Map k a - -> Map k a - -> Map k a -unionWith merge mls mrs = - let ls = toList mls - rs = toList mrs - f :: a -> Maybe a -> a - f a b' = - case b' of - Nothing -> a - Just b -> merge a b - ls' :: [(k, a)] - ls' = P.fmap (\(c, i) -> (c, f i (lookup c (fromList rs)))) ls - rs' :: [(k, a)] - rs' = filter (\(c, _) -> not (any (\(c', _) -> c' == c) ls)) rs - in fromList (ls' ++ rs') diff --git a/MetaLamp/nft-marketplace/src/Plutus/Abstract/ContractResponse.hs b/MetaLamp/nft-marketplace/src/Plutus/Abstract/ContractResponse.hs index ef2ec572d..307f90265 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Abstract/ContractResponse.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Abstract/ContractResponse.hs @@ -11,6 +11,7 @@ {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE LambdaCase #-} module Plutus.Abstract.ContractResponse where @@ -63,15 +64,16 @@ withContractResponse :: forall l a p r s. => Proxy l -> (a -> r) -> (p -> Contract (ContractResponse Text r) s Text a) - -> Contract (ContractResponse Text r) s Void () + -> Promise (ContractResponse Text r) s Void () withContractResponse _ g c = do - e <- runError $ do - p <- endpoint @l - _ <- tell CrPending - errorHandler `handleError` c p - tell $ case e of - Left err -> CrError err - Right a -> CrSuccess $ g a + handleEndpoint @l $ \case + Left err -> tell $ CrError err + Right p -> do + _ <- tell CrPending + e <- runError $ errorHandler `handleError` c p + tell $ case e of + Left err -> CrError err + Right a -> CrSuccess $ g a errorHandler :: Text -> Contract w s Text b errorHandler e = do diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs index 2bb405506..64c7d1fa0 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs @@ -18,7 +18,6 @@ import qualified Control.Lens as Lens import qualified Data.Aeson as J import qualified Data.Text as T import qualified Ext.Plutus.Contracts.Auction as Auction -import qualified Ext.PlutusTx.AssocMap as AssocMap import qualified GHC.Generics as Haskell import Ledger import qualified Ledger.Constraints as Constraints diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/Core.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/Core.hs index f83c19fef..c84ed686f 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/Core.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/Core.hs @@ -14,6 +14,7 @@ {-# OPTIONS_GHC -fno-ignore-interface-pragmas #-} {-# OPTIONS_GHC -fno-omit-interface-pragmas #-} {-# OPTIONS_GHC -fobject-code #-} +{-# LANGUAGE StandaloneDeriving #-} module Plutus.Contracts.Services.Sale.Core where @@ -41,12 +42,12 @@ type LovelacePrice = Integer data Sale = Sale - { saleProtocolToken :: !AssetClass, + { saleProtocolToken :: !ThreadToken, salePrice :: !LovelacePrice, saleValue :: !Value } deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) - deriving anyclass (J.ToJSON, J.FromJSON, Schema.ToSchema) + deriving anyclass (J.ToJSON, J.FromJSON) PlutusTx.unstableMakeIsData ''Sale From 43d7bffe081979aaee9ae002fa9a4593ec32714e Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Fri, 3 Sep 2021 14:03:53 +0700 Subject: [PATCH 075/217] fix lib errors --- MetaLamp/nft-marketplace/plutus-starter.cabal | 1 + .../src/Plutus/Abstract/ContractResponse.hs | 1 - .../Contracts/NftMarketplace/OffChain/Info.hs | 10 +++--- .../NftMarketplace/OffChain/Owner.hs | 21 ++++-------- .../Contracts/NftMarketplace/OffChain/User.hs | 34 +++++++++++-------- .../Contracts/NftMarketplace/OnChain/Core.hs | 8 ++--- .../NftMarketplace/OnChain/Core/NFT.hs | 18 +++++----- .../OnChain/Core/StateMachine.hs | 6 ++-- .../Contracts/Services/Sale/Endpoints.hs | 6 ++-- .../Contracts/Services/Sale/StateMachine.hs | 5 +-- .../src/Plutus/PAB/Simulation.hs | 33 +++++++----------- .../test/Marketplace/Fixtures/NFT.hs | 14 ++++---- 12 files changed, 70 insertions(+), 87 deletions(-) diff --git a/MetaLamp/nft-marketplace/plutus-starter.cabal b/MetaLamp/nft-marketplace/plutus-starter.cabal index 5c390c3ba..7d7513d75 100644 --- a/MetaLamp/nft-marketplace/plutus-starter.cabal +++ b/MetaLamp/nft-marketplace/plutus-starter.cabal @@ -37,6 +37,7 @@ library semigroups, cryptonite, memory, + data-default, -- Plutus: playground-common, plutus-contract, diff --git a/MetaLamp/nft-marketplace/src/Plutus/Abstract/ContractResponse.hs b/MetaLamp/nft-marketplace/src/Plutus/Abstract/ContractResponse.hs index 307f90265..a6a7ce136 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Abstract/ContractResponse.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Abstract/ContractResponse.hs @@ -17,7 +17,6 @@ module Plutus.Abstract.ContractResponse where import qualified Control.Lens as Lens import Control.Monad hiding (fmap) -import qualified Data.ByteString as BS import qualified Data.Map as Map import Data.Monoid (Last (..)) import Data.Proxy (Proxy (..)) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs index 2fa22d1a7..89fd602a4 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs @@ -51,7 +51,7 @@ marketplaceStore marketplace = do getStateDatum :: Maybe (OnChainState Core.MarketplaceDatum i, UtxoMap) -> Contract w s Text Core.MarketplaceDatum -getStateDatum = maybe (throwError "Marketplace output not found") (pure . tyTxOutData . fst . fst) +getStateDatum = maybe (throwError "Marketplace output not found") (pure . tyTxOutData . ocsTxOut . fst) getNftEntry :: Core.MarketplaceDatum -> Core.InternalNftId -> Contract w s Text Core.NFT getNftEntry nftStore (Core.InternalNftId ipfsCidHash ipfsCid) = @@ -114,9 +114,9 @@ data InfoContractState = Lens.makeClassyPrisms ''InfoContractState -infoEndpoints :: Core.Marketplace -> Contract (ContractResponse Text InfoContractState) MarketplaceInfoSchema Void () -infoEndpoints marketplace = forever $ - withContractResponse (Proxy @"fundsAt") FundsAt fundsAt +infoEndpoints :: Core.Marketplace -> Promise (ContractResponse Text InfoContractState) MarketplaceInfoSchema Void () +infoEndpoints marketplace = + (withContractResponse (Proxy @"fundsAt") FundsAt fundsAt `select` withContractResponse (Proxy @"marketplaceFunds") MarketplaceFunds (const $ marketplaceFunds marketplace) `select` withContractResponse (Proxy @"marketplaceStore") MarketplaceStore (const $ marketplaceStore marketplace) - `select` withContractResponse (Proxy @"getAuctionState") AuctionState (getAuctionState marketplace) + `select` withContractResponse (Proxy @"getAuctionState") AuctionState (getAuctionState marketplace)) <> infoEndpoints marketplace diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Owner.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Owner.hs index bf9ce106d..14939c0ac 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Owner.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Owner.hs @@ -34,18 +34,11 @@ import qualified Prelude as Haskell import Text.Printf (printf) -- | Starts the NFT Marketplace protocol: minting protocol NFT, creating empty nft storage -start :: () -> Contract w s Text Core.Marketplace -start () = start' $ do +start :: Contract w s Text Core.Marketplace +start = do + marketplaceToken <- mapError (T.pack . Haskell.show @SMContractError) $ getThreadToken pkh <- pubKeyHash <$> ownPubKey - fmap Currency.currencySymbol $ - mapError (T.pack . Haskell.show @Currency.CurrencyError) $ - Currency.forgeContract pkh [(Core.marketplaceProtocolName, 1)] - -start' :: Contract w s Text CurrencySymbol -> Contract w s Text Core.Marketplace -start' getMarketplaceToken = do - marketplaceToken <- getMarketplaceToken - pkh <- pubKeyHash <$> ownPubKey - let marketplace = Core.marketplace marketplaceToken + let marketplace = Core.Marketplace marketplaceToken let client = Core.marketplaceClient marketplace void $ mapError (T.pack . Haskell.show @SMContractError) $ runInitialise client (Core.MarketplaceDatum AssocMap.empty AssocMap.empty) mempty @@ -56,10 +49,10 @@ type MarketplaceOwnerSchema = Endpoint "start" () data OwnerContractState = Started Core.Marketplace - deriving stock (Haskell.Eq, Haskell.Ord, Haskell.Show, Haskell.Generic) + deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON) Lens.makeClassyPrisms ''OwnerContractState -ownerEndpoints :: Contract (ContractResponse Text OwnerContractState) MarketplaceOwnerSchema Void () -ownerEndpoints = forever $ withContractResponse (Proxy @"start") Started start +ownerEndpoints :: Promise (ContractResponse Text OwnerContractState) MarketplaceOwnerSchema Void () +ownerEndpoints = withContractResponse (Proxy @"start") Started (const start) <> ownerEndpoints diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs index a36b2c343..d5433f346 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs @@ -1,3 +1,5 @@ +{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-} @@ -51,9 +53,9 @@ getOwnPubKey = pubKeyHash <$> ownPubKey data CreateNftParams = CreateNftParams { - cnpIpfsCid :: ByteString, - cnpNftName :: ByteString, - cnpNftDescription :: ByteString, + cnpIpfsCid :: BuiltinByteString, + cnpNftName :: BuiltinByteString, + cnpNftDescription :: BuiltinByteString, cnpNftCategory :: Core.Category, cnpRevealIssuer :: Bool } @@ -76,7 +78,7 @@ createNft marketplace CreateNftParams {..} = do let tokenName = V.TokenName cnpIpfsCid nft <- mapError (T.pack . Haskell.show @Currency.CurrencyError) $ - Currency.forgeContract pkh [(tokenName, 1)] + Currency.mintContract pkh [(tokenName, 1)] let client = Core.marketplaceClient marketplace let nftEntry = Core.NftInfo @@ -186,10 +188,12 @@ closeSale marketplace CloseLotParams {..} = do logInfo @Haskell.String $ printf "Closed lot sale %s" (Haskell.show sale) pure () +deriving newtype instance Schema.ToSchema DiffMilliSeconds + data StartAnAuctionParams = StartAnAuctionParams { saapItemId :: UserItemId, - saapDuration :: Slot + saapDuration :: DiffMilliSeconds } deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON, Schema.ToSchema) @@ -209,8 +213,8 @@ startAnAuction marketplace StartAnAuctionParams {..} = do Right bundleId@(Core.InternalBundleId bundleHash cids) -> Core.bundleValue cids <$> getBundleEntry nftStore bundleId - currSlot <- currentSlot - let endTime = currSlot + saapDuration + currTime <- currentTime + let endTime = currTime + fromMilliSeconds saapDuration (auctionToken, auctionParams) <- mapError (T.pack . Haskell.show) $ Auction.startAuction auctionValue endTime let client = Core.marketplaceClient marketplace @@ -282,9 +286,9 @@ bidOnAuction marketplace BidOnAuctionParams {..} = do data BundleUpParams = BundleUpParams { - bupIpfsCids :: [ByteString], - bupName :: ByteString, - bupDescription :: ByteString, + bupIpfsCids :: [BuiltinByteString], + bupName :: BuiltinByteString, + bupDescription :: BuiltinByteString, bupCategory :: Core.Category } deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) @@ -315,7 +319,7 @@ bundleUp marketplace BundleUpParams {..} = do data UnbundleParams = UnbundleParams { - upIpfsCids :: [ByteString] + upIpfsCids :: [BuiltinByteString] } deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON, Schema.ToSchema) @@ -373,9 +377,9 @@ data UserContractState = Lens.makeClassyPrisms ''UserContractState -userEndpoints :: Core.Marketplace -> Contract (ContractResponse Text UserContractState) MarketplaceUserSchema Void () -userEndpoints marketplace = forever $ - withContractResponse (Proxy @"createNft") (const NftCreated) (createNft marketplace) +userEndpoints :: Core.Marketplace -> Promise (ContractResponse Text UserContractState) MarketplaceUserSchema Void () +userEndpoints marketplace = + (withContractResponse (Proxy @"createNft") (const NftCreated) (createNft marketplace) `select` withContractResponse (Proxy @"openSale") (const OpenedSale) (openSale marketplace) `select` withContractResponse (Proxy @"buyItem") (const NftBought) (buyItem marketplace) `select` withContractResponse (Proxy @"closeSale") (const ClosedSale) (closeSale marketplace) @@ -385,4 +389,4 @@ userEndpoints marketplace = forever $ `select` withContractResponse (Proxy @"bundleUp") (const Bundled) (bundleUp marketplace) `select` withContractResponse (Proxy @"unbundle") (const Unbundled) (unbundle marketplace) `select` withContractResponse (Proxy @"ownPubKey") GetPubKey (const getOwnPubKey) - `select` withContractResponse (Proxy @"ownPubKeyBalance") GetPubKeyBalance (const ownPubKeyBalance) + `select` withContractResponse (Proxy @"ownPubKeyBalance") GetPubKeyBalance (const ownPubKeyBalance)) <> userEndpoints marketplace diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core.hs index 1a32e686f..4f400f25d 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core.hs @@ -24,11 +24,9 @@ import PlutusTx.Prelude hidin import Prelude (Semigroup (..)) import qualified Prelude as Haskell -marketplaceProtocolName :: TokenName -marketplaceProtocolName = "NFT Marketplace" - -marketplace :: CurrencySymbol -> Marketplace -marketplace protocol = Marketplace (assetClass protocol marketplaceProtocolName) +-- TODO! +-- marketplace :: CurrencySymbol -> Marketplace +-- marketplace protocol = Marketplace (assetClass protocol marketplaceProtocolName) marketplaceValidator :: Marketplace -> Validator marketplaceValidator = Scripts.validatorScript . marketplaceInst diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs index 05de56a00..909f20c34 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/NFT.hs @@ -39,18 +39,18 @@ import Prelude (Semigroup (..)) import qualified Prelude as Haskell -- TODO (?) add tags -type IpfsCid = ByteString -type IpfsCidHash = ByteString -type Auction = (AssetClass, PubKeyHash, Value, Slot) -type Category = [ByteString] +type IpfsCid = BuiltinByteString +type IpfsCidHash = BuiltinByteString +type Auction = (ThreadToken, PubKeyHash, Value, POSIXTime) +type Category = [BuiltinByteString] type LotLink = Either Sale.Sale Auction -type BundleId = ByteString +type BundleId = BuiltinByteString data NftInfo = NftInfo { niCurrency :: !CurrencySymbol - , niName :: !ByteString - , niDescription :: !ByteString + , niName :: !BuiltinByteString + , niDescription :: !BuiltinByteString , niCategory :: !Category , niIssuer :: !(Maybe PubKeyHash) } @@ -91,8 +91,8 @@ Lens.makeClassyPrisms ''Bundle data BundleInfo = BundleInfo - { biName :: !ByteString - , biDescription :: !ByteString + { biName :: !BuiltinByteString + , biDescription :: !BuiltinByteString , biCategory :: !Category } deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs index 64c7d1fa0..0f3971edb 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs @@ -37,9 +37,9 @@ import qualified Prelude as Haskell newtype Marketplace = Marketplace - { marketplaceProtocolToken :: AssetClass + { marketplaceProtocolToken :: ThreadToken } - deriving stock (Haskell.Eq, Haskell.Ord, Haskell.Show, Haskell.Generic) + deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON) PlutusTx.makeLift ''Marketplace @@ -176,7 +176,7 @@ transition marketplace state redeemer = case redeemer of _ -> trace "Invalid transition" Nothing where stateToken :: Value - stateToken = V.assetClassValue (marketplaceProtocolToken marketplace) 1 + stateToken = mempty -- TODO! V.assetClassValue (marketplaceProtocolToken marketplace) 1 nftStore :: MarketplaceDatum nftStore = stateData state diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/Endpoints.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/Endpoints.hs index 81ba77e6f..12a5af156 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/Endpoints.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/Endpoints.hs @@ -53,11 +53,9 @@ PlutusTx.makeLift ''OpenSaleParams openSale :: OpenSaleParams -> Contract w s Text Core.Sale openSale OpenSaleParams {..} = do pkh <- getOwnPubKey - saleCurrency <- fmap Currency.currencySymbol $ - mapError (T.pack . Haskell.show @Currency.CurrencyError) $ - Currency.forgeContract pkh [(Core.saleProtocolName, 1)] + saleToken <- mapError (T.pack . Haskell.show @SMContractError) $ getThreadToken let sale = Core.Sale - { saleProtocolToken = assetClass saleCurrency Core.saleProtocolName, + { saleProtocolToken = saleToken, salePrice = ospSalePrice, saleValue = ospSaleValue } diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/StateMachine.hs index 8154aaae2..bfb7663d2 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/StateMachine.hs @@ -66,7 +66,7 @@ transition Sale{..} state redeemer = case (stateData state, redeemer) of _ -> Nothing where stateToken :: Value - stateToken = assetClassValue saleProtocolToken 1 + stateToken = mempty -- TODO! assetClassValue saleProtocolToken 1 val = stateValue state @@ -101,9 +101,6 @@ saleInst sale = Scripts.mkTypedValidator @SaleScript saleClient :: Sale -> StateMachineClient SaleDatum SaleRedeemer saleClient sale = mkStateMachineClient $ StateMachineInstance (saleStateMachine sale) (saleInst sale) -saleProtocolName :: TokenName -saleProtocolName = "Sale" - saleValidator :: Sale -> Validator saleValidator = Scripts.validatorScript . saleInst diff --git a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs index 887fc2e68..e95b7ddd9 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs @@ -25,7 +25,6 @@ import Data.Aeson (FromJSON, Result (..), ToJSON, encode, fromJSON) -import qualified Data.ByteString as BS import qualified Data.Map.Strict as Map import qualified Data.Monoid as Monoid import qualified Data.Semigroup as Semigroup @@ -59,12 +58,11 @@ import Plutus.PAB.Simulator (Simulation, import qualified Plutus.PAB.Simulator as Simulator import Plutus.PAB.Types (PABError (..)) import qualified Plutus.PAB.Webserver.Server as PAB.Server -import Plutus.V1.Ledger.Crypto (getPubKeyHash, - pubKeyHash) import Prelude hiding (init) import Wallet.Emulator.Types (Wallet (..), walletPubKey) import Wallet.Types (ContractInstanceId) +import Data.Default (Default (def)) ownerWallet :: Wallet ownerWallet = Wallet 1 @@ -277,26 +275,21 @@ data MarketplaceContracts = instance Pretty MarketplaceContracts where pretty = viaShow -handleMarketplaceContract :: - ( Member (Error PABError) effs - , Member (LogMsg (PABMultiAgentMsg (Builtin MarketplaceContracts))) effs - ) - => ContractEffect (Builtin MarketplaceContracts) - ~> Eff effs -handleMarketplaceContract = Builtin.handleBuiltin getSchema getContract where - getSchema = \case - MarketplaceUser _ -> Builtin.endpointsToSchemas @Marketplace.MarketplaceUserSchema - MarketplaceInfo _ -> Builtin.endpointsToSchemas @Marketplace.MarketplaceInfoSchema - MarketplaceStart -> Builtin.endpointsToSchemas @Marketplace.MarketplaceOwnerSchema - getContract = \case - MarketplaceInfo marketplace -> SomeBuiltin $ Marketplace.infoEndpoints marketplace - MarketplaceUser marketplace -> SomeBuiltin $ Marketplace.userEndpoints marketplace - MarketplaceStart -> SomeBuiltin Marketplace.ownerEndpoints +instance Builtin.HasDefinitions MarketplaceContracts where + getDefinitions = [MarketplaceStart] + getSchema = \case + MarketplaceUser _ -> Builtin.endpointsToSchemas @Marketplace.MarketplaceUserSchema + MarketplaceInfo _ -> Builtin.endpointsToSchemas @Marketplace.MarketplaceInfoSchema + MarketplaceStart -> Builtin.endpointsToSchemas @Marketplace.MarketplaceOwnerSchema + getContract = \case + MarketplaceInfo marketplace -> SomeBuiltin . awaitPromise $ Marketplace.infoEndpoints marketplace + MarketplaceUser marketplace -> SomeBuiltin . awaitPromise $ Marketplace.userEndpoints marketplace + MarketplaceStart -> SomeBuiltin . awaitPromise $ Marketplace.ownerEndpoints handlers :: SimulatorEffectHandlers (Builtin MarketplaceContracts) handlers = - Simulator.mkSimulatorHandlers @(Builtin MarketplaceContracts) [] - $ interpret handleMarketplaceContract + Simulator.mkSimulatorHandlers def def + $ interpret (Builtin.contractHandler (Builtin.handleBuiltin @MarketplaceContracts)) oneAdaInLovelace :: Integer oneAdaInLovelace = 1000000 diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/NFT.hs b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/NFT.hs index c603fc0fd..cf56f2f70 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/NFT.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/NFT.hs @@ -7,7 +7,7 @@ module Marketplace.Fixtures.NFT where import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace import PlutusTx.Builtins (sha2_256) -import PlutusTx.Prelude (ByteString) +import PlutusTx.Prelude (BuiltinByteString) cids :: [Marketplace.IpfsCid] cids = [catTokenIpfsCid, photoTokenIpfsCid] @@ -22,10 +22,10 @@ bundleInfo = Marketplace.BundleInfo , biCategory = bundleCategory } -bundleName :: ByteString +bundleName :: BuiltinByteString bundleName = "Picture gallery" -bundleDescription :: ByteString +bundleDescription :: BuiltinByteString bundleDescription = "Collection of visual media" bundleCategory :: Marketplace.Category @@ -37,10 +37,10 @@ catTokenIpfsCid = "QmPeoJnaDttpFrSySYBY3reRFCzL3qv4Uiqz376EBv9W16" catTokenIpfsCidHash :: Marketplace.IpfsCidHash catTokenIpfsCidHash = sha2_256 catTokenIpfsCid -catTokenName :: ByteString +catTokenName :: BuiltinByteString catTokenName = "Cat token" -catTokenDescription :: ByteString +catTokenDescription :: BuiltinByteString catTokenDescription = "A picture of a cat on a pogo stick" catTokenCategory :: Marketplace.Category @@ -58,10 +58,10 @@ photoTokenIpfsCid = "QmeSFBsEZ7XtK7yv5CQ79tqFnH9V2jhFhSSq1LV5W3kuiB" photoTokenIpfsCidHash :: Marketplace.IpfsCidHash photoTokenIpfsCidHash = sha2_256 photoTokenIpfsCid -photoTokenName :: ByteString +photoTokenName :: BuiltinByteString photoTokenName = "Photo token" -photoTokenDescription :: ByteString +photoTokenDescription :: BuiltinByteString photoTokenDescription = "A picture of a sunset" photoTokenCategory :: Marketplace.Category From 489f2ab0305ec2bfd40ba32b390299424e2a1ee0 Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Fri, 3 Sep 2021 14:18:13 +0700 Subject: [PATCH 076/217] add cabal common options --- MetaLamp/nft-marketplace/default.nix | 2 +- MetaLamp/nft-marketplace/plutus-starter.cabal | 32 +++++++++++++------ MetaLamp/nft-marketplace/shell.nix | 3 +- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/MetaLamp/nft-marketplace/default.nix b/MetaLamp/nft-marketplace/default.nix index 6c0824149..d8104d849 100644 --- a/MetaLamp/nft-marketplace/default.nix +++ b/MetaLamp/nft-marketplace/default.nix @@ -22,7 +22,7 @@ let # haskell-language-server # } # } - + packages = import ./nix; inherit (packages) pkgs plutus-starter; diff --git a/MetaLamp/nft-marketplace/plutus-starter.cabal b/MetaLamp/nft-marketplace/plutus-starter.cabal index 7d7513d75..7e75330d9 100644 --- a/MetaLamp/nft-marketplace/plutus-starter.cabal +++ b/MetaLamp/nft-marketplace/plutus-starter.cabal @@ -21,7 +21,25 @@ maintainer: Your email -- category: -- extra-source-files: CHANGELOG.md +flag defer-plugin-errors + description: + Defer errors from the plugin, useful for things like Haddock that can't handle it. + default: False + manual: True + +common lang + default-language: Haskell2010 + ghc-options: + -Wall -Wnoncanonical-monad-instances + -Wincomplete-uni-patterns -Wincomplete-record-updates + -Wredundant-constraints -Widentities -rtsopts + -- See Plutus Tx readme + -fobject-code -fno-ignore-interface-pragmas -fno-omit-interface-pragmas + if flag(defer-plugin-errors) + ghc-options: -fplugin-opt PlutusTx.Plugin:defer-errors + library + import: lang exposed-modules: Plutus.Contracts.NftMarketplace.OnChain.Core Plutus.Contracts.NftMarketplace.OffChain.ID Plutus.Contracts.NftMarketplace.OnChain.Core.ID Plutus.Contracts.Services.Sale.Core Plutus.Contracts.NftMarketplace.OnChain.Core.NFT Ext.Plutus.Contracts.Auction Plutus.Contracts.Services.Sale Plutus.Contracts.Services.Sale.Endpoints Plutus.Contracts.Services.Sale.StateMachine Plutus.Contracts.NftMarketplace.OffChain.Info Ext.Plutus.Ledger.Value Plutus.Contracts.NftMarketplace.OffChain.User Plutus.Abstract.ContractResponse Plutus.Contracts.NftMarketplace.Endpoints Plutus.Contracts.NftMarketplace.OffChain.Owner Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine Plutus.PAB.Simulation build-depends: @@ -48,12 +66,9 @@ library plutus-use-cases, plutus-pab hs-source-dirs: src - default-language: Haskell2010 - ghc-options: - -- See Plutus Tx readme - -fobject-code -fno-ignore-interface-pragmas -fno-omit-interface-pragmas executable pab + import: lang main-is: Main.hs hs-source-dirs: pab ghc-options: @@ -63,6 +78,7 @@ executable pab plutus-starter executable pab-simulation + import: lang main-is: Main.hs hs-source-dirs: pab-simulation ghc-options: @@ -72,6 +88,7 @@ executable pab-simulation plutus-starter executable generate-purs + import: lang main-is: Main.hs hs-source-dirs: generate-purs other-modules: MarketplaceTypes @@ -102,17 +119,12 @@ executable generate-purs plutus-tx test-suite test + import: lang type: exitcode-stdio-1.0 main-is: Main.hs hs-source-dirs: test other-modules: Marketplace.Spec.Start Utils.Data Marketplace.Spec.Auction Marketplace.Fixtures.NFT Marketplace.Spec.Bundles Marketplace.Spec.Sale Utils.Trace Utils Marketplace.Fixtures.Wallet Marketplace.Fixtures Marketplace.Fixtures.CheckOptions Marketplace.Fixtures.Script Marketplace.Spec.CreateNft - default-language: Haskell2010 - ghc-options: -Wall -Wnoncanonical-monad-instances - -Wincomplete-uni-patterns -Wincomplete-record-updates - -Wredundant-constraints -Widentities -rtsopts - -- See Plutus Tx readme - -fobject-code -fno-ignore-interface-pragmas -fno-omit-interface-pragmas build-depends: plutus-core -any, plutus-tx -any, diff --git a/MetaLamp/nft-marketplace/shell.nix b/MetaLamp/nft-marketplace/shell.nix index 69f0ba40d..91af39c0f 100644 --- a/MetaLamp/nft-marketplace/shell.nix +++ b/MetaLamp/nft-marketplace/shell.nix @@ -15,10 +15,11 @@ in spago purty fix-purty + fix-stylish-haskell haskell-language-server stylish-haskell - fix-stylish-haskell pkgs.niv cardano-repo-tool + pkgs.ghcid ]; } From 3d89b68c7785fc31d11fe44e1cff94dba6547fa9 Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Fri, 3 Sep 2021 15:47:49 +0700 Subject: [PATCH 077/217] fix purs generation --- .../nft-marketplace/generate-purs/Main.hs | 140 ++---------------- .../generate-purs/MarketplaceTypes.hs | 3 - MetaLamp/nft-marketplace/plutus-starter.cabal | 21 +-- 3 files changed, 17 insertions(+), 147 deletions(-) diff --git a/MetaLamp/nft-marketplace/generate-purs/Main.hs b/MetaLamp/nft-marketplace/generate-purs/Main.hs index 5aa41d505..1adf87ad9 100644 --- a/MetaLamp/nft-marketplace/generate-purs/Main.hs +++ b/MetaLamp/nft-marketplace/generate-purs/Main.hs @@ -11,94 +11,21 @@ module Main where -import MarketplaceTypes (marketplaceTypes, - ratioBridge) -import Cardano.Metadata.Types (AnnotatedSignature, - HashFunction, - Property, - PropertyKey, - Subject, - SubjectProperties) -import Cardano.Wallet.Types (WalletInfo) +import qualified Plutus.PAB.Run.PSGenerator as PAB +import MarketplaceTypes +import Language.PureScript.Bridge import Control.Applicative ((<|>)) +import Data.Proxy +import Servant.PureScript (HasBridge(..),apiModuleName, defaultSettings, Settings, _generateSubscriberAPI) import Control.Lens (set, view, (&)) -import Control.Monad (when) -import Control.Monad.Freer.Extras.Log (LogLevel, - LogMessage) -import Data.Proxy (Proxy (Proxy)) -import qualified Data.Text as Text -import Language.PureScript.Bridge (BridgePart, - Language (Haskell), - SumType, - TypeInfo (TypeInfo), - buildBridge, equal, - genericShow, - haskType, - mkSumType, order, - typeModule, - typeName, - writePSTypesWith, - (^==)) -import Language.PureScript.Bridge.CodeGenSwitches (ForeignOptions (ForeignOptions), - genForeign, - unwrapSingleConstructors) -import Language.PureScript.Bridge.TypeParameters (A) -import qualified PSGenerator.Common -import Plutus.Contract.Checkpoint (CheckpointKey, - CheckpointStore, - CheckpointStoreItem) -import Plutus.Contract.Effects (TxConfirmed) -import Plutus.Contract.Resumable (Responses) -import Plutus.PAB.Effects.Contract.ContractExe (ContractExe) -import Plutus.PAB.Events.ContractInstanceState (PartiallyDecodedResponse) -import qualified Plutus.PAB.Webserver.API as API -import Plutus.PAB.Webserver.Types (ChainReport, - CombinedWSStreamToClient, - CombinedWSStreamToServer, - ContractActivationArgs, - ContractInstanceClientState, - ContractReport, - ContractSignatureResponse, - FullReport, - InstanceStatusToClient) -import Plutus.V1.Ledger.Value (AssetClass, - TokenName (..)) -import Servant ((:<|>)) -import Servant.PureScript (HasBridge, - Settings, - _generateSubscriberAPI, - apiModuleName, - defaultBridge, - defaultSettings, - languageBridge, - writeAPIModuleWithSettings) import System.Directory (doesDirectoryExist, removeDirectoryRecursive) -import Wallet.Emulator.Wallet (Wallet (..)) +import Control.Monad (when) myBridge :: BridgePart myBridge = - PSGenerator.Common.aesonBridge <|> - PSGenerator.Common.containersBridge <|> - PSGenerator.Common.languageBridge <|> - PSGenerator.Common.ledgerBridge <|> - PSGenerator.Common.servantBridge <|> - PSGenerator.Common.miscBridge <|> - ratioBridge <|> - metadataBridge <|> - defaultBridge - --- Some of the metadata types have a datakind type parameter that --- PureScript won't support, so we must drop it. -metadataBridge :: BridgePart -metadataBridge = do - (typeName ^== "Property") - <|> (typeName ^== "SubjectProperties") - <|> (typeName ^== "AnnotatedSignature") - typeModule ^== "Cardano.Metadata.Types" - moduleName <- view (haskType . typeModule) - name <- view (haskType . typeName) - pure $ TypeInfo "plutus-pab" moduleName name [] + PAB.pabBridge <|> + ratioBridge data MyBridge @@ -110,64 +37,23 @@ instance HasBridge MyBridge where myTypes :: [SumType 'Haskell] myTypes = - marketplaceTypes <> - PSGenerator.Common.ledgerTypes <> - PSGenerator.Common.playgroundTypes <> - PSGenerator.Common.walletTypes <> - [ (equal <*> (genericShow <*> mkSumType)) (Proxy @ContractExe) - , (equal <*> (genericShow <*> mkSumType)) (Proxy @(FullReport A)) - , (equal <*> (genericShow <*> mkSumType)) (Proxy @ChainReport) - , (equal <*> (genericShow <*> mkSumType)) (Proxy @(ContractReport A)) - , (equal <*> (genericShow <*> mkSumType)) - (Proxy @(ContractSignatureResponse A)) - , (equal <*> (genericShow <*> mkSumType)) (Proxy @(PartiallyDecodedResponse A)) - - -- Contract request / response types - , (equal <*> (genericShow <*> mkSumType)) (Proxy @TxConfirmed) - , (equal <*> (genericShow <*> mkSumType)) (Proxy @CheckpointStore) - , (order <*> (genericShow <*> mkSumType)) (Proxy @CheckpointKey) - , (equal <*> (genericShow <*> mkSumType)) (Proxy @(CheckpointStoreItem A)) - , (equal <*> (genericShow <*> mkSumType)) (Proxy @(Responses A)) + PAB.pabTypes <> + marketplaceTypes - -- Logging types - , (equal <*> (genericShow <*> mkSumType)) (Proxy @(LogMessage A)) - , (equal <*> (genericShow <*> mkSumType)) (Proxy @LogLevel) - - -- Metadata types - , (order <*> (genericShow <*> mkSumType)) (Proxy @Subject) - , (equal <*> (genericShow <*> mkSumType)) (Proxy @(SubjectProperties A)) - , (equal <*> (genericShow <*> mkSumType)) (Proxy @(Property A)) - , (order <*> (genericShow <*> mkSumType)) (Proxy @PropertyKey) - , (equal <*> (genericShow <*> mkSumType)) (Proxy @HashFunction) - , (equal <*> (genericShow <*> mkSumType)) (Proxy @(AnnotatedSignature A)) - - -- * Web API types - , (equal <*> (genericShow <*> mkSumType)) (Proxy @(ContractActivationArgs A)) - , (genericShow <*> mkSumType) (Proxy @(ContractInstanceClientState A)) - , (genericShow <*> mkSumType) (Proxy @InstanceStatusToClient) - , (genericShow <*> mkSumType) (Proxy @CombinedWSStreamToClient) - , (genericShow <*> mkSumType) (Proxy @CombinedWSStreamToServer) - , (genericShow <*> mkSumType) (Proxy @WalletInfo) - ] +instance PAB.HasPSTypes MyBridge where + psTypes _ = myTypes mySettings :: Settings mySettings = (defaultSettings & set apiModuleName "Plutus.PAB.Webserver") {_generateSubscriberAPI = False} - -defaultWallet :: Wallet -defaultWallet = Wallet 1 ------------------------------------------------------------ generateTo :: FilePath -> IO () generateTo outputDir = do exists <- doesDirectoryExist outputDir when exists $ removeDirectoryRecursive outputDir - writePSTypesWith - (genForeign (ForeignOptions {unwrapSingleConstructors = True})) - outputDir - (buildBridge myBridge) - myTypes + PAB.generateWith myBridgeProxy outputDir main :: IO () main = generateTo "client/generated" diff --git a/MetaLamp/nft-marketplace/generate-purs/MarketplaceTypes.hs b/MetaLamp/nft-marketplace/generate-purs/MarketplaceTypes.hs index f9ebe6b33..e9688c042 100644 --- a/MetaLamp/nft-marketplace/generate-purs/MarketplaceTypes.hs +++ b/MetaLamp/nft-marketplace/generate-purs/MarketplaceTypes.hs @@ -29,14 +29,12 @@ import Language.PureScript.Bridge (BridgePart, (^==)) import Language.PureScript.Bridge.Builder (BridgeData) import Language.PureScript.Bridge.TypeParameters (A, E) -import qualified PSGenerator.Common import Plutus.Abstract.ContractResponse (ContractResponse) import qualified Plutus.Contracts.NftMarketplace.Endpoints as Marketplace import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace import qualified Ext.Plutus.Contracts.Auction as Auction import qualified Plutus.Contracts.Services.Sale as Sale import Plutus.PAB.Simulation (MarketplaceContracts (..)) -import Plutus.V1.Ledger.Value (AssetClass) ratioBridge :: BridgePart ratioBridge = do @@ -53,7 +51,6 @@ marketplaceTypes :: [SumType 'Haskell] marketplaceTypes = [ (equal <*> (genericShow <*> mkSumType)) (Proxy @MarketplaceContracts) , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.Marketplace) , (equal <*> (genericShow <*> mkSumType)) (Proxy @(ContractResponse E A)) - , (order <*> (equal <*> (genericShow <*> mkSumType))) (Proxy @AssetClass) , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.MarketplaceDatum) , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.UserItemId) , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.UserContractState) diff --git a/MetaLamp/nft-marketplace/plutus-starter.cabal b/MetaLamp/nft-marketplace/plutus-starter.cabal index 7e75330d9..83c5c9fb1 100644 --- a/MetaLamp/nft-marketplace/plutus-starter.cabal +++ b/MetaLamp/nft-marketplace/plutus-starter.cabal @@ -96,27 +96,14 @@ executable generate-purs -threaded build-depends: base >= 4.9 && < 5, - aeson, + purescript-bridge, + servant-purescript -any, + mtl, directory, - servant-purescript, - filepath, - servant-server, - bytestring, - aeson-pretty, - freer-extras, lens, - text, - mtl, -- Plutus: - plutus-ledger-api, - purescript-bridge, - playground-common, plutus-starter, - plutus-pab, - plutus-contract, - plutus-use-cases, - plutus-ledger, - plutus-tx + plutus-pab test-suite test import: lang From 1b0653a32f72f2cec80f37d4cee7dd12b4b6705d Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Fri, 3 Sep 2021 22:26:04 +0700 Subject: [PATCH 078/217] fix tests --- .../Contracts/NftMarketplace/OffChain/Owner.hs | 3 +-- .../Plutus/Contracts/NftMarketplace/OnChain/Core.hs | 4 ---- .../NftMarketplace/OnChain/Core/StateMachine.hs | 4 ++-- .../nft-marketplace/src/Plutus/PAB/Simulation.hs | 2 +- .../test/Marketplace/Fixtures/CheckOptions.hs | 11 +++-------- .../test/Marketplace/Fixtures/Script.hs | 12 ++++++------ .../nft-marketplace/test/Marketplace/Spec/Auction.hs | 12 ++++++------ .../nft-marketplace/test/Marketplace/Spec/Bundles.hs | 4 ++-- .../test/Marketplace/Spec/CreateNft.hs | 4 ++-- .../nft-marketplace/test/Marketplace/Spec/Sale.hs | 8 ++++---- .../nft-marketplace/test/Marketplace/Spec/Start.hs | 12 +++--------- MetaLamp/nft-marketplace/test/Utils/Data.hs | 9 ++++++++- MetaLamp/nft-marketplace/test/Utils/Trace.hs | 4 ++-- 13 files changed, 40 insertions(+), 49 deletions(-) diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Owner.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Owner.hs index 14939c0ac..1f8f840d9 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Owner.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Owner.hs @@ -36,9 +36,8 @@ import Text.Printf (printf) -- | Starts the NFT Marketplace protocol: minting protocol NFT, creating empty nft storage start :: Contract w s Text Core.Marketplace start = do - marketplaceToken <- mapError (T.pack . Haskell.show @SMContractError) $ getThreadToken pkh <- pubKeyHash <$> ownPubKey - let marketplace = Core.Marketplace marketplaceToken + let marketplace = Core.Marketplace pkh let client = Core.marketplaceClient marketplace void $ mapError (T.pack . Haskell.show @SMContractError) $ runInitialise client (Core.MarketplaceDatum AssocMap.empty AssocMap.empty) mempty diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core.hs index 4f400f25d..23f412fe1 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core.hs @@ -24,10 +24,6 @@ import PlutusTx.Prelude hidin import Prelude (Semigroup (..)) import qualified Prelude as Haskell --- TODO! --- marketplace :: CurrencySymbol -> Marketplace --- marketplace protocol = Marketplace (assetClass protocol marketplaceProtocolName) - marketplaceValidator :: Marketplace -> Validator marketplaceValidator = Scripts.validatorScript . marketplaceInst diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs index 0f3971edb..d0e839e65 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OnChain/Core/StateMachine.hs @@ -37,7 +37,7 @@ import qualified Prelude as Haskell newtype Marketplace = Marketplace - { marketplaceProtocolToken :: ThreadToken + { marketplaceOperator :: PubKeyHash } deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON) @@ -251,7 +251,7 @@ marketplaceStateMachine marketplace = StateMachine { smTransition = transition marketplace , smFinal = const False , smCheck = stateTransitionCheck - , smThreadToken = Just $ marketplaceProtocolToken marketplace + , smThreadToken = Nothing } {-# INLINABLE mkMarketplaceValidator #-} diff --git a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs index e95b7ddd9..85d645788 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs @@ -183,7 +183,7 @@ runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do let auction = Marketplace.StartAnAuctionParams { saapItemId = Marketplace.UserNftId photoTokenIpfsCid, - saapDuration = 80 + saapDuration = 80 * 1000 } _ <- Simulator.callEndpointOnInstance userCid "startAnAuction" auction diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/CheckOptions.hs b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/CheckOptions.hs index ee17d0951..589e62b84 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/CheckOptions.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/CheckOptions.hs @@ -10,21 +10,16 @@ import qualified Data.Map as Map import Ledger (Value) import qualified Ledger.Ada as Ada import qualified Ledger.Value as V -import qualified Marketplace.Fixtures.Script as Fixtures import qualified Marketplace.Fixtures.Wallet as Fixtures import Plutus.Contract.Test import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace import qualified Plutus.Trace as Trace +import Data.Default (Default (def)) options :: CheckOptions options = defaultCheckOptions & emulatorConfig .~ emulatorCfg & maxSlot .~ 15_000 where emulatorCfg :: Trace.EmulatorConfig - emulatorCfg = - Trace.EmulatorConfig $ Left $ - Map.singleton Fixtures.ownerWallet v <> Map.fromList ((, Ada.lovelaceValueOf 1_000_000_000) <$> Fixtures.userWallets) - v :: Value - v = - Ada.lovelaceValueOf 1_000_000_000 <> - V.singleton Fixtures.marketplaceSymbol Marketplace.marketplaceProtocolName 1 + emulatorCfg = def + \ No newline at end of file diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/Script.hs b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/Script.hs index 5f55743e3..f546fc80d 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/Script.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/Script.hs @@ -3,18 +3,18 @@ module Marketplace.Fixtures.Script where -import Ledger (Address, +import Ledger (Address,pubKeyHash, CurrencySymbol) import qualified Ledger.Value as V import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace - +import qualified Marketplace.Fixtures.Wallet as Fixtures +import Wallet.Emulator.Types (Wallet (..), + walletPubKey) + marketplace :: Marketplace.Marketplace marketplace = Marketplace.Marketplace $ - V.assetClass marketplaceSymbol Marketplace.marketplaceProtocolName - -marketplaceSymbol :: CurrencySymbol -marketplaceSymbol = "ff" + pubKeyHash $ walletPubKey Fixtures.ownerWallet marketplaceAddress :: Address marketplaceAddress = Marketplace.marketplaceAddress marketplace diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Auction.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Auction.hs index 14e5a4d04..564f64b67 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Auction.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Auction.hs @@ -112,7 +112,7 @@ startAnAuctionParams :: Marketplace.StartAnAuctionParams startAnAuctionParams = Marketplace.StartAnAuctionParams { Marketplace.saapItemId = Marketplace.UserNftId Fixtures.catTokenIpfsCid, - Marketplace.saapDuration = 155 + Marketplace.saapDuration = 155 * 1000 } closeLotParams :: Marketplace.CloseLotParams @@ -195,7 +195,7 @@ startAnAuctionDatumsCheck :: TracePredicate startAnAuctionDatumsCheck = dataAtAddress Fixtures.marketplaceAddress - (nftIsOnAuction . Marketplace.mdSingletons) + (Utils.checkOneDatum (nftIsOnAuction . Marketplace.mdSingletons)) where nftIsOnAuction = maybe False (\t -> t ^. Marketplace._nftLot ^? traverse . _2 . _Right & fmap auctionValue & (== Just (Marketplace.nftValue Fixtures.catTokenIpfsCid t))) . @@ -205,7 +205,7 @@ completeAuctionDatumsCheck :: TracePredicate completeAuctionDatumsCheck = dataAtAddress Fixtures.marketplaceAddress - (nftNotOnAuction . Marketplace.mdSingletons) + (Utils.checkOneDatum (nftNotOnAuction . Marketplace.mdSingletons)) where nftNotOnAuction = maybe False (isNothing . Marketplace.nftLot) . AssocMap.lookup Fixtures.catTokenIpfsCidHash @@ -245,7 +245,7 @@ startAnAuctionParamsB :: Marketplace.StartAnAuctionParams startAnAuctionParamsB = Marketplace.StartAnAuctionParams { Marketplace.saapItemId = Marketplace.UserBundleId Fixtures.cids, - Marketplace.saapDuration = 142 + Marketplace.saapDuration = 142 * 1000 } closeLotParamsB :: Marketplace.CloseLotParams @@ -310,7 +310,7 @@ startAnAuctionDatumsCheckB :: TracePredicate startAnAuctionDatumsCheckB = dataAtAddress Fixtures.marketplaceAddress - (bundleIsOnAuction . Marketplace.mdBundles) + (Utils.checkOneDatum (bundleIsOnAuction . Marketplace.mdBundles)) where bundleIsOnAuction = maybe False (\b -> b ^. Marketplace._nbTokens ^? Marketplace._HasLot . _2 . _Right & fmap auctionValue & (== Just (Marketplace.bundleValue AssocMap.empty b))) . @@ -320,7 +320,7 @@ completeAuctionDatumsCheckB :: TracePredicate completeAuctionDatumsCheckB = dataAtAddress Fixtures.marketplaceAddress - (bundleNotOnAuction . Marketplace.mdBundles) + (Utils.checkOneDatum (bundleNotOnAuction . Marketplace.mdBundles)) where bundleNotOnAuction = maybe False (Prelude.not . Marketplace.hasLotBundle) . AssocMap.lookup Fixtures.bundleId diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Bundles.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Bundles.hs index 7babb9524..764bb45fb 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Bundles.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Bundles.hs @@ -100,7 +100,7 @@ bundleDatumsCheck :: TracePredicate bundleDatumsCheck = dataAtAddress Fixtures.marketplaceAddress - (containsBundle . Marketplace.mdBundles) + (Utils.checkOneDatum (containsBundle . Marketplace.mdBundles)) where containsBundle = maybe False checkBundle . AssocMap.lookup Fixtures.bundleId @@ -136,7 +136,7 @@ unbundleErrorTrace = do unbundleDatumsCheck :: TracePredicate unbundleDatumsCheck = dataAtAddress - Fixtures.marketplaceAddress $ + Fixtures.marketplaceAddress $ Utils.checkOneDatum $ \mp -> (containsNoBundle . Marketplace.mdBundles $ mp) && (containsNfts . Marketplace.mdSingletons $ mp) where containsNoBundle = isNothing . AssocMap.lookup Fixtures.bundleId diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/CreateNft.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/CreateNft.hs index bdb7892de..17b0ce161 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Spec/CreateNft.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/CreateNft.hs @@ -71,7 +71,7 @@ datumsCheck :: TracePredicate datumsCheck = dataAtAddress Fixtures.marketplaceAddress - (containsNft . Marketplace.mdSingletons) + (Utils.checkOneDatum (containsNft . Marketplace.mdSingletons)) where containsNft = maybe False (\t -> (t ^. Marketplace._nftLot & isNothing) && (t ^. Marketplace._nftRecord . Marketplace._niIssuer & isNothing) && @@ -82,7 +82,7 @@ datumsCheck' :: TracePredicate datumsCheck' = dataAtAddress Fixtures.marketplaceAddress - (containsNft . Marketplace.mdSingletons) + (Utils.checkOneDatum (containsNft . Marketplace.mdSingletons)) where containsNft = maybe False (\t -> (t ^. Marketplace._nftLot & isNothing) && t ^. Marketplace._nftRecord . Marketplace._niIssuer == Just (Utils.walletPkh Fixtures.userWallet) && diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Sale.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Sale.hs index 5ee05b5a3..f67b73e42 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Sale.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Sale.hs @@ -166,7 +166,7 @@ openSaleDatumsCheck :: TracePredicate openSaleDatumsCheck = dataAtAddress Fixtures.marketplaceAddress - (nftIsOnSale . Marketplace.mdSingletons) + (Utils.checkOneDatum (nftIsOnSale . Marketplace.mdSingletons)) where nftIsOnSale = maybe False (\t -> t ^. Marketplace._nftLot ^? traverse . _2 . _Left & fmap Sale.saleValue & (== Just (Marketplace.nftValue Fixtures.catTokenIpfsCid t))) . @@ -176,7 +176,7 @@ completeSaleDatumsCheck :: TracePredicate completeSaleDatumsCheck = dataAtAddress Fixtures.marketplaceAddress - (nftNotOnSale . Marketplace.mdSingletons) + (Utils.checkOneDatum (nftNotOnSale . Marketplace.mdSingletons)) where nftNotOnSale = maybe False (isNothing . Marketplace.nftLot) . AssocMap.lookup Fixtures.catTokenIpfsCidHash @@ -265,7 +265,7 @@ openSaleDatumsCheckB :: TracePredicate openSaleDatumsCheckB = dataAtAddress Fixtures.marketplaceAddress - (bundleIsOnSale . Marketplace.mdBundles) + (Utils.checkOneDatum (bundleIsOnSale . Marketplace.mdBundles)) where bundleIsOnSale = maybe False (\b -> b ^. Marketplace._nbTokens ^? Marketplace._HasLot . _2 . _Left & fmap Sale.saleValue & (== Just (Marketplace.bundleValue AssocMap.empty b))) . @@ -275,7 +275,7 @@ completeSaleDatumsCheckB :: TracePredicate completeSaleDatumsCheckB = dataAtAddress Fixtures.marketplaceAddress - (bundleNotOnSale . Marketplace.mdBundles) + (Utils.checkOneDatum (bundleNotOnSale . Marketplace.mdBundles)) where bundleNotOnSale = maybe False (Prelude.not . Marketplace.hasLotBundle) . AssocMap.lookup Fixtures.bundleId diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Start.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Start.hs index afaa85bd6..5f993c056 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Start.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Start.hs @@ -24,7 +24,7 @@ tests = [ checkPredicateOptions Fixtures.options "Should start a new marketplace with empty store" - (datumsCheck .&&. valueCheck) + datumsCheck startTrace ] @@ -36,16 +36,10 @@ startTrace = do startContract :: Contract () Marketplace.MarketplaceOwnerSchema Text Marketplace.Marketplace -startContract = Marketplace.start' $ pure Fixtures.marketplaceSymbol +startContract = Marketplace.start datumsCheck :: TracePredicate datumsCheck = dataAtAddress Fixtures.marketplaceAddress - (== Marketplace.MarketplaceDatum AssocMap.empty AssocMap.empty) - -valueCheck :: TracePredicate -valueCheck = - valueAtAddress - Fixtures.marketplaceAddress - (== V.singleton Fixtures.marketplaceSymbol Marketplace.marketplaceProtocolName 1) + (== [Marketplace.MarketplaceDatum AssocMap.empty AssocMap.empty]) diff --git a/MetaLamp/nft-marketplace/test/Utils/Data.hs b/MetaLamp/nft-marketplace/test/Utils/Data.hs index 94844a487..a6a8b852e 100644 --- a/MetaLamp/nft-marketplace/test/Utils/Data.hs +++ b/MetaLamp/nft-marketplace/test/Utils/Data.hs @@ -1,7 +1,10 @@ module Utils.Data where -import Plutus.V1.Ledger.Crypto (PubKeyHash, pubKeyHash) +import Plutus.V1.Ledger.Crypto (PubKeyHash) import Wallet.Emulator.Wallet (Wallet, walletPubKey) +import Wallet.Emulator.Types (Wallet (..), + walletPubKey) +import Ledger (Address,pubKeyHash) one :: (a -> Bool) -> [a] -> Bool one f = foldr reducer False @@ -10,3 +13,7 @@ one f = foldr reducer False walletPkh :: Wallet -> PubKeyHash walletPkh = pubKeyHash . walletPubKey + +checkOneDatum :: (d -> Bool) -> [d] -> Bool +checkOneDatum check [d] = check d +checkOneDatum _ _ = False diff --git a/MetaLamp/nft-marketplace/test/Utils/Trace.hs b/MetaLamp/nft-marketplace/test/Utils/Trace.hs index ca756fb92..bc0d7d8aa 100644 --- a/MetaLamp/nft-marketplace/test/Utils/Trace.hs +++ b/MetaLamp/nft-marketplace/test/Utils/Trace.hs @@ -15,8 +15,8 @@ import Plutus.Contract.Test (TracePredicate, assertAccumState) import qualified Plutus.Trace.Emulator as Trace -assertCrError :: forall e r s err a. (Show r, Show e) => - C.Contract (ContractResponse e r) s err a +assertCrError :: forall contract e r s err a. (Show r, Show e, C.IsContract contract) => + contract (ContractResponse e r) s err a -> Trace.ContractInstanceTag -> TracePredicate assertCrError c tag = assertAccumState c tag (isJust . (^? _CrError)) "Expected contract error but there was none" From 8fe5a3bb001aae7eb4ad28a745c81658ad05200b Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Fri, 3 Sep 2021 23:16:44 +0700 Subject: [PATCH 079/217] wip fix client --- .../nft-marketplace/client/scripts/fetch-plutus-purs.sh | 2 +- MetaLamp/nft-marketplace/client/spago.dhall | 1 + MetaLamp/nft-marketplace/generate-purs/MarketplaceTypes.hs | 7 ++++++- MetaLamp/nft-marketplace/plutus-starter.cabal | 4 +++- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/MetaLamp/nft-marketplace/client/scripts/fetch-plutus-purs.sh b/MetaLamp/nft-marketplace/client/scripts/fetch-plutus-purs.sh index 4b185c343..526cde1c4 100755 --- a/MetaLamp/nft-marketplace/client/scripts/fetch-plutus-purs.sh +++ b/MetaLamp/nft-marketplace/client/scripts/fetch-plutus-purs.sh @@ -6,4 +6,4 @@ git remote add origin -f https://github.com/input-output-hk/plutus git config core.sparseCheckout true echo 'web-common-plutus/*' >> .git/info/sparse-checkout echo 'web-common/*' >> .git/info/sparse-checkout -git pull origin bd16cc29045ffc7eaa6beaabe3b985a56cb9292a # plutus-starter-devcontainer/v1.0.6 +git pull origin cc7bc06c4344cee6cd59bc170063fd627da25ed3 # plutus-starter-devcontainer/v1.0.8 diff --git a/MetaLamp/nft-marketplace/client/spago.dhall b/MetaLamp/nft-marketplace/client/spago.dhall index c1041dcf2..136f8c5ef 100644 --- a/MetaLamp/nft-marketplace/client/spago.dhall +++ b/MetaLamp/nft-marketplace/client/spago.dhall @@ -6,6 +6,7 @@ You can edit this file as you like. , dependencies = [ "aff" , "affjax" + , "aff-promise" , "argonaut-codecs" , "avar" , "bigints" diff --git a/MetaLamp/nft-marketplace/generate-purs/MarketplaceTypes.hs b/MetaLamp/nft-marketplace/generate-purs/MarketplaceTypes.hs index e9688c042..fea8adbb7 100644 --- a/MetaLamp/nft-marketplace/generate-purs/MarketplaceTypes.hs +++ b/MetaLamp/nft-marketplace/generate-purs/MarketplaceTypes.hs @@ -35,6 +35,8 @@ import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace import qualified Ext.Plutus.Contracts.Auction as Auction import qualified Plutus.Contracts.Services.Sale as Sale import Plutus.PAB.Simulation (MarketplaceContracts (..)) +import Plutus.Contract.StateMachine.ThreadToken (ThreadToken) +import Plutus.V1.Ledger.Time (DiffMilliSeconds) ratioBridge :: BridgePart ratioBridge = do @@ -48,7 +50,10 @@ psRatio = expand <$> psTypeParameters expand [x] = TypeInfo "web-common" "Data.Json.JsonTuple" "JsonTuple" [x, x] marketplaceTypes :: [SumType 'Haskell] -marketplaceTypes = [ (equal <*> (genericShow <*> mkSumType)) (Proxy @MarketplaceContracts) +marketplaceTypes = + [ (equal <*> (genericShow <*> mkSumType)) (Proxy @ThreadToken) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @DiffMilliSeconds) + , (equal <*> (genericShow <*> mkSumType)) (Proxy @MarketplaceContracts) , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.Marketplace) , (equal <*> (genericShow <*> mkSumType)) (Proxy @(ContractResponse E A)) , (equal <*> (genericShow <*> mkSumType)) (Proxy @Marketplace.MarketplaceDatum) diff --git a/MetaLamp/nft-marketplace/plutus-starter.cabal b/MetaLamp/nft-marketplace/plutus-starter.cabal index 83c5c9fb1..0db1310ac 100644 --- a/MetaLamp/nft-marketplace/plutus-starter.cabal +++ b/MetaLamp/nft-marketplace/plutus-starter.cabal @@ -103,7 +103,9 @@ executable generate-purs lens, -- Plutus: plutus-starter, - plutus-pab + plutus-pab, + plutus-contract, + plutus-ledger-api test-suite test import: lang From d2ec4d3551edc08f4549824d1c68f23278b6d233 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Sat, 4 Sep 2021 10:28:23 +0700 Subject: [PATCH 080/217] fix client --- .../nft-marketplace/client/src/Business/Marketplace.purs | 2 +- MetaLamp/nft-marketplace/client/src/Main.purs | 2 +- MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/MetaLamp/nft-marketplace/client/src/Business/Marketplace.purs b/MetaLamp/nft-marketplace/client/src/Business/Marketplace.purs index 76cb40925..b2082760d 100644 --- a/MetaLamp/nft-marketplace/client/src/Business/Marketplace.purs +++ b/MetaLamp/nft-marketplace/client/src/Business/Marketplace.purs @@ -49,7 +49,7 @@ getMarketplaceResponseWith endpoint pick cid param = pollEndpoint getNext endpoi (preview pick state) getMarketplaceContractId :: forall a. Prism' MarketplaceContracts a -> ContractInstanceClientState MarketplaceContracts -> Maybe ContractId -getMarketplaceContractId pick (ContractInstanceClientState { cicContract, cicDefintion }) = (const $ toContractIdParam cicContract) <$> (preview pick cicDefintion) +getMarketplaceContractId pick (ContractInstanceClientState st) = (const $ toContractIdParam st.cicContract) <$> (preview pick st.cicDefinition) toContractIdParam :: ContractInstanceId -> ContractId toContractIdParam (ContractInstanceId { unContractInstanceId: JsonUUID uuid }) = ContractId <<< UUID.toString $ uuid diff --git a/MetaLamp/nft-marketplace/client/src/Main.purs b/MetaLamp/nft-marketplace/client/src/Main.purs index 031d8609a..aa2923b18 100644 --- a/MetaLamp/nft-marketplace/client/src/Main.purs +++ b/MetaLamp/nft-marketplace/client/src/Main.purs @@ -22,7 +22,7 @@ main :: Effect Unit main = runHalogenAff do let - env = { ipfsServer: { host: "localhost", port: 5001 }, pabServer: { host: "localhost", port: 8080 } } + env = { ipfsServer: { host: "localhost", port: 5001 }, pabServer: { host: "localhost", port: 9080 } } let rootComponent = H.hoist (runAppM env) App.component body <- awaitBody diff --git a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs index 85d645788..a7968b09e 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs @@ -92,16 +92,16 @@ activateContracts = do startMpServer :: IO () startMpServer = void $ Simulator.runSimulationWith handlers $ do - Simulator.logString @(Builtin MarketplaceContracts) "Starting NFT Marketplace PAB webserver on port 8080. Press enter to exit." + Simulator.logString @(Builtin MarketplaceContracts) "Starting NFT Marketplace PAB webserver on port 9080. Press enter to exit." shutdown <- PAB.Server.startServerDebug _ <- activateContracts - Simulator.logString @(Builtin MarketplaceContracts) "NFT Marketplace PAB webserver started on port 8080. Initialization complete. Press enter to exit." + Simulator.logString @(Builtin MarketplaceContracts) "NFT Marketplace PAB webserver started on port 9080. Initialization complete. Press enter to exit." _ <- liftIO getLine shutdown runNftMarketplace :: IO () runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do - Simulator.logString @(Builtin MarketplaceContracts) "Starting Marketplace PAB webserver on port 8080. Press enter to exit." + Simulator.logString @(Builtin MarketplaceContracts) "Starting Marketplace PAB webserver on port 9080. Press enter to exit." shutdown <- PAB.Server.startServerDebug ContractIDs {..} <- activateContracts let userCid = cidUser Map.! Wallet 2 From 17b46f58f6a03432117d6747a2866cf8a3bb54c6 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Sat, 4 Sep 2021 12:45:17 +0700 Subject: [PATCH 081/217] fix pab api call --- MetaLamp/nft-marketplace/client/src/AppAff.purs | 6 +++--- .../nft-marketplace/client/src/Component/MainPage.purs | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/MetaLamp/nft-marketplace/client/src/AppAff.purs b/MetaLamp/nft-marketplace/client/src/AppAff.purs index 0fe73aa10..f5bb0067c 100644 --- a/MetaLamp/nft-marketplace/client/src/AppAff.purs +++ b/MetaLamp/nft-marketplace/client/src/AppAff.purs @@ -162,9 +162,9 @@ pinIpfs path file = do decodeResp = decode instance contractAppM :: Contract AppM where - getContracts = getC "/api/new/contract/instances" - getContractStatus (ContractId cid) = getC $ "/api/new/contract/instance/" <> cid <> "/status" - callEndpoint (Endpoint endpoint) (ContractId cid) params = postC ("/api/new/contract/instance/" <> cid <> "/endpoint/" <> endpoint) (string <<< encodeJSON $ params) + getContracts = getC "/api/contract/instances" + getContractStatus (ContractId cid) = getC $ "/api/contract/instance/" <> cid <> "/status" + callEndpoint (Endpoint endpoint) (ContractId cid) params = postC ("/api/contract/instance/" <> cid <> "/endpoint/" <> endpoint) (string <<< encodeJSON $ params) instance pollContractAppM :: PollContract AppM where pollDelay = liftAff <<< delay <<< Milliseconds $ 1000.0 diff --git a/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs b/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs index 5a27b63ba..774291d8c 100644 --- a/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs +++ b/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs @@ -128,12 +128,12 @@ component = handleAction :: Action -> H.HalogenM State Action Slots output m Unit handleAction = case _ of Initialize -> do - handleAction GetContracts - handleAction GetInstances - res <- IPFS.catFile "QmPD9JxfSzKBwHEac9m3zbnQiHnRRYRubL9vCnhoC2AdL4" - logInfo $ show res initialRoute <- hush <<< (Routing.parse routeCodec) <$> H.liftEffect Routing.getHash navigate $ fromMaybe UserPage initialRoute + res <- IPFS.catFile "QmTxda1tn2ourkdKYhTwMfLScLCLxJUXMX3q4ScLyVFy8n" + logInfo $ show res + handleAction GetContracts + handleAction GetInstances GoTo route e -> do H.liftEffect $ preventDefault (toEvent e) oldRoute <- H.gets _.route From 16078b405baf6b6b280b647f8e0b83dfcb6b825f Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Sat, 4 Sep 2021 14:55:46 +0700 Subject: [PATCH 082/217] add cors middleware to pab --- MetaLamp/nft-marketplace/cabal.project | 5 ++ MetaLamp/nft-marketplace/plutus-starter.cabal | 5 +- .../src/Ext/Plutus/PAB/Webserver/Server.hs | 53 +++++++++++++++++++ .../src/Plutus/PAB/Simulation.hs | 9 ++-- 4 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 MetaLamp/nft-marketplace/src/Ext/Plutus/PAB/Webserver/Server.hs diff --git a/MetaLamp/nft-marketplace/cabal.project b/MetaLamp/nft-marketplace/cabal.project index 3b86f5217..710c15c7a 100644 --- a/MetaLamp/nft-marketplace/cabal.project +++ b/MetaLamp/nft-marketplace/cabal.project @@ -214,3 +214,8 @@ source-repository-package type: git location: https://github.com/input-output-hk/goblins tag: cde90a2b27f79187ca8310b6549331e59595e7ba + +source-repository-package + type: git + location: https://github.com/sordina/servant-options + tag: aa9338b1925e7bc5d65bad35f02aec8c20f8365b diff --git a/MetaLamp/nft-marketplace/plutus-starter.cabal b/MetaLamp/nft-marketplace/plutus-starter.cabal index 0db1310ac..660f3d9cb 100644 --- a/MetaLamp/nft-marketplace/plutus-starter.cabal +++ b/MetaLamp/nft-marketplace/plutus-starter.cabal @@ -41,7 +41,7 @@ common lang library import: lang exposed-modules: - Plutus.Contracts.NftMarketplace.OnChain.Core Plutus.Contracts.NftMarketplace.OffChain.ID Plutus.Contracts.NftMarketplace.OnChain.Core.ID Plutus.Contracts.Services.Sale.Core Plutus.Contracts.NftMarketplace.OnChain.Core.NFT Ext.Plutus.Contracts.Auction Plutus.Contracts.Services.Sale Plutus.Contracts.Services.Sale.Endpoints Plutus.Contracts.Services.Sale.StateMachine Plutus.Contracts.NftMarketplace.OffChain.Info Ext.Plutus.Ledger.Value Plutus.Contracts.NftMarketplace.OffChain.User Plutus.Abstract.ContractResponse Plutus.Contracts.NftMarketplace.Endpoints Plutus.Contracts.NftMarketplace.OffChain.Owner Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine Plutus.PAB.Simulation + Plutus.Contracts.NftMarketplace.OnChain.Core Ext.Plutus.PAB.Webserver.Server Plutus.Contracts.NftMarketplace.OffChain.ID Plutus.Contracts.NftMarketplace.OnChain.Core.ID Plutus.Contracts.Services.Sale.Core Plutus.Contracts.NftMarketplace.OnChain.Core.NFT Ext.Plutus.Contracts.Auction Plutus.Contracts.Services.Sale Plutus.Contracts.Services.Sale.Endpoints Plutus.Contracts.Services.Sale.StateMachine Plutus.Contracts.NftMarketplace.OffChain.Info Ext.Plutus.Ledger.Value Plutus.Contracts.NftMarketplace.OffChain.User Plutus.Abstract.ContractResponse Plutus.Contracts.NftMarketplace.Endpoints Plutus.Contracts.NftMarketplace.OffChain.Owner Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine Plutus.PAB.Simulation build-depends: base >= 4.9 && < 5, aeson, @@ -56,6 +56,9 @@ library cryptonite, memory, data-default, + servant-server, + wai-cors, + servant-options, -- Plutus: playground-common, plutus-contract, diff --git a/MetaLamp/nft-marketplace/src/Ext/Plutus/PAB/Webserver/Server.hs b/MetaLamp/nft-marketplace/src/Ext/Plutus/PAB/Webserver/Server.hs new file mode 100644 index 000000000..d850e1785 --- /dev/null +++ b/MetaLamp/nft-marketplace/src/Ext/Plutus/PAB/Webserver/Server.hs @@ -0,0 +1,53 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE MonoLocalBinds #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE PartialTypeSignatures #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE ScopedTypeVariables #-} + +module Ext.Plutus.PAB.Webserver.Server where + +import qualified Plutus.PAB.Webserver.Server as PAB +import Control.Concurrent.Availability (Availability, available, newToken) +import Data.Aeson (FromJSON, ToJSON) +import qualified Servant +import qualified Plutus.PAB.Effects.Contract as Contract +import Plutus.PAB.Simulator (Simulation) +import qualified Plutus.PAB.Simulator as Simulator +import Cardano.Wallet.Types (WalletInfo (..)) +import Ledger.Crypto (pubKeyHash) +import qualified Network.Wai.Middleware.Cors as Cors +import qualified Network.Wai.Middleware.Servant.Options as Cors +import Plutus.PAB.Webserver.API (API, WSAPI, WalletProxy) +import Servant (Application, Handler (Handler), Raw, ServerT, err500, errBody, + hoistServer, serve, serveDirectoryFileServer, (:<|>) ((:<|>))) +import Data.Proxy + +-- Note: this definition is only to provide options responses +-- WSAPI is websocket api which does not support options requests +type CombinedAPI t = + API (Contract.ContractDef t) Integer + +startServer :: forall t. + ( FromJSON (Contract.ContractDef t) + , ToJSON (Contract.ContractDef t) + , Contract.PABContract t + , Servant.MimeUnrender Servant.JSON (Contract.ContractDef t) + ) + => Simulation t (Simulation t ()) +startServer = do + availability <- newToken + let mkWalletInfo = do + (wllt, pk) <- Simulator.addWallet + pure $ WalletInfo{wiWallet = wllt, wiPubKey = pk, wiPubKeyHash = pubKeyHash pk} + snd <$> PAB.startServer' [Cors.cors (const $ Just policy), provideOptions] 9080 (Right mkWalletInfo) Nothing availability 30 + where + provideOptions = Cors.provideOptions (Proxy @(CombinedAPI t)) + policy = Cors.simpleCorsResourcePolicy + { Cors.corsRequestHeaders = [ "content-type" ] } diff --git a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs index a7968b09e..4c1c98152 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs @@ -57,7 +57,9 @@ import Plutus.PAB.Simulator (Simulation, SimulatorEffectHandlers) import qualified Plutus.PAB.Simulator as Simulator import Plutus.PAB.Types (PABError (..)) -import qualified Plutus.PAB.Webserver.Server as PAB.Server +import qualified Plutus.PAB.Types as PAB +import qualified Ext.Plutus.PAB.Webserver.Server as Ext.Plutus.PAB +import qualified Plutus.PAB.Webserver.Server as PAB import Prelude hiding (init) import Wallet.Emulator.Types (Wallet (..), walletPubKey) @@ -93,7 +95,8 @@ activateContracts = do startMpServer :: IO () startMpServer = void $ Simulator.runSimulationWith handlers $ do Simulator.logString @(Builtin MarketplaceContracts) "Starting NFT Marketplace PAB webserver on port 9080. Press enter to exit." - shutdown <- PAB.Server.startServerDebug + shutdown <- Ext.Plutus.PAB.startServer + _ <- activateContracts Simulator.logString @(Builtin MarketplaceContracts) "NFT Marketplace PAB webserver started on port 9080. Initialization complete. Press enter to exit." _ <- liftIO getLine @@ -102,7 +105,7 @@ startMpServer = void $ Simulator.runSimulationWith handlers $ do runNftMarketplace :: IO () runNftMarketplace = void $ Simulator.runSimulationWith handlers $ do Simulator.logString @(Builtin MarketplaceContracts) "Starting Marketplace PAB webserver on port 9080. Press enter to exit." - shutdown <- PAB.Server.startServerDebug + shutdown <- PAB.startServerDebug ContractIDs {..} <- activateContracts let userCid = cidUser Map.! Wallet 2 sender = pubKeyHash . walletPubKey $ Wallet 2 From ab0df988ab194ee51fc701530570144add22789e Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Sat, 4 Sep 2021 14:57:06 +0700 Subject: [PATCH 083/217] run fmt --- .../nft-marketplace/generate-purs/Main.hs | 20 +++---- .../generate-purs/MarketplaceTypes.hs | 52 +++++++++---------- .../src/Ext/Plutus/Contracts/Auction.hs | 3 +- .../src/Ext/Plutus/PAB/Webserver/Server.hs | 37 +++++++------ .../src/Plutus/Abstract/ContractResponse.hs | 2 +- .../Contracts/NftMarketplace/OffChain/Info.hs | 2 +- .../Contracts/NftMarketplace/OffChain/User.hs | 26 +++++----- .../Plutus/Contracts/Services/Sale/Core.hs | 2 +- .../src/Plutus/PAB/Simulation.hs | 6 +-- .../test/Marketplace/Fixtures/CheckOptions.hs | 4 +- .../test/Marketplace/Fixtures/Script.hs | 9 ++-- MetaLamp/nft-marketplace/test/Utils/Data.hs | 7 ++- 12 files changed, 90 insertions(+), 80 deletions(-) diff --git a/MetaLamp/nft-marketplace/generate-purs/Main.hs b/MetaLamp/nft-marketplace/generate-purs/Main.hs index 1adf87ad9..148f98563 100644 --- a/MetaLamp/nft-marketplace/generate-purs/Main.hs +++ b/MetaLamp/nft-marketplace/generate-purs/Main.hs @@ -11,16 +11,18 @@ module Main where -import qualified Plutus.PAB.Run.PSGenerator as PAB -import MarketplaceTypes +import Control.Applicative ((<|>)) +import Control.Lens (set, view, (&)) +import Control.Monad (when) +import Data.Proxy import Language.PureScript.Bridge -import Control.Applicative ((<|>)) -import Data.Proxy -import Servant.PureScript (HasBridge(..),apiModuleName, defaultSettings, Settings, _generateSubscriberAPI) -import Control.Lens (set, view, (&)) -import System.Directory (doesDirectoryExist, - removeDirectoryRecursive) -import Control.Monad (when) +import MarketplaceTypes +import qualified Plutus.PAB.Run.PSGenerator as PAB +import Servant.PureScript (HasBridge (..), Settings, + _generateSubscriberAPI, + apiModuleName, defaultSettings) +import System.Directory (doesDirectoryExist, + removeDirectoryRecursive) myBridge :: BridgePart myBridge = diff --git a/MetaLamp/nft-marketplace/generate-purs/MarketplaceTypes.hs b/MetaLamp/nft-marketplace/generate-purs/MarketplaceTypes.hs index fea8adbb7..cf8fc9e74 100644 --- a/MetaLamp/nft-marketplace/generate-purs/MarketplaceTypes.hs +++ b/MetaLamp/nft-marketplace/generate-purs/MarketplaceTypes.hs @@ -11,32 +11,32 @@ module MarketplaceTypes where -import Control.Monad.Reader (MonadReader) -import Data.Proxy (Proxy (Proxy)) -import Language.PureScript.Bridge (BridgePart, - Language (Haskell), - PSType, SumType, - TypeInfo (TypeInfo), - buildBridge, - equal, - genericShow, - haskType, - mkSumType, order, - psTypeParameters, - typeModule, - typeName, - writePSTypesWith, - (^==)) -import Language.PureScript.Bridge.Builder (BridgeData) -import Language.PureScript.Bridge.TypeParameters (A, E) -import Plutus.Abstract.ContractResponse (ContractResponse) -import qualified Plutus.Contracts.NftMarketplace.Endpoints as Marketplace -import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace -import qualified Ext.Plutus.Contracts.Auction as Auction -import qualified Plutus.Contracts.Services.Sale as Sale -import Plutus.PAB.Simulation (MarketplaceContracts (..)) -import Plutus.Contract.StateMachine.ThreadToken (ThreadToken) -import Plutus.V1.Ledger.Time (DiffMilliSeconds) +import Control.Monad.Reader (MonadReader) +import Data.Proxy (Proxy (Proxy)) +import qualified Ext.Plutus.Contracts.Auction as Auction +import Language.PureScript.Bridge (BridgePart, + Language (Haskell), + PSType, SumType, + TypeInfo (TypeInfo), + buildBridge, + equal, + genericShow, + haskType, + mkSumType, order, + psTypeParameters, + typeModule, + typeName, + writePSTypesWith, + (^==)) +import Language.PureScript.Bridge.Builder (BridgeData) +import Language.PureScript.Bridge.TypeParameters (A, E) +import Plutus.Abstract.ContractResponse (ContractResponse) +import Plutus.Contract.StateMachine.ThreadToken (ThreadToken) +import qualified Plutus.Contracts.NftMarketplace.Endpoints as Marketplace +import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace +import qualified Plutus.Contracts.Services.Sale as Sale +import Plutus.PAB.Simulation (MarketplaceContracts (..)) +import Plutus.V1.Ledger.Time (DiffMilliSeconds) ratioBridge :: BridgePart ratioBridge = do diff --git a/MetaLamp/nft-marketplace/src/Ext/Plutus/Contracts/Auction.hs b/MetaLamp/nft-marketplace/src/Ext/Plutus/Contracts/Auction.hs index 7fbfa9a0c..8f60cce7f 100644 --- a/MetaLamp/nft-marketplace/src/Ext/Plutus/Contracts/Auction.hs +++ b/MetaLamp/nft-marketplace/src/Ext/Plutus/Contracts/Auction.hs @@ -32,7 +32,8 @@ import qualified Ledger.Typed.Scripts as Scripts import Ledger.Typed.Tx (TypedScriptTxOut (..)) import Ledger.Value (AssetClass) import Plutus.Contract -import Plutus.Contract.StateMachine hiding (mkValidator,typedValidator) +import Plutus.Contract.StateMachine hiding (mkValidator, + typedValidator) import qualified Plutus.Contract.StateMachine as SM import Plutus.Contract.Util (loopM) import qualified Plutus.Contracts.Currency as Currency diff --git a/MetaLamp/nft-marketplace/src/Ext/Plutus/PAB/Webserver/Server.hs b/MetaLamp/nft-marketplace/src/Ext/Plutus/PAB/Webserver/Server.hs index d850e1785..01717b020 100644 --- a/MetaLamp/nft-marketplace/src/Ext/Plutus/PAB/Webserver/Server.hs +++ b/MetaLamp/nft-marketplace/src/Ext/Plutus/PAB/Webserver/Server.hs @@ -7,27 +7,34 @@ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PartialTypeSignatures #-} +{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeOperators #-} -{-# LANGUAGE ScopedTypeVariables #-} module Ext.Plutus.PAB.Webserver.Server where -import qualified Plutus.PAB.Webserver.Server as PAB -import Control.Concurrent.Availability (Availability, available, newToken) -import Data.Aeson (FromJSON, ToJSON) -import qualified Servant -import qualified Plutus.PAB.Effects.Contract as Contract -import Plutus.PAB.Simulator (Simulation) -import qualified Plutus.PAB.Simulator as Simulator -import Cardano.Wallet.Types (WalletInfo (..)) -import Ledger.Crypto (pubKeyHash) -import qualified Network.Wai.Middleware.Cors as Cors +import Cardano.Wallet.Types (WalletInfo (..)) +import Control.Concurrent.Availability (Availability, + available, newToken) +import Data.Aeson (FromJSON, ToJSON) +import Data.Proxy +import Ledger.Crypto (pubKeyHash) +import qualified Network.Wai.Middleware.Cors as Cors import qualified Network.Wai.Middleware.Servant.Options as Cors -import Plutus.PAB.Webserver.API (API, WSAPI, WalletProxy) -import Servant (Application, Handler (Handler), Raw, ServerT, err500, errBody, - hoistServer, serve, serveDirectoryFileServer, (:<|>) ((:<|>))) -import Data.Proxy +import qualified Plutus.PAB.Effects.Contract as Contract +import Plutus.PAB.Simulator (Simulation) +import qualified Plutus.PAB.Simulator as Simulator +import Plutus.PAB.Webserver.API (API, WSAPI, + WalletProxy) +import qualified Plutus.PAB.Webserver.Server as PAB +import Servant (Application, + Handler (Handler), Raw, + ServerT, err500, + errBody, hoistServer, + serve, + serveDirectoryFileServer, + (:<|>) ((:<|>))) +import qualified Servant -- Note: this definition is only to provide options responses -- WSAPI is websocket api which does not support options requests diff --git a/MetaLamp/nft-marketplace/src/Plutus/Abstract/ContractResponse.hs b/MetaLamp/nft-marketplace/src/Plutus/Abstract/ContractResponse.hs index a6a7ce136..8395d1ccb 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Abstract/ContractResponse.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Abstract/ContractResponse.hs @@ -5,13 +5,13 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FunctionalDependencies #-} +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE LambdaCase #-} module Plutus.Abstract.ContractResponse where diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs index 89fd602a4..80164fe98 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Info.hs @@ -6,8 +6,8 @@ {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeApplications #-} -{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeOperators #-} module Plutus.Contracts.NftMarketplace.OffChain.Info where diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs index d5433f346..4cd0b4717 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs @@ -1,17 +1,17 @@ -{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE DeriveAnyClass #-} -{-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE DerivingStrategies #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE NoImplicitPrelude #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE TypeApplications #-} -{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeOperators #-} module Plutus.Contracts.NftMarketplace.OffChain.User where diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/Core.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/Core.hs index c84ed686f..ee8002f73 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/Core.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/Services/Sale/Core.hs @@ -14,7 +14,7 @@ {-# OPTIONS_GHC -fno-ignore-interface-pragmas #-} {-# OPTIONS_GHC -fno-omit-interface-pragmas #-} {-# OPTIONS_GHC -fobject-code #-} -{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE StandaloneDeriving #-} module Plutus.Contracts.Services.Sale.Core where diff --git a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs index 4c1c98152..2198791e2 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/PAB/Simulation.hs @@ -25,12 +25,14 @@ import Data.Aeson (FromJSON, Result (..), ToJSON, encode, fromJSON) +import Data.Default (Default (def)) import qualified Data.Map.Strict as Map import qualified Data.Monoid as Monoid import qualified Data.Semigroup as Semigroup import Data.Text (Text) import Data.Text.Prettyprint.Doc (Pretty (..), viaShow) +import qualified Ext.Plutus.PAB.Webserver.Server as Ext.Plutus.PAB import GHC.Generics (Generic) import Ledger import Ledger.Ada (adaSymbol, @@ -57,14 +59,12 @@ import Plutus.PAB.Simulator (Simulation, SimulatorEffectHandlers) import qualified Plutus.PAB.Simulator as Simulator import Plutus.PAB.Types (PABError (..)) -import qualified Plutus.PAB.Types as PAB -import qualified Ext.Plutus.PAB.Webserver.Server as Ext.Plutus.PAB +import qualified Plutus.PAB.Types as PAB import qualified Plutus.PAB.Webserver.Server as PAB import Prelude hiding (init) import Wallet.Emulator.Types (Wallet (..), walletPubKey) import Wallet.Types (ContractInstanceId) -import Data.Default (Default (def)) ownerWallet :: Wallet ownerWallet = Wallet 1 diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/CheckOptions.hs b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/CheckOptions.hs index 589e62b84..a6a924911 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/CheckOptions.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/CheckOptions.hs @@ -6,6 +6,7 @@ module Marketplace.Fixtures.CheckOptions where import Control.Lens ((&), (.~)) +import Data.Default (Default (def)) import qualified Data.Map as Map import Ledger (Value) import qualified Ledger.Ada as Ada @@ -14,7 +15,6 @@ import qualified Marketplace.Fixtures.Wallet as Fixtures import Plutus.Contract.Test import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace import qualified Plutus.Trace as Trace -import Data.Default (Default (def)) options :: CheckOptions options = defaultCheckOptions & emulatorConfig .~ emulatorCfg @@ -22,4 +22,4 @@ options = defaultCheckOptions & emulatorConfig .~ emulatorCfg where emulatorCfg :: Trace.EmulatorConfig emulatorCfg = def - \ No newline at end of file + diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/Script.hs b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/Script.hs index f546fc80d..f774e7597 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/Script.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/Script.hs @@ -3,14 +3,15 @@ module Marketplace.Fixtures.Script where -import Ledger (Address,pubKeyHash, - CurrencySymbol) +import Ledger (Address, + CurrencySymbol, + pubKeyHash) import qualified Ledger.Value as V +import qualified Marketplace.Fixtures.Wallet as Fixtures import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace -import qualified Marketplace.Fixtures.Wallet as Fixtures import Wallet.Emulator.Types (Wallet (..), walletPubKey) - + marketplace :: Marketplace.Marketplace marketplace = Marketplace.Marketplace $ diff --git a/MetaLamp/nft-marketplace/test/Utils/Data.hs b/MetaLamp/nft-marketplace/test/Utils/Data.hs index a6a8b852e..86ec8fbba 100644 --- a/MetaLamp/nft-marketplace/test/Utils/Data.hs +++ b/MetaLamp/nft-marketplace/test/Utils/Data.hs @@ -1,10 +1,9 @@ module Utils.Data where +import Ledger (Address, pubKeyHash) import Plutus.V1.Ledger.Crypto (PubKeyHash) +import Wallet.Emulator.Types (Wallet (..), walletPubKey) import Wallet.Emulator.Wallet (Wallet, walletPubKey) -import Wallet.Emulator.Types (Wallet (..), - walletPubKey) -import Ledger (Address,pubKeyHash) one :: (a -> Bool) -> [a] -> Bool one f = foldr reducer False @@ -16,4 +15,4 @@ walletPkh = pubKeyHash . walletPubKey checkOneDatum :: (d -> Bool) -> [d] -> Bool checkOneDatum check [d] = check d -checkOneDatum _ _ = False +checkOneDatum _ _ = False From 191687957a06c6aa338d24041d0c723a93edcffe Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Sat, 4 Sep 2021 15:31:50 +0700 Subject: [PATCH 084/217] add servant-options to nix --- MetaLamp/nft-marketplace/nix/pkgs/haskell/haskell.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/MetaLamp/nft-marketplace/nix/pkgs/haskell/haskell.nix b/MetaLamp/nft-marketplace/nix/pkgs/haskell/haskell.nix index dc1cb6572..68e51bdc8 100644 --- a/MetaLamp/nft-marketplace/nix/pkgs/haskell/haskell.nix +++ b/MetaLamp/nft-marketplace/nix/pkgs/haskell/haskell.nix @@ -28,6 +28,7 @@ let "https://github.com/input-output-hk/cardano-ledger-specs"."d5b184a820853c7ba202efd615b8fadca1acb52c" = "04k5p6qwmfdza65gl5319r1ahdfwjnyqgzpfxdx0x2g5jcbimar4"; "https://github.com/input-output-hk/cardano-prelude"."fd773f7a58412131512b9f694ab95653ac430852" = "02jddik1yw0222wd6q0vv10f7y8rdgrlqaiy83ph002f9kjx7mh6"; "https://github.com/input-output-hk/goblins"."cde90a2b27f79187ca8310b6549331e59595e7ba" = "17c88rbva3iw82yg9srlxjv2ia5wjb9cyqw44hik565f5v9svnyg"; + "https://github.com/sordina/servant-options"."aa9338b1925e7bc5d65bad35f02aec8c20f8365b" = "0vlp3y414f2i4nhmlp1gh9jns8jydbq1mgv8j7vzh62r506slb1j"; "https://github.com/input-output-hk/iohk-monitoring-framework"."808724ff8a19a33d0ed06f9ef59fbd900b08553c" = "0298dpl29gxzs9as9ha6y0w18hqwc00ipa3hzkxv7nlfrjjz8hmz"; "https://github.com/input-output-hk/optparse-applicative"."7497a29cb998721a9068d5725d49461f2bba0e7a" = "1gvsrg925vynwgqwplgjmp53vj953qyh3wbdf34pw21c8r47w35r"; "https://github.com/input-output-hk/ouroboros-network"."877ce057ff6fb086474c8eaad53f2b7f0e0fce6b" = "1kp0qysfy3hl96a3a61rijascq36f1imh3z4jy0vyiygb6qrv47z"; From 811c634c4a19bafa9a048a11e306a3d9e306489b Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Tue, 7 Sep 2021 18:11:13 +0700 Subject: [PATCH 085/217] add instance state to user page --- .../client/src/Component/MainPage.purs | 41 ++++++++++++---- .../client/src/Component/UserPage.purs | 47 +++++++++++++++++-- .../client/src/Data/UserInstance.purs | 6 +++ 3 files changed, 80 insertions(+), 14 deletions(-) create mode 100644 MetaLamp/nft-marketplace/client/src/Data/UserInstance.purs diff --git a/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs b/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs index 774291d8c..8864098b3 100644 --- a/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs +++ b/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs @@ -3,6 +3,7 @@ module Component.MainPage where import Data.Route import Data.Unit import Prelude + import Business.Marketplace (getMarketplaceContracts) import Business.Marketplace as Marketplace import Business.MarketplaceInfo (InfoContractId, getInfoContractId) @@ -26,9 +27,10 @@ import Data.Either (either, hush) import Data.Json.JsonTuple (JsonTuple(..)) import Data.Lens (Lens') import Data.Lens.Record (prop) -import Data.Maybe (Maybe(..), fromMaybe) +import Data.Maybe (Maybe(..), fromMaybe, maybe) import Data.Symbol (SProxy(..)) import Data.Tuple (Tuple(..)) +import Data.UserInstance (UserInstance) import Effect.Class (class MonadEffect) import Halogen as H import Halogen.HTML as HH @@ -54,7 +56,7 @@ import Web.UIEvent.MouseEvent (MouseEvent, toEvent) type State = { route :: Maybe Route , contracts :: RemoteData String (Array (ContractInstanceClientState MarketplaceContracts)) - , userInstances :: RemoteData String (Array ({ pubKey :: PubKeyHash, contractId :: UserContractId })) + , userInstances :: RemoteData String (Array UserInstance) , currentInstance :: WalletSelector.UserWallet , infoInstance :: Maybe InfoContractId } @@ -62,7 +64,7 @@ type State _contracts :: Lens' State (RemoteData String (Array (ContractInstanceClientState MarketplaceContracts))) _contracts = prop (SProxy :: SProxy "contracts") -_userInstances :: Lens' State (RemoteData String (Array ({ pubKey :: PubKeyHash, contractId :: UserContractId }))) +_userInstances :: Lens' State (RemoteData String (Array UserInstance)) _userInstances = prop (SProxy :: SProxy "userInstances") data Query a @@ -156,10 +158,10 @@ component = lift $ H.modify_ _ { infoInstance = Just cid } _ -> throwError "Info contract not found" parTraverse - ( \contractId -> do - lift $ logInfo $ "Found user instance: " <> show contractId - pubKey <- lift (ownPubKey contractId) >>= either (throwError <<< show) pure - pure $ { pubKey, contractId } + ( \userInstance -> do + lift $ logInfo $ "Found user instance: " <> show userInstance + userPubKey <- lift (ownPubKey userInstance) >>= either (throwError <<< show) pure + pure $ { userInstance, userPubKey } ) (catMaybes <<< map getUserContractId $ contracts) HandleFile file -> do @@ -174,13 +176,15 @@ component = $ H.modify_ _ { route = Just route } pure (Just a) -pages :: forall m. State -> H.ComponentHTML Action Slots m +pages :: forall m. + LogMessages m => + State -> H.ComponentHTML Action Slots m pages st = navbar $ case st.route of Nothing -> HH.h1_ [ HH.text "Loading page..." ] Just route -> case route of - UserPage -> HH.slot User._userPage unit User.component unit absurd + UserPage -> renderUserPage $ getUserPageInput st MarketPage -> HH.slot Market._marketPage unit Market.component unit absurd navbar :: forall w. HH.HTML w Action -> HH.HTML w Action @@ -204,3 +208,22 @@ navbar html = ] , html ] + +getUserPageInput :: State -> Maybe User.Input +getUserPageInput st = do + infoInstance <- st.infoInstance + ucs <- RD.toMaybe st.userInstances + userInstance <- case ucs of + [userA, userB, userC] -> Just $ case st.currentInstance of + WalletSelector.WalletA -> userA + WalletSelector.WalletB -> userB + WalletSelector.WalletC -> userC + _ -> Nothing + pure {infoInstance, userInstance} + +renderUserPage :: forall m. + LogMessages m => + Maybe User.Input -> H.ComponentHTML Action Slots m +renderUserPage = case _ of + Nothing -> HH.h1_ [ HH.text "Loading user page..." ] + Just ui -> HH.slot User._userPage unit User.component ui absurd diff --git a/MetaLamp/nft-marketplace/client/src/Component/UserPage.purs b/MetaLamp/nft-marketplace/client/src/Component/UserPage.purs index 6abf464d0..501afb4fe 100644 --- a/MetaLamp/nft-marketplace/client/src/Component/UserPage.purs +++ b/MetaLamp/nft-marketplace/client/src/Component/UserPage.purs @@ -1,10 +1,18 @@ module Component.UserPage where import Prelude + +import Business.MarketplaceInfo (InfoContractId) +import Business.MarketplaceUser (UserContractId) +import Capability.LogMessages (class LogMessages, logInfo) +import Chain.State (handleAction) +import Data.Maybe (Maybe(..)) import Data.Symbol (SProxy(..)) -import Halogen (Component) +import Data.UserInstance (UserInstance) +import Halogen (Component, lift) import Halogen as H import Halogen.HTML as HH +import Plutus.V1.Ledger.Crypto (PubKeyHash) type Slot id = forall query. H.Slot query Void id @@ -12,13 +20,42 @@ type Slot id _userPage :: SProxy "userPage" _userPage = SProxy -component :: forall q i o m. H.Component HH.HTML q i o m +type Input + = { userInstance :: UserInstance + , infoInstance :: InfoContractId + } + +type State + = { userInstance :: UserInstance + , infoInstance :: InfoContractId + } + +data Action + = Initialize + | Reinitialize Input + +component :: forall query output m. LogMessages m => H.Component HH.HTML query Input output m component = H.mkComponent { initialState: identity , render - , eval: H.mkEval H.defaultEval + , eval: H.mkEval + $ H.defaultEval + { handleAction = handleAction + , initialize = Just Initialize + , receive = Just <<< Reinitialize + } } + where + render :: State -> H.ComponentHTML Action () m + render _ = HH.h1_ [ HH.text "UserPage" ] + + handleAction :: forall slots. Action -> H.HalogenM State Action slots output m Unit + handleAction = case _ of + Initialize -> do + state <- H.get + logInfo $ show state + Reinitialize st -> do + H.put st + handleAction Initialize -render :: forall state action m. state -> H.ComponentHTML action () m -render _ = HH.h1_ [ HH.text "UserPage" ] diff --git a/MetaLamp/nft-marketplace/client/src/Data/UserInstance.purs b/MetaLamp/nft-marketplace/client/src/Data/UserInstance.purs new file mode 100644 index 000000000..04e57aa2d --- /dev/null +++ b/MetaLamp/nft-marketplace/client/src/Data/UserInstance.purs @@ -0,0 +1,6 @@ +module Data.UserInstance where + +import Business.MarketplaceUser (UserContractId) +import Plutus.V1.Ledger.Crypto (PubKeyHash) + +type UserInstance = { userInstance :: UserContractId, userPubKey :: PubKeyHash } From 0ec51e58b4f276ff2c68dfd7cd0322e88d999476 Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Tue, 7 Sep 2021 21:05:00 +0700 Subject: [PATCH 086/217] wip add create nft element --- .../client/src/Business/MarketplaceUser.purs | 29 ++++++- .../client/src/Component/MainPage.purs | 43 +++++----- .../client/src/Component/UserPage.purs | 84 +++++++++++++++---- .../client/src/Data/UserInstance.purs | 3 +- 4 files changed, 118 insertions(+), 41 deletions(-) diff --git a/MetaLamp/nft-marketplace/client/src/Business/MarketplaceUser.purs b/MetaLamp/nft-marketplace/client/src/Business/MarketplaceUser.purs index c53665f76..8ee5c4178 100644 --- a/MetaLamp/nft-marketplace/client/src/Business/MarketplaceUser.purs +++ b/MetaLamp/nft-marketplace/client/src/Business/MarketplaceUser.purs @@ -9,7 +9,7 @@ import Data.Generic.Rep (class Generic) import Data.Generic.Rep.Show (genericShow) import Data.Maybe (Maybe) import Data.Newtype (class Newtype, unwrap) -import Plutus.Contracts.NftMarketplace.OffChain.User (_GetPubKey, _GetPubKeyBalance) +import Plutus.Contracts.NftMarketplace.OffChain.User import Plutus.PAB.Simulation (MarketplaceContracts, _MarketplaceUser) import Plutus.PAB.Webserver.Types (ContractInstanceClientState) import Plutus.V1.Ledger.Crypto (PubKeyHash) @@ -33,3 +33,30 @@ ownPubKey cid = getMarketplaceResponseWith (Endpoint "ownPubKey") _GetPubKey (un ownPubKeyBalance :: forall m. PollContract m => UserContractId -> m (Either PollError Value) ownPubKeyBalance cid = getMarketplaceResponseWith (Endpoint "ownPubKeyBalance") _GetPubKeyBalance (unwrap cid) ContractUnit + +createNft :: forall m. PollContract m => UserContractId -> CreateNftParams -> m (Either PollError Unit) +createNft = getMarketplaceResponseWith (Endpoint "createNft") _NftCreated <<< unwrap + +openSale :: forall m. PollContract m => UserContractId -> OpenSaleParams -> m (Either PollError Unit) +openSale = getMarketplaceResponseWith (Endpoint "openSale") _OpenedSale <<< unwrap + +buyItem :: forall m. PollContract m => UserContractId -> CloseLotParams -> m (Either PollError Unit) +buyItem = getMarketplaceResponseWith (Endpoint "buyItem") _NftBought <<< unwrap + +closeSale :: forall m. PollContract m => UserContractId -> CloseLotParams -> m (Either PollError Unit) +closeSale = getMarketplaceResponseWith (Endpoint "closeSale") _ClosedSale <<< unwrap + +startAnAuction :: forall m. PollContract m => UserContractId -> StartAnAuctionParams -> m (Either PollError Unit) +startAnAuction = getMarketplaceResponseWith (Endpoint "startAnAuction") _AuctionStarted <<< unwrap + +completeAnAuction :: forall m. PollContract m => UserContractId -> CloseLotParams -> m (Either PollError Unit) +completeAnAuction = getMarketplaceResponseWith (Endpoint "completeAnAuction") _AuctionComplete <<< unwrap + +bidOnAuction :: forall m. PollContract m => UserContractId -> BidOnAuctionParams -> m (Either PollError Unit) +bidOnAuction = getMarketplaceResponseWith (Endpoint "bidOnAuction") _BidSubmitted <<< unwrap + +bundleUp :: forall m. PollContract m => UserContractId -> BundleUpParams -> m (Either PollError Unit) +bundleUp = getMarketplaceResponseWith (Endpoint "bundleUp") _Bundled <<< unwrap + +unbundle :: forall m. PollContract m => UserContractId -> UnbundleParams -> m (Either PollError Unit) +unbundle = getMarketplaceResponseWith (Endpoint "unbundle") _Unbundled <<< unwrap diff --git a/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs b/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs index 8864098b3..64db303a2 100644 --- a/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs +++ b/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs @@ -3,7 +3,6 @@ module Component.MainPage where import Data.Route import Data.Unit import Prelude - import Business.Marketplace (getMarketplaceContracts) import Business.Marketplace as Marketplace import Business.MarketplaceInfo (InfoContractId, getInfoContractId) @@ -50,7 +49,6 @@ import Routing.Hash as Routing import Utils.BEM as BEM import View.RemoteDataState (remoteDataState) import Web.Event.Event (preventDefault) -import Web.File.File as File import Web.UIEvent.MouseEvent (MouseEvent, toEvent) type State @@ -82,7 +80,6 @@ data Action | ChooseWallet WalletSelector.Output | GetContracts | GetInstances - | HandleFile File.File component :: forall m input output. @@ -119,14 +116,9 @@ component = HH.div_ [ HH.text "Choose wallet: " , HH.slot WalletSelector._walletSelector unit WalletSelector.component st.currentInstance (Just <<< ChooseWallet) - , HH.input [ HP.type_ HP.InputFile, HE.onFileUpload f ] , pages st ] - f = case _ of - [ file ] -> Just $ HandleFile file - _ -> Nothing - handleAction :: Action -> H.HalogenM State Action Slots output m Unit handleAction = case _ of Initialize -> do @@ -158,15 +150,12 @@ component = lift $ H.modify_ _ { infoInstance = Just cid } _ -> throwError "Info contract not found" parTraverse - ( \userInstance -> do - lift $ logInfo $ "Found user instance: " <> show userInstance - userPubKey <- lift (ownPubKey userInstance) >>= either (throwError <<< show) pure - pure $ { userInstance, userPubKey } + ( \userContract -> do + lift $ logInfo $ "Found user instance: " <> show userContract + userPubKey <- lift (ownPubKey userContract) >>= either (throwError <<< show) pure + pure $ { userContract, userPubKey } ) (catMaybes <<< map getUserContractId $ contracts) - HandleFile file -> do - res <- IPFS.pinFile file - logInfo $ "uploaded file : " <> show res handleQuery :: forall a. Query a -> H.HalogenM State Action Slots output m (Maybe a) handleQuery = case _ of @@ -176,7 +165,11 @@ component = $ H.modify_ _ { route = Just route } pure (Just a) -pages :: forall m. +pages :: + forall m. + IPFS.IPFS m => + PollContract m => + MonadEffect m => LogMessages m => State -> H.ComponentHTML Action Slots m pages st = @@ -214,14 +207,20 @@ getUserPageInput st = do infoInstance <- st.infoInstance ucs <- RD.toMaybe st.userInstances userInstance <- case ucs of - [userA, userB, userC] -> Just $ case st.currentInstance of - WalletSelector.WalletA -> userA - WalletSelector.WalletB -> userB - WalletSelector.WalletC -> userC + [ userA, userB, userC ] -> + Just + $ case st.currentInstance of + WalletSelector.WalletA -> userA + WalletSelector.WalletB -> userB + WalletSelector.WalletC -> userC _ -> Nothing - pure {infoInstance, userInstance} + pure { infoInstance, userInstance } -renderUserPage :: forall m. +renderUserPage :: + forall m. + IPFS.IPFS m => + MonadEffect m => + PollContract m => LogMessages m => Maybe User.Input -> H.ComponentHTML Action Slots m renderUserPage = case _ of diff --git a/MetaLamp/nft-marketplace/client/src/Component/UserPage.purs b/MetaLamp/nft-marketplace/client/src/Component/UserPage.purs index 501afb4fe..945411258 100644 --- a/MetaLamp/nft-marketplace/client/src/Component/UserPage.purs +++ b/MetaLamp/nft-marketplace/client/src/Component/UserPage.purs @@ -1,18 +1,28 @@ module Component.UserPage where import Prelude - import Business.MarketplaceInfo (InfoContractId) import Business.MarketplaceUser (UserContractId) -import Capability.LogMessages (class LogMessages, logInfo) +import Business.MarketplaceUser as MarketplaceUser +import Capability.IPFS as IPFS +import Capability.LogMessages (class LogMessages, logError, logInfo) +import Capability.PollContract (class PollContract) import Chain.State (handleAction) +import Control.Monad.Error.Class (throwError) +import Data.Either (Either(..)) import Data.Maybe (Maybe(..)) import Data.Symbol (SProxy(..)) import Data.UserInstance (UserInstance) -import Halogen (Component, lift) +import Effect.Class (class MonadEffect) +import Effect.Exception (throw) +import Halogen (Component, lift, liftEffect) import Halogen as H import Halogen.HTML as HH +import Halogen.HTML.Events as HE +import Halogen.HTML.Properties as HP +import Plutus.Contracts.NftMarketplace.OffChain.User as MarketplaceUser import Plutus.V1.Ledger.Crypto (PubKeyHash) +import Web.File.File as File type Slot id = forall query. H.Slot query Void id @@ -33,29 +43,69 @@ type State data Action = Initialize | Reinitialize Input + | CreateNft File.File -component :: forall query output m. LogMessages m => H.Component HH.HTML query Input output m +component :: + forall query output m. + LogMessages m => + IPFS.IPFS m => + PollContract m => + MonadEffect m => + H.Component HH.HTML query Input output m component = H.mkComponent { initialState: identity , render - , eval: H.mkEval + , eval: + H.mkEval $ H.defaultEval { handleAction = handleAction , initialize = Just Initialize , receive = Just <<< Reinitialize } } - where - render :: State -> H.ComponentHTML Action () m - render _ = HH.h1_ [ HH.text "UserPage" ] - - handleAction :: forall slots. Action -> H.HalogenM State Action slots output m Unit - handleAction = case _ of - Initialize -> do - state <- H.get - logInfo $ show state - Reinitialize st -> do - H.put st - handleAction Initialize + where + render :: State -> H.ComponentHTML Action () m + render _ = + HH.div_ + [ HH.h3_ [ HH.text "Create NFT from file: " ] + , HH.input [ HP.type_ HP.InputFile, HE.onFileUpload onNftUpload ] + ] + + handleAction :: forall slots. Action -> H.HalogenM State Action slots output m Unit + handleAction = case _ of + Initialize -> do + state <- H.get + logInfo $ show state + Reinitialize st -> do + H.put st + handleAction Initialize + CreateNft file -> do + ipfsCid <- + IPFS.pinFile file + >>= case _ of + Left e -> do + let + errMsg = "could not upload to IPFS: " <> show e + logError errMsg + liftEffect $ throw errMsg + Right res -> do + logInfo $ "uploaded file to IPFS: " <> show res + pure res + contractId <- H.gets _.userInstance.userContract + resp <- + MarketplaceUser.createNft contractId + $ MarketplaceUser.CreateNftParams + { cnpIpfsCid: ipfsCid + , cnpNftName: "String" + , cnpNftDescription: "String" + , cnpNftCategory: [ "String" ] + , cnpRevealIssuer: true + } + logInfo $ "Marketplace nft created: " <> show resp + pure unit +onNftUpload :: Array File.File → Maybe Action +onNftUpload = case _ of + [ file ] -> Just $ CreateNft file + _ -> Nothing diff --git a/MetaLamp/nft-marketplace/client/src/Data/UserInstance.purs b/MetaLamp/nft-marketplace/client/src/Data/UserInstance.purs index 04e57aa2d..1f7cb76d7 100644 --- a/MetaLamp/nft-marketplace/client/src/Data/UserInstance.purs +++ b/MetaLamp/nft-marketplace/client/src/Data/UserInstance.purs @@ -3,4 +3,5 @@ module Data.UserInstance where import Business.MarketplaceUser (UserContractId) import Plutus.V1.Ledger.Crypto (PubKeyHash) -type UserInstance = { userInstance :: UserContractId, userPubKey :: PubKeyHash } +type UserInstance + = { userContract :: UserContractId, userPubKey :: PubKeyHash } From 93d5e103b40dcefdffc95ad6d106a50a5f16d6de Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Wed, 8 Sep 2021 14:41:07 +0700 Subject: [PATCH 087/217] add bytestring serialization --- MetaLamp/nft-marketplace/plutus-starter.cabal | 2 +- .../Contracts/NftMarketplace/Endpoints.hs | 9 +- .../Contracts/NftMarketplace/OffChain/ID.hs | 46 ++++---- .../NftMarketplace/OffChain/Serialization.hs | 10 ++ .../Contracts/NftMarketplace/OffChain/User.hs | 106 ++++++++---------- .../test/Marketplace/Fixtures/NFT.hs | 56 +++++---- .../test/Marketplace/Spec/Auction.hs | 20 ++-- .../test/Marketplace/Spec/CreateNft.hs | 2 +- .../test/Marketplace/Spec/Sale.hs | 20 ++-- 9 files changed, 141 insertions(+), 130 deletions(-) create mode 100644 MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Serialization.hs diff --git a/MetaLamp/nft-marketplace/plutus-starter.cabal b/MetaLamp/nft-marketplace/plutus-starter.cabal index 660f3d9cb..c0aaeb30f 100644 --- a/MetaLamp/nft-marketplace/plutus-starter.cabal +++ b/MetaLamp/nft-marketplace/plutus-starter.cabal @@ -41,7 +41,7 @@ common lang library import: lang exposed-modules: - Plutus.Contracts.NftMarketplace.OnChain.Core Ext.Plutus.PAB.Webserver.Server Plutus.Contracts.NftMarketplace.OffChain.ID Plutus.Contracts.NftMarketplace.OnChain.Core.ID Plutus.Contracts.Services.Sale.Core Plutus.Contracts.NftMarketplace.OnChain.Core.NFT Ext.Plutus.Contracts.Auction Plutus.Contracts.Services.Sale Plutus.Contracts.Services.Sale.Endpoints Plutus.Contracts.Services.Sale.StateMachine Plutus.Contracts.NftMarketplace.OffChain.Info Ext.Plutus.Ledger.Value Plutus.Contracts.NftMarketplace.OffChain.User Plutus.Abstract.ContractResponse Plutus.Contracts.NftMarketplace.Endpoints Plutus.Contracts.NftMarketplace.OffChain.Owner Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine Plutus.PAB.Simulation + Plutus.Contracts.NftMarketplace.OnChain.Core Plutus.Contracts.NftMarketplace.OffChain.Serialization Ext.Plutus.PAB.Webserver.Server Plutus.Contracts.NftMarketplace.OffChain.ID Plutus.Contracts.NftMarketplace.OnChain.Core.ID Plutus.Contracts.Services.Sale.Core Plutus.Contracts.NftMarketplace.OnChain.Core.NFT Ext.Plutus.Contracts.Auction Plutus.Contracts.Services.Sale Plutus.Contracts.Services.Sale.Endpoints Plutus.Contracts.Services.Sale.StateMachine Plutus.Contracts.NftMarketplace.OffChain.Info Ext.Plutus.Ledger.Value Plutus.Contracts.NftMarketplace.OffChain.User Plutus.Abstract.ContractResponse Plutus.Contracts.NftMarketplace.Endpoints Plutus.Contracts.NftMarketplace.OffChain.Owner Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine Plutus.PAB.Simulation build-depends: base >= 4.9 && < 5, aeson, diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/Endpoints.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/Endpoints.hs index 12987c34e..43af3c43b 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/Endpoints.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/Endpoints.hs @@ -2,7 +2,8 @@ module Plutus.Contracts.NftMarketplace.Endpoints ( module Export ) where -import Plutus.Contracts.NftMarketplace.OffChain.ID as Export -import Plutus.Contracts.NftMarketplace.OffChain.Info as Export -import Plutus.Contracts.NftMarketplace.OffChain.Owner as Export -import Plutus.Contracts.NftMarketplace.OffChain.User as Export +import Plutus.Contracts.NftMarketplace.OffChain.ID as Export +import Plutus.Contracts.NftMarketplace.OffChain.Info as Export +import Plutus.Contracts.NftMarketplace.OffChain.Owner as Export +import Plutus.Contracts.NftMarketplace.OffChain.Serialization as Export +import Plutus.Contracts.NftMarketplace.OffChain.User as Export diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/ID.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/ID.hs index ae6dbc3ea..7d833f8c8 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/ID.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/ID.hs @@ -8,51 +8,51 @@ {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeApplications #-} +{-# LANGUAGE ViewPatterns #-} module Plutus.Contracts.NftMarketplace.OffChain.ID where -import Control.Monad hiding (fmap) -import qualified Data.Aeson as J -import Data.Proxy (Proxy (..)) -import Data.Text (Text) -import qualified Data.Text as T -import qualified GHC.Generics as Haskell +import Control.Monad hiding + (fmap) +import qualified Data.Aeson as J +import Data.Proxy (Proxy (..)) +import Data.Text (Text) +import qualified Data.Text as T +import qualified GHC.Generics as Haskell import Ledger -import qualified Ledger.Typed.Scripts as Scripts +import qualified Ledger.Typed.Scripts as Scripts import Ledger.Value -import Plutus.Abstract.ContractResponse (ContractResponse, - withContractResponse) +import Plutus.Abstract.ContractResponse (ContractResponse, + withContractResponse) import Plutus.Contract import Plutus.Contract.StateMachine -import Plutus.Contracts.Currency as Currency -import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Core +import Plutus.Contracts.Currency as Currency +import Plutus.Contracts.NftMarketplace.OffChain.Serialization (deserializeByteString) +import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Core import qualified PlutusTx -import qualified PlutusTx.AssocMap as AssocMap -import PlutusTx.Prelude hiding - (Semigroup (..)) -import Prelude (Semigroup (..)) -import qualified Prelude as Haskell +import qualified PlutusTx.AssocMap as AssocMap +import PlutusTx.Prelude hiding + (Semigroup (..)) +import Prelude (Semigroup (..)) +import qualified Prelude as Haskell import qualified Schema -import Text.Printf (printf) +import Text.Printf (printf) -- type UserItemId = Either Core.IpfsCid [Core.IpfsCid] -data UserItemId = UserNftId Core.IpfsCid | UserBundleId [Core.IpfsCid] +data UserItemId = UserNftId Text | UserBundleId [Text] deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON) instance Schema.ToSchema UserItemId where toSchema = Schema.FormSchemaUnsupported "TODO how to make these instances for sum types?" -PlutusTx.unstableMakeIsData ''UserItemId -PlutusTx.makeLift ''UserItemId - toInternalId :: UserItemId -> Either Core.InternalNftId Core.InternalBundleId -toInternalId (UserNftId ipfsCid) = Left +toInternalId (UserNftId (deserializeByteString -> ipfsCid)) = Left Core.InternalNftId { Core.iniIpfsCidHash = sha2_256 ipfsCid, Core.iniIpfsCid = ipfsCid } -toInternalId (UserBundleId cids) = Right +toInternalId (UserBundleId (fmap deserializeByteString -> cids)) = Right Core.InternalBundleId { Core.ibiIpfsCids = AssocMap.fromList $ (\cid -> (sha2_256 cid, cid)) <$> cids, Core.ibiBundleId = Core.calcBundleIdHash cids diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Serialization.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Serialization.hs new file mode 100644 index 000000000..ad7195ca7 --- /dev/null +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/Serialization.hs @@ -0,0 +1,10 @@ +{-# LANGUAGE NoImplicitPrelude #-} +module Plutus.Contracts.NftMarketplace.OffChain.Serialization where + +import Data.ByteString (ByteString) +import Data.Text (Text) +import qualified Data.Text.Encoding as T +import PlutusTx.Prelude + +deserializeByteString :: Text -> BuiltinByteString +deserializeByteString = toBuiltin . T.encodeUtf8 diff --git a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs index 4cd0b4717..1d1ae2970 100644 --- a/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs +++ b/MetaLamp/nft-marketplace/src/Plutus/Contracts/NftMarketplace/OffChain/User.hs @@ -15,67 +15,70 @@ module Plutus.Contracts.NftMarketplace.OffChain.User where -import Control.Lens (_2, _Left, - _Right, (^.), - (^?)) -import qualified Control.Lens as Lens -import Control.Monad hiding (fmap) -import qualified Data.Aeson as J -import Data.Proxy (Proxy (..)) -import Data.Text (Text) -import qualified Data.Text as T -import qualified Ext.Plutus.Contracts.Auction as Auction -import Ext.Plutus.Ledger.Value (utxoValue) -import qualified GHC.Generics as Haskell +import Control.Lens (_2, + _Left, + _Right, + (^.), + (^?)) +import qualified Control.Lens as Lens +import Control.Monad hiding + (fmap) +import qualified Data.Aeson as J +import Data.Proxy (Proxy (..)) +import Data.Text (Text) +import qualified Data.Text as T +import qualified Ext.Plutus.Contracts.Auction as Auction +import Ext.Plutus.Ledger.Value (utxoValue) +import qualified GHC.Generics as Haskell import Ledger -import qualified Ledger.Typed.Scripts as Scripts +import qualified Ledger.Typed.Scripts as Scripts import Ledger.Typed.Tx -import qualified Ledger.Value as V +import qualified Ledger.Value as V import Plutus.Abstract.ContractResponse import Plutus.Contract import Plutus.Contract.StateMachine -import Plutus.Contracts.Currency as Currency +import Plutus.Contracts.Currency as Currency import Plutus.Contracts.NftMarketplace.OffChain.ID import Plutus.Contracts.NftMarketplace.OffChain.Info -import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Core -import qualified Plutus.Contracts.Services.Sale as Sale +import Plutus.Contracts.NftMarketplace.OffChain.Serialization (deserializeByteString) +import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Core +import qualified Plutus.Contracts.Services.Sale as Sale import qualified PlutusTx -import qualified PlutusTx.AssocMap as AssocMap -import PlutusTx.Prelude hiding - (Semigroup (..)) -import Prelude (Semigroup (..)) -import qualified Prelude as Haskell +import qualified PlutusTx.AssocMap as AssocMap +import PlutusTx.Prelude hiding + (Semigroup (..)) +import Prelude (Semigroup (..)) +import qualified Prelude as Haskell import qualified Schema -import Text.Printf (printf) +import Text.Printf (printf) getOwnPubKey :: Contract w s Text PubKeyHash getOwnPubKey = pubKeyHash <$> ownPubKey data CreateNftParams = CreateNftParams { - cnpIpfsCid :: BuiltinByteString, - cnpNftName :: BuiltinByteString, - cnpNftDescription :: BuiltinByteString, - cnpNftCategory :: Core.Category, + cnpIpfsCid :: Text, + cnpNftName :: Text, + cnpNftDescription :: Text, + cnpNftCategory :: [Text], cnpRevealIssuer :: Bool } deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON, Schema.ToSchema) -PlutusTx.unstableMakeIsData ''CreateNftParams -PlutusTx.makeLift ''CreateNftParams Lens.makeClassy_ ''CreateNftParams -- | The user specifies which NFT to mint and add to marketplace store, -- he gets it into his wallet and the corresponding store entry is created createNft :: Core.Marketplace -> CreateNftParams -> Contract w s Text () createNft marketplace CreateNftParams {..} = do - let ipfsCidHash = sha2_256 cnpIpfsCid + let ipfsCid = deserializeByteString cnpIpfsCid + let ipfsCidHash = sha2_256 ipfsCid nftStore <- Core.mdSingletons <$> marketplaceStore marketplace when (isJust $ AssocMap.lookup ipfsCidHash nftStore) $ throwError "Nft entry already exists" pkh <- getOwnPubKey - let tokenName = V.TokenName cnpIpfsCid + let tokenName = V.TokenName ipfsCid nft <- mapError (T.pack . Haskell.show @Currency.CurrencyError) $ Currency.mintContract pkh [(tokenName, 1)] @@ -83,9 +86,9 @@ createNft marketplace CreateNftParams {..} = do let client = Core.marketplaceClient marketplace let nftEntry = Core.NftInfo { niCurrency = Currency.currencySymbol nft - , niName = cnpNftName - , niDescription = cnpNftDescription - , niCategory = cnpNftCategory + , niName = deserializeByteString cnpNftName + , niDescription = deserializeByteString cnpNftDescription + , niCategory = deserializeByteString <$> cnpNftCategory , niIssuer = if cnpRevealIssuer then Just pkh else Nothing } void $ mapError' $ runStep client $ Core.CreateNftRedeemer ipfsCidHash nftEntry @@ -101,8 +104,6 @@ data OpenSaleParams = deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON, Schema.ToSchema) -PlutusTx.unstableMakeIsData ''OpenSaleParams -PlutusTx.makeLift ''OpenSaleParams Lens.makeClassy_ ''OpenSaleParams -- | The user opens sale for his NFT @@ -136,8 +137,6 @@ data CloseLotParams = deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON, Schema.ToSchema) -PlutusTx.unstableMakeIsData ''CloseLotParams -PlutusTx.makeLift ''CloseLotParams Lens.makeClassy_ ''CloseLotParams -- | The user buys specified NFT lot @@ -198,8 +197,6 @@ data StartAnAuctionParams = deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON, Schema.ToSchema) -PlutusTx.unstableMakeIsData ''StartAnAuctionParams -PlutusTx.makeLift ''StartAnAuctionParams Lens.makeClassy_ ''StartAnAuctionParams -- | The user starts an auction for specified NFT @@ -258,8 +255,6 @@ data BidOnAuctionParams = deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON, Schema.ToSchema) -PlutusTx.unstableMakeIsData ''BidOnAuctionParams -PlutusTx.makeLift ''BidOnAuctionParams Lens.makeClassy_ ''BidOnAuctionParams -- | The user submits a bid on the auction for specified NFT @@ -286,29 +281,28 @@ bidOnAuction marketplace BidOnAuctionParams {..} = do data BundleUpParams = BundleUpParams { - bupIpfsCids :: [BuiltinByteString], - bupName :: BuiltinByteString, - bupDescription :: BuiltinByteString, - bupCategory :: Core.Category + bupIpfsCids :: [Text], + bupName :: Text, + bupDescription :: Text, + bupCategory :: [Text] } deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON, Schema.ToSchema) -PlutusTx.unstableMakeIsData ''BundleUpParams -PlutusTx.makeLift ''BundleUpParams Lens.makeClassy_ ''BundleUpParams -- | The user creates a bundle from specified NFTs bundleUp :: forall w s. Core.Marketplace -> BundleUpParams -> Contract w s Text () bundleUp marketplace BundleUpParams {..} = do - let bundleId = Core.calcBundleIdHash bupIpfsCids + let ipfsCids = deserializeByteString <$> bupIpfsCids + let bundleId = Core.calcBundleIdHash ipfsCids bundles <- Core.mdBundles <$> marketplaceStore marketplace when (isJust $ AssocMap.lookup bundleId bundles) $ throwError "Bundle entry already exists" - let nftIds = sha2_256 <$> bupIpfsCids + let nftIds = sha2_256 <$> ipfsCids let bundleInfo = Core.BundleInfo - { biName = bupName - , biDescription = bupDescription - , biCategory = bupCategory + { biName = deserializeByteString bupName + , biDescription = deserializeByteString bupDescription + , biCategory = deserializeByteString <$> bupCategory } let client = Core.marketplaceClient marketplace @@ -319,19 +313,17 @@ bundleUp marketplace BundleUpParams {..} = do data UnbundleParams = UnbundleParams { - upIpfsCids :: [BuiltinByteString] + upIpfsCids :: [Text] } deriving stock (Haskell.Eq, Haskell.Show, Haskell.Generic) deriving anyclass (J.ToJSON, J.FromJSON, Schema.ToSchema) -PlutusTx.unstableMakeIsData ''UnbundleParams -PlutusTx.makeLift ''UnbundleParams Lens.makeClassy_ ''UnbundleParams -- | The user unbundles specified NFTs unbundle :: Core.Marketplace -> UnbundleParams -> Contract w s Text () unbundle marketplace UnbundleParams {..} = do - let bundleId = Core.calcBundleIdHash upIpfsCids + let bundleId = Core.calcBundleIdHash $ fmap deserializeByteString upIpfsCids bundles <- Core.mdBundles <$> marketplaceStore marketplace when (isNothing $ AssocMap.lookup bundleId bundles) $ throwError "Bundle entry does not exist" diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/NFT.hs b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/NFT.hs index cf56f2f70..9a87eb3a7 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/NFT.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Fixtures/NFT.hs @@ -5,73 +5,81 @@ module Marketplace.Fixtures.NFT where +import Data.Text (Text) +import qualified Plutus.Contracts.NftMarketplace.Endpoints as Marketplace import qualified Plutus.Contracts.NftMarketplace.OnChain.Core as Marketplace import PlutusTx.Builtins (sha2_256) import PlutusTx.Prelude (BuiltinByteString) -cids :: [Marketplace.IpfsCid] +cids :: [Text] cids = [catTokenIpfsCid, photoTokenIpfsCid] bundleId :: Marketplace.BundleId -bundleId = Marketplace.calcBundleIdHash cids +bundleId = Marketplace.calcBundleIdHash $ fmap Marketplace.deserializeByteString cids bundleInfo :: Marketplace.BundleInfo bundleInfo = Marketplace.BundleInfo - { biName = bundleName - , biDescription = bundleDescription - , biCategory = bundleCategory + { biName = Marketplace.deserializeByteString bundleName + , biDescription = Marketplace.deserializeByteString bundleDescription + , biCategory = Marketplace.deserializeByteString <$> bundleCategory } -bundleName :: BuiltinByteString +bundleName :: Text bundleName = "Picture gallery" -bundleDescription :: BuiltinByteString +bundleDescription :: Text bundleDescription = "Collection of visual media" -bundleCategory :: Marketplace.Category +bundleCategory :: [Text] bundleCategory = ["User","Stan"] -catTokenIpfsCid :: Marketplace.IpfsCid +catTokenIpfsCid :: Text catTokenIpfsCid = "QmPeoJnaDttpFrSySYBY3reRFCzL3qv4Uiqz376EBv9W16" +catTokenIpfsCidBs :: BuiltinByteString +catTokenIpfsCidBs = "QmPeoJnaDttpFrSySYBY3reRFCzL3qv4Uiqz376EBv9W16" + catTokenIpfsCidHash :: Marketplace.IpfsCidHash -catTokenIpfsCidHash = sha2_256 catTokenIpfsCid +catTokenIpfsCidHash = sha2_256 $ Marketplace.deserializeByteString catTokenIpfsCid -catTokenName :: BuiltinByteString +catTokenName :: Text catTokenName = "Cat token" -catTokenDescription :: BuiltinByteString +catTokenDescription :: Text catTokenDescription = "A picture of a cat on a pogo stick" -catTokenCategory :: Marketplace.Category +catTokenCategory :: [Text] catTokenCategory = ["GIFs"] hasCatTokenRecord :: Marketplace.NftInfo -> Bool hasCatTokenRecord Marketplace.NftInfo {..} = - niCategory == catTokenCategory && - niName == catTokenName && - niDescription == catTokenDescription + niCategory == (Marketplace.deserializeByteString <$> catTokenCategory) && + niName == (Marketplace.deserializeByteString catTokenName) && + niDescription == (Marketplace.deserializeByteString catTokenDescription) -photoTokenIpfsCid :: Marketplace.IpfsCid +photoTokenIpfsCid :: Text photoTokenIpfsCid = "QmeSFBsEZ7XtK7yv5CQ79tqFnH9V2jhFhSSq1LV5W3kuiB" +photoTokenIpfsCidBs :: BuiltinByteString +photoTokenIpfsCidBs = "QmeSFBsEZ7XtK7yv5CQ79tqFnH9V2jhFhSSq1LV5W3kuiB" + photoTokenIpfsCidHash :: Marketplace.IpfsCidHash -photoTokenIpfsCidHash = sha2_256 photoTokenIpfsCid +photoTokenIpfsCidHash = sha2_256 $ Marketplace.deserializeByteString photoTokenIpfsCid -photoTokenName :: BuiltinByteString +photoTokenName :: Text photoTokenName = "Photo token" -photoTokenDescription :: BuiltinByteString +photoTokenDescription :: Text photoTokenDescription = "A picture of a sunset" -photoTokenCategory :: Marketplace.Category +photoTokenCategory :: [Text] photoTokenCategory = ["Photos"] hasPhotoTokenRecord :: Marketplace.NftInfo -> Bool hasPhotoTokenRecord Marketplace.NftInfo {..} = - niCategory == photoTokenCategory && - niName == photoTokenName && - niDescription == photoTokenDescription + niCategory == (Marketplace.deserializeByteString <$> photoTokenCategory) && + niName == (Marketplace.deserializeByteString photoTokenName) && + niDescription == (Marketplace.deserializeByteString photoTokenDescription) oneAdaInLovelace :: Integer oneAdaInLovelace = 1000000 diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Auction.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Auction.hs index 564f64b67..8f74dc9bc 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Auction.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Auction.hs @@ -198,7 +198,7 @@ startAnAuctionDatumsCheck = (Utils.checkOneDatum (nftIsOnAuction . Marketplace.mdSingletons)) where nftIsOnAuction = maybe False (\t -> t ^. Marketplace._nftLot ^? traverse . _2 . _Right & fmap auctionValue & - (== Just (Marketplace.nftValue Fixtures.catTokenIpfsCid t))) . + (== Just (Marketplace.nftValue Fixtures.catTokenIpfsCidBs t))) . AssocMap.lookup Fixtures.catTokenIpfsCidHash completeAuctionDatumsCheck :: TracePredicate @@ -216,7 +216,7 @@ startAnAuctionValueCheck = (walletAddress Fixtures.userWallet) (isNothing . find hasNft . V.flattenValue) where - hasNft v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCid + hasNft v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCidBs completeAnAuctionValueCheck :: TracePredicate completeAnAuctionValueCheck = @@ -224,7 +224,7 @@ completeAnAuctionValueCheck = (walletAddress Fixtures.userWallet) (Utils.one hasNft . V.flattenValue) where - hasNft v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCid + hasNft v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCidBs buyOnAuctionValueCheck :: TracePredicate buyOnAuctionValueCheck = @@ -232,7 +232,7 @@ buyOnAuctionValueCheck = (walletAddress Fixtures.buyerWallet) (Utils.one hasNft . V.flattenValue) where - hasNft v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCid + hasNft v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCidBs errorCheckUser :: TracePredicate errorCheckUser = Utils.assertCrError (Marketplace.userEndpoints Fixtures.marketplace) (Trace.walletInstanceTag Fixtures.userWallet) @@ -331,8 +331,8 @@ startAnAuctionValueCheckB = (walletAddress Fixtures.userWallet) $ \v -> (isNothing . find hasCatToken . V.flattenValue $ v) && (isNothing . find hasPhotoToken . V.flattenValue $ v) where - hasCatToken v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCid - hasPhotoToken v = (v ^. _2 & V.unTokenName) == Fixtures.photoTokenIpfsCid + hasCatToken v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCidBs + hasPhotoToken v = (v ^. _2 & V.unTokenName) == Fixtures.photoTokenIpfsCidBs completeAnAuctionValueCheckB :: TracePredicate completeAnAuctionValueCheckB = @@ -340,8 +340,8 @@ completeAnAuctionValueCheckB = (walletAddress Fixtures.userWallet) $ \v -> (Utils.one hasCatToken . V.flattenValue $ v) && (Utils.one hasPhotoToken . V.flattenValue $ v) where - hasCatToken v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCid - hasPhotoToken v = (v ^. _2 & V.unTokenName) == Fixtures.photoTokenIpfsCid + hasCatToken v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCidBs + hasPhotoToken v = (v ^. _2 & V.unTokenName) == Fixtures.photoTokenIpfsCidBs buyOnAuctionValueCheckB :: TracePredicate buyOnAuctionValueCheckB = @@ -349,5 +349,5 @@ buyOnAuctionValueCheckB = (walletAddress Fixtures.buyerWallet) $ \v -> (Utils.one hasCatToken . V.flattenValue $ v) && (Utils.one hasPhotoToken . V.flattenValue $ v) where - hasCatToken v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCid - hasPhotoToken v = (v ^. _2 & V.unTokenName) == Fixtures.photoTokenIpfsCid + hasCatToken v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCidBs + hasPhotoToken v = (v ^. _2 & V.unTokenName) == Fixtures.photoTokenIpfsCidBs diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/CreateNft.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/CreateNft.hs index 17b0ce161..0c9e03814 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Spec/CreateNft.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/CreateNft.hs @@ -95,4 +95,4 @@ valueCheck = (walletAddress Fixtures.userWallet) (Utils.one hasNft . V.flattenValue) where - hasNft v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCid + hasNft v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCidBs diff --git a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Sale.hs b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Sale.hs index f67b73e42..10b688fc5 100644 --- a/MetaLamp/nft-marketplace/test/Marketplace/Spec/Sale.hs +++ b/MetaLamp/nft-marketplace/test/Marketplace/Spec/Sale.hs @@ -169,7 +169,7 @@ openSaleDatumsCheck = (Utils.checkOneDatum (nftIsOnSale . Marketplace.mdSingletons)) where nftIsOnSale = maybe False (\t -> t ^. Marketplace._nftLot ^? traverse . _2 . _Left & fmap Sale.saleValue & - (== Just (Marketplace.nftValue Fixtures.catTokenIpfsCid t))) . + (== Just (Marketplace.nftValue Fixtures.catTokenIpfsCidBs t))) . AssocMap.lookup Fixtures.catTokenIpfsCidHash completeSaleDatumsCheck :: TracePredicate @@ -187,7 +187,7 @@ openSaleValueCheck = (walletAddress Fixtures.userWallet) (isNothing . find hasNft . V.flattenValue) where - hasNft v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCid + hasNft v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCidBs closeSaleValueCheck :: TracePredicate closeSaleValueCheck = @@ -195,7 +195,7 @@ closeSaleValueCheck = (walletAddress Fixtures.userWallet) (Utils.one hasNft . V.flattenValue) where - hasNft v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCid + hasNft v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCidBs buyItemValueCheck :: TracePredicate buyItemValueCheck = @@ -203,7 +203,7 @@ buyItemValueCheck = (walletAddress Fixtures.buyerWallet) (Utils.one hasNft . V.flattenValue) where - hasNft v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCid + hasNft v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCidBs errorCheckUser :: TracePredicate errorCheckUser = Utils.assertCrError (Marketplace.userEndpoints Fixtures.marketplace) (Trace.walletInstanceTag Fixtures.userWallet) @@ -286,8 +286,8 @@ openSaleValueCheckB = (walletAddress Fixtures.userWallet) $ \v -> (isNothing . find hasCatToken . V.flattenValue $ v) && (isNothing . find hasPhotoToken . V.flattenValue $ v) where - hasCatToken v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCid - hasPhotoToken v = (v ^. _2 & V.unTokenName) == Fixtures.photoTokenIpfsCid + hasCatToken v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCidBs + hasPhotoToken v = (v ^. _2 & V.unTokenName) == Fixtures.photoTokenIpfsCidBs closeSaleValueCheckB :: TracePredicate closeSaleValueCheckB = @@ -295,8 +295,8 @@ closeSaleValueCheckB = (walletAddress Fixtures.userWallet) $ \v -> (Utils.one hasCatToken . V.flattenValue $ v) && (Utils.one hasPhotoToken . V.flattenValue $ v) where - hasCatToken v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCid - hasPhotoToken v = (v ^. _2 & V.unTokenName) == Fixtures.photoTokenIpfsCid + hasCatToken v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCidBs + hasPhotoToken v = (v ^. _2 & V.unTokenName) == Fixtures.photoTokenIpfsCidBs buyItemValueCheckB :: TracePredicate buyItemValueCheckB = @@ -304,5 +304,5 @@ buyItemValueCheckB = (walletAddress Fixtures.buyerWallet) $ \v -> (Utils.one hasCatToken . V.flattenValue $ v) && (Utils.one hasPhotoToken . V.flattenValue $ v) where - hasCatToken v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCid - hasPhotoToken v = (v ^. _2 & V.unTokenName) == Fixtures.photoTokenIpfsCid + hasCatToken v = (v ^. _2 & V.unTokenName) == Fixtures.catTokenIpfsCidBs + hasPhotoToken v = (v ^. _2 & V.unTokenName) == Fixtures.photoTokenIpfsCidBs From 748697f8958bf33621ba2b15357113b5c4827e6c Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Wed, 8 Sep 2021 19:05:29 +0700 Subject: [PATCH 088/217] add create nft form --- MetaLamp/nft-marketplace/client/spago.dhall | 1 + .../client/src/Component/CreateNftForm.purs | 216 ++++++++++++++++++ .../client/src/Component/MainPage.purs | 5 + .../client/src/Component/UserPage.purs | 14 +- .../client/src/Utils/FormValidation.purs | 122 ++++++++++ .../client/src/View/FormElement.purs | 190 +++++++++++++++ 6 files changed, 546 insertions(+), 2 deletions(-) create mode 100644 MetaLamp/nft-marketplace/client/src/Component/CreateNftForm.purs create mode 100644 MetaLamp/nft-marketplace/client/src/Utils/FormValidation.purs create mode 100644 MetaLamp/nft-marketplace/client/src/View/FormElement.purs diff --git a/MetaLamp/nft-marketplace/client/spago.dhall b/MetaLamp/nft-marketplace/client/spago.dhall index 136f8c5ef..aedb337cc 100644 --- a/MetaLamp/nft-marketplace/client/spago.dhall +++ b/MetaLamp/nft-marketplace/client/spago.dhall @@ -30,6 +30,7 @@ You can edit this file as you like. , "web-socket" , "routing" , "routing-duplex" + , "halogen-formless" ] , packages = ./packages.dhall , sources = diff --git a/MetaLamp/nft-marketplace/client/src/Component/CreateNftForm.purs b/MetaLamp/nft-marketplace/client/src/Component/CreateNftForm.purs new file mode 100644 index 000000000..a29b808c4 --- /dev/null +++ b/MetaLamp/nft-marketplace/client/src/Component/CreateNftForm.purs @@ -0,0 +1,216 @@ +module Component.CreateNftForm where + +import Prelude + +import Data.Array (filter, snoc, catMaybes) +import Data.Const (Const) +import Data.Either (Either(..)) +import Data.List (toUnfoldable) +import Data.Map as M +import Data.Maybe (Maybe(..), fromMaybe) +import Data.Newtype (class Newtype) +import Data.Symbol (SProxy(..)) +import Effect.Aff.Class (class MonadAff) +import Formless as F +import Halogen as H +import Halogen.HTML as HH +import Halogen.HTML.Events as HE +import Halogen.HTML.Properties as HP +import Utils.FormValidation as V +import View.FormElement (class_) +import View.FormElement as UI + +----- +-- Event form + +-- Form types + +type Event = { | EventRow F.OutputType } + +newtype EventForm r f = EventForm (r (EventRow f)) +derive instance newtypeEventForm :: Newtype (EventForm r f) _ + +type EventRow f = + ( name :: f Void String String + , description :: f Void String String + , subcategories :: f V.FieldError (Maybe (Array CategoryInfo)) (Array CategoryInfo) + ) + +-- Form component types + +type Slot = + H.Slot (F.Query EventForm (Const Void) ChildSlots) Event + +type State = + ( formIds :: Array Int + , nextId :: Int + ) + +data Action + = AddCategoryForm + | SubmitAll + | HandleCategoryForm Int CategoryFormMessage + +type ChildSlots = + ( categoryForm :: CategoryFormSlot Int ) + +-- Form spec + +eventComponent :: forall m. + MonadAff m => + F.Component EventForm (Const Void) ChildSlots Unit Event m +eventComponent = F.component (const eventFormInput) $ F.defaultSpec + { render = render + , handleAction = handleAction + , handleEvent = handleEvent + } + where + eventFormInput :: F.Input EventForm State m + eventFormInput = + { validators: EventForm + { name: F.noValidation + , description: F.noValidation + , subcategories: F.hoistFn_ (fromMaybe []) + } + , initialInputs: Nothing + , formIds: [] + , nextId: 0 + } + + handleAction = case _ of + HandleCategoryForm ix Destroy -> do + H.modify_ \st -> st { formIds = filter (_ /= ix) st.formIds } + eval $ F.set _subcategories Nothing + + AddCategoryForm -> + H.modify_ \st -> st + { nextId = st.nextId + 1, formIds = st.formIds `snoc` st.nextId } + + SubmitAll -> do + st <- H.get + res <- H.queryAll _categoryForm $ H.request F.submitReply + case map F.unwrapOutputFields $ catMaybes $ toUnfoldable $ M.values res of + [] -> eval F.submit + subcategories -> eval (F.set _subcategories (Just subcategories)) *> eval F.submit + + where + eval act = F.handleAction handleAction handleEvent act + _subcategories = SProxy :: _ "subcategories" + _categoryForm = SProxy :: _ "categoryForm" + + handleEvent = case _ of + F.Submitted outputs -> + H.raise (F.unwrapOutputFields outputs) + _ -> pure unit + + render st = + HH.div_ + [ HH.div + [ class_ "field is-grouped" ] + [ HH.div + [ class_ "control" ] + [ UI.button + [ HE.onClick \_ -> Just $ F.injAction AddCategoryForm ] + [ HH.text "Add Category Form" ] + ] + , HH.div + [ class_ "control" ] + [ UI.buttonPrimary + [ HE.onClick \_ -> Just $ F.injAction SubmitAll ] + [ HH.text "Submit" ] + ] + ] + , UI.input + { label: "NFT Name" + , help: Right + "Provide an NFT name" + , placeholder: "My NFT" + } + [ HP.value $ F.getInput _name st.form + , HE.onValueInput $ Just <<< F.setValidate _name + ] + , UI.input + { label: "NFT Description" + , help: Right + "Provide an NFT description" + , placeholder: "Beautiful artwork" + } + [ HP.value $ F.getInput _description st.form + , HE.onValueInput $ Just <<< F.setValidate _description + ] + , HH.div_ + (mkCategoryForm <$> st.formIds) + ] + where + mkCategoryForm i = do + let handler = Just <<< F.injAction <<< HandleCategoryForm i + HH.slot _categoryForm i categoryFormComponent unit handler + + _name = SProxy :: SProxy "name" + _description = SProxy :: SProxy "description" + _categoryForm = SProxy :: SProxy "categoryForm" + + +----- +-- Category form, nested inside +----- + +-- Form types + +type CategoryInfo = { | CategoryRow F.OutputType } + +newtype CategoryForm r f = CategoryForm (r (CategoryRow f)) +derive instance newtypeCategoryForm :: Newtype (CategoryForm r f) _ + +type CategoryRow f = + ( category :: f Void String String + ) + +-- Form component types + +type CategoryFormSlot = + H.Slot (F.Query' CategoryForm) CategoryFormMessage + +data CategoryFormAction = RemoveMe +data CategoryFormMessage = Destroy + +-- Form spec + +categoryFormComponent :: forall m. + MonadAff m => F.Component CategoryForm (Const Void) () Unit CategoryFormMessage m +categoryFormComponent = F.component (const categoryFormInput) $ F.defaultSpec + { render = render + , handleAction = handleAction + } + where + categoryFormInput :: F.Input' CategoryForm m + categoryFormInput = + { validators: CategoryForm + { category: F.noValidation + } + , initialInputs: Nothing + } + + handleAction = case _ of + RemoveMe -> H.raise Destroy + + render st = + UI.formContent_ + [ HH.div + [ class_ "field" ] + [ UI.buttonPrimary + [ HE.onClick \_ -> Just $ F.injAction RemoveMe ] + [ HH.text "Remove Me" ] + ] + + , UI.input + { label: "Inner category" + , help: Right "Provide any additional category you'd like." + , placeholder: "photos" + } + [ HP.value $ F.getInput _category st.form + , HE.onValueInput $ Just <<< F.set _category + ] + ] + where + _category = SProxy :: SProxy "category" diff --git a/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs b/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs index 64db303a2..f6efe3dfe 100644 --- a/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs +++ b/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs @@ -3,6 +3,7 @@ module Component.MainPage where import Data.Route import Data.Unit import Prelude + import Business.Marketplace (getMarketplaceContracts) import Business.Marketplace as Marketplace import Business.MarketplaceInfo (InfoContractId, getInfoContractId) @@ -30,6 +31,7 @@ import Data.Maybe (Maybe(..), fromMaybe, maybe) import Data.Symbol (SProxy(..)) import Data.Tuple (Tuple(..)) import Data.UserInstance (UserInstance) +import Effect.Aff.Class (class MonadAff) import Effect.Class (class MonadEffect) import Halogen as H import Halogen.HTML as HH @@ -85,6 +87,7 @@ component :: forall m input output. MonadEffect m => Navigate m => + MonadAff m => LogMessages m => PollContract m => IPFS.IPFS m => @@ -170,6 +173,7 @@ pages :: IPFS.IPFS m => PollContract m => MonadEffect m => + MonadAff m => LogMessages m => State -> H.ComponentHTML Action Slots m pages st = @@ -220,6 +224,7 @@ renderUserPage :: forall m. IPFS.IPFS m => MonadEffect m => + MonadAff m => PollContract m => LogMessages m => Maybe User.Input -> H.ComponentHTML Action Slots m diff --git a/MetaLamp/nft-marketplace/client/src/Component/UserPage.purs b/MetaLamp/nft-marketplace/client/src/Component/UserPage.purs index 945411258..1cbb599fa 100644 --- a/MetaLamp/nft-marketplace/client/src/Component/UserPage.purs +++ b/MetaLamp/nft-marketplace/client/src/Component/UserPage.purs @@ -1,6 +1,7 @@ module Component.UserPage where import Prelude + import Business.MarketplaceInfo (InfoContractId) import Business.MarketplaceUser (UserContractId) import Business.MarketplaceUser as MarketplaceUser @@ -8,11 +9,13 @@ import Capability.IPFS as IPFS import Capability.LogMessages (class LogMessages, logError, logInfo) import Capability.PollContract (class PollContract) import Chain.State (handleAction) +import Component.CreateNftForm as CreateNftForm import Control.Monad.Error.Class (throwError) import Data.Either (Either(..)) import Data.Maybe (Maybe(..)) import Data.Symbol (SProxy(..)) import Data.UserInstance (UserInstance) +import Effect.Aff.Class (class MonadAff) import Effect.Class (class MonadEffect) import Effect.Exception (throw) import Halogen (Component, lift, liftEffect) @@ -44,6 +47,10 @@ data Action = Initialize | Reinitialize Input | CreateNft File.File + | HandleEventForm CreateNftForm.Event + +type Slots = + ( eventForm :: CreateNftForm.Slot Unit ) component :: forall query output m. @@ -51,6 +58,7 @@ component :: IPFS.IPFS m => PollContract m => MonadEffect m => + MonadAff m => H.Component HH.HTML query Input output m component = H.mkComponent @@ -65,14 +73,15 @@ component = } } where - render :: State -> H.ComponentHTML Action () m + render :: State -> H.ComponentHTML Action Slots m render _ = HH.div_ [ HH.h3_ [ HH.text "Create NFT from file: " ] , HH.input [ HP.type_ HP.InputFile, HE.onFileUpload onNftUpload ] + , HH.slot (SProxy :: _ "eventForm") unit CreateNftForm.eventComponent unit (Just <<< HandleEventForm) ] - handleAction :: forall slots. Action -> H.HalogenM State Action slots output m Unit + handleAction :: Action -> H.HalogenM State Action Slots output m Unit handleAction = case _ of Initialize -> do state <- H.get @@ -104,6 +113,7 @@ component = } logInfo $ "Marketplace nft created: " <> show resp pure unit + HandleEventForm event -> logInfo $ show event onNftUpload :: Array File.File → Maybe Action onNftUpload = case _ of diff --git a/MetaLamp/nft-marketplace/client/src/Utils/FormValidation.purs b/MetaLamp/nft-marketplace/client/src/Utils/FormValidation.purs new file mode 100644 index 000000000..1ae95444f --- /dev/null +++ b/MetaLamp/nft-marketplace/client/src/Utils/FormValidation.purs @@ -0,0 +1,122 @@ +module Utils.FormValidation where + +import Prelude + +import Data.Either (Either(..)) +import Data.Foldable (length) as Foldable +import Data.Generic.Rep (class Generic) +import Data.Generic.Rep.Show (genericShow) +import Data.Int (fromString) as Int +import Data.Lens (preview) +import Data.Maybe (Maybe, maybe) +import Data.Newtype (class Newtype) +import Data.String (contains, length, null) +import Data.String.Pattern (Pattern(..)) +import Effect.Aff (Milliseconds(..), delay) +import Effect.Aff.Class (class MonadAff, liftAff) +import Formless (FormFieldResult, _Error) +import Formless.Validation (Validation(..), hoistFnE_) + +data FieldError + = EmptyField + | InvalidEmail + | EmailInUse + | TooShort Int + | TooLong Int + | InvalidInt String + | NotEqual String String + | NotEnoughMoney + +derive instance genericFieldError :: Generic FieldError _ +instance showFieldError :: Show FieldError where + show = genericShow + +instance toTextFieldError :: ToText FieldError where + toText EmptyField = "This field is required." + toText InvalidEmail = "That email is not valid." + toText EmailInUse = "That email is already being used." + toText (TooShort n) = "You must enter at least " <> show n <> " characters." + toText (TooLong n) = "You must enter less than " <> show n <> " characters." + toText (InvalidInt str) = "Could not parse \"" <> str <> "\" to a valid integer." + toText (NotEqual str0 str1) = "This field contains \"" <> str1 <> "\" but must be equal to \"" <> str0 <> "\" to validate." + toText (NotEnoughMoney) = "You don't have that much money." + +-- | Some useful types we'll parse to +newtype Name = Name String +derive instance newtypeName :: Newtype Name _ +derive newtype instance showName :: Show Name + +newtype Email = Email String +derive instance newtypeEmail :: Newtype Email _ +derive newtype instance eqEmail :: Eq Email +derive newtype instance showEmail :: Show Email + +-- | Unpacks errors to render as a string +showError :: ∀ e o. ToText e => FormFieldResult e o -> Maybe String +showError = map toText <<< preview _Error + +class ToText item where + toText :: item -> String + +instance toTextString :: ToText String where + toText = identity + +-------------------- +-- Formless Validation +-------------------- + +emailFormat :: ∀ form m. Monad m => Validation form m FieldError String Email +emailFormat = hoistFnE_ $ \str -> + if contains (Pattern "@") str + then pure $ Email str + else Left InvalidEmail + +minLength :: ∀ form m. Monad m => Int -> Validation form m FieldError String String +minLength n = hoistFnE_ $ \str -> + let n' = length str + in if n' < n then Left (TooShort n) else Right str + +-- | The opposite of minLength. +maxLength :: ∀ form m. Monad m => Int -> Validation form m FieldError String String +maxLength n = hoistFnE_ \str -> + let n' = length str + in if n' > n then Left (TooLong n) else Right str + +exists :: ∀ form m a. Monad m => Validation form m FieldError (Maybe a) a +exists = hoistFnE_ $ maybe (Left EmptyField) Right + +strIsInt :: ∀ form m. Monad m => Validation form m FieldError String Int +strIsInt = hoistFnE_ $ \str -> maybe (Left $ InvalidInt str) Right (Int.fromString str) + +nonEmptyArray :: ∀ form m a. Monad m => Validation form m FieldError (Array a) (Array a) +nonEmptyArray = hoistFnE_ \arr -> + if Foldable.length arr > 0 + then Right arr + else Left EmptyField + +-- | Validate that an input string is not empty +nonEmptyStr :: ∀ form m. Monad m => Validation form m FieldError String String +nonEmptyStr = hoistFnE_ $ \str -> + if null str + then Left EmptyField + else Right str + +-------------------- +-- Formless Async Validation +-------------------- + +emailIsUsed :: ∀ form m. MonadAff m => Validation form m FieldError Email Email +emailIsUsed = Validation \_ e@(Email e') -> do + -- Perhaps we hit the server to if the email is in use + _ <- liftAff $ delay $ Milliseconds 1000.0 + pure $ if (contains (Pattern "t") e') + then Left EmailInUse + else pure e + +enoughMoney :: ∀ form m. MonadAff m => Validation form m FieldError Int Int +enoughMoney = Validation \_ i -> do + -- Let's check if we have enough money... + _ <- liftAff $ delay $ Milliseconds 5000.0 + pure $ if (i > 1000) + then Left NotEnoughMoney + else pure i \ No newline at end of file diff --git a/MetaLamp/nft-marketplace/client/src/View/FormElement.purs b/MetaLamp/nft-marketplace/client/src/View/FormElement.purs new file mode 100644 index 000000000..834133756 --- /dev/null +++ b/MetaLamp/nft-marketplace/client/src/View/FormElement.purs @@ -0,0 +1,190 @@ +module View.FormElement where + +import Prelude + +import DOM.HTML.Indexed (HTMLa, HTMLbutton, HTMLinput, HTMLtextarea) +import DOM.HTML.Indexed.InputType (InputType(..)) +import Data.Either (Either(..), either) +import Data.Maybe (Maybe(..), maybe) +import Data.Newtype (class Newtype) +import Data.Symbol (class IsSymbol, SProxy(..)) +import Data.Variant (Variant) +import Utils.FormValidation (class ToText, toText) +import Utils.FormValidation as V +import Formless (FormFieldResult(..)) +import Formless as F +import Halogen.HTML as HH +import Halogen.HTML.Events as HE +import Halogen.HTML.Properties as HP +import Prim.Row (class Cons) +import Record.Builder as Builder +import Web.Event.Event (Event) +import Web.UIEvent.FocusEvent (FocusEvent) + +type Plain i p = Array (HH.HTML i p) -> HH.HTML i p + +class_ :: forall r t. String -> HH.IProp ( "class" :: String | r ) t +class_ = HP.class_ <<< HH.ClassName + +---------- +-- Typography + +h1_ :: forall i p. Plain i p +h1_ = HH.h1 [ class_ "title" ] + +h2_ :: forall i p. Plain i p +h2_ = HH.h2 [ class_ "subtitle is-size-4 has-text-grey" ] + +p_ :: forall i p. String -> HH.HTML i p +p_ str = HH.p_ [ HH.text str ] + +a :: forall i p. Array (HH.IProp HTMLa p) -> Plain i p +a props = HH.a ([ class_ "has-text-blue" ] <> props) + +---------- +-- Layout + +section_ :: forall i p. Plain i p +section_ content = + HH.section + [ class_ "section columns" ] + [ HH.div + [ class_ "column is-11-tablet is-three-fifths-desktop" ] + content + ] + +formContent_ :: forall i p. Plain i p +formContent_ content = + HH.div + [ class_ "content" ] + [ HH.div + [ class_ "column has-background-white-bis" ] + content + ] + +content_ :: forall i p. Plain i p +content_ = HH.div [ class_ "content" ] + +---------- +-- Buttons + +button :: forall i p. Array (HH.IProp HTMLbutton p) -> Plain i p +button props = HH.button ([ class_ "button is-light" ] <> props) + +buttonDark :: forall i p. Array (HH.IProp HTMLbutton p) -> Plain i p +buttonDark props = HH.button ([ class_ "button is-dark" ] <> props) + +buttonPrimary :: forall i p. Array (HH.IProp HTMLbutton p) -> Plain i p +buttonPrimary props = HH.button ([ class_ "button is-link" ] <> props) + +---------- +-- Form + +grouped_ :: forall i p. Plain i p +grouped_ array = + HH.div + [ class_ "field is-grouped" ] + ( wrap <$> array ) + where + wrap x = HH.p [ class_ "control" ] [ x ] + +field :: forall i p. { label :: String, help :: Either String String } -> Plain i p +field config contents = + HH.div + [ class_ "field" ] + [ HH.div + [ class_ "label" ] + [ HH.text config.label ] + , HH.div + [ class_ "control" ] + contents + , case config.help of + Left str -> helpError_ str + Right str -> help_ str + ] + where + help_ str = HH.p [ class_ "help" ] [ HH.text str ] + helpError_ str = HH.p [ class_ "help is-danger" ] [ HH.text str ] + +---------- +-- Formless + +-- Render a result as help text +resultToHelp :: forall t e. ToText e => String -> FormFieldResult e t -> Either String String +resultToHelp str = case _ of + NotValidated -> Right str + Validating -> Right "validating..." + other -> maybe (Right str) Left $ V.showError other + +-- Provide your own label, error or help text, and placeholder +type FieldConfig' = + { label :: String + , help :: Either String String + , placeholder :: String + } + +-- Provide a label, help text, placeholder, and symbol to have Formless wire everything +-- up on your behalf. +type FieldConfig sym = + { label :: String + , help :: String + , placeholder :: String + , sym :: SProxy sym + } + +input :: forall i p. FieldConfig' -> Array (HH.IProp HTMLinput p) -> HH.HTML i p +input config props = + field + { label: config.label, help: config.help } + [ HH.input $ + [ HP.type_ InputText + , either (const $ class_ "input is-danger") (const $ class_ "input") config.help + , HP.placeholder config.placeholder + ] <> props + ] + +textarea :: forall i p. FieldConfig' -> Array (HH.IProp HTMLtextarea p) -> HH.HTML i p +textarea config props = + field + { label: config.label, help: config.help } + [ HH.textarea $ + [ config.help # either + (const $ class_ "textarea is-danger") + (const $ class_ "textarea") + , HP.placeholder config.placeholder + ] <> props + ] + +-- Already ready to work with Formless +formlessField + :: forall form st act ps m sym e o t0 t1 r fields inputs + . IsSymbol sym + => ToText e + => Newtype (form Record F.FormField) { | fields } + => Newtype (form Variant F.InputFunction) (Variant inputs) + => Cons sym (F.FormField e String o) t0 fields + => Cons sym (F.InputFunction e String o) t1 inputs + => (FieldConfig' + -> Array (HH.IProp + (value :: String, onBlur :: FocusEvent, onInput :: Event | r) + (F.Action form act) + ) + -> F.ComponentHTML form act ps m + ) + -> FieldConfig sym + -> F.PublicState form st + -> F.ComponentHTML form act ps m +formlessField fieldType config state = fieldType (Builder.build config' config) props + where + config' = + Builder.delete (SProxy :: SProxy "sym") + >>> Builder.modify (SProxy :: SProxy "help") (const help') + + help' = + maybe (Right config.help) (Left <<< toText) (F.getError config.sym state.form) + + props = + [ HP.value (F.getInput config.sym state.form) + , HE.onValueInput (Just <<< F.setValidate config.sym) + ] + From fbaa92f5bff423c993fc242b463e28b5127eeacc Mon Sep 17 00:00:00 2001 From: Stanislav Zhdanovich Date: Wed, 8 Sep 2021 21:40:37 +0700 Subject: [PATCH 089/217] mv file upload to create nft form --- .../client/src/Component/CreateNftForm.purs | 251 +++++++++++------- .../client/src/Component/MainPage.purs | 1 - .../client/src/Component/UserPage.purs | 27 +- .../client/src/Utils/FormValidation.purs | 96 ++++--- .../client/src/View/FormElement.purs | 118 ++++---- 5 files changed, 276 insertions(+), 217 deletions(-) diff --git a/MetaLamp/nft-marketplace/client/src/Component/CreateNftForm.purs b/MetaLamp/nft-marketplace/client/src/Component/CreateNftForm.purs index a29b808c4..119deb57d 100644 --- a/MetaLamp/nft-marketplace/client/src/Component/CreateNftForm.purs +++ b/MetaLamp/nft-marketplace/client/src/Component/CreateNftForm.purs @@ -1,17 +1,18 @@ module Component.CreateNftForm where import Prelude - import Data.Array (filter, snoc, catMaybes) import Data.Const (Const) import Data.Either (Either(..)) import Data.List (toUnfoldable) import Data.Map as M -import Data.Maybe (Maybe(..), fromMaybe) +import Data.Maybe (Maybe(..), fromMaybe, isJust, maybe) import Data.Newtype (class Newtype) import Data.Symbol (SProxy(..)) import Effect.Aff.Class (class MonadAff) +import Effect.Exception (throw) import Formless as F +import Halogen (liftEffect) import Halogen as H import Halogen.HTML as HH import Halogen.HTML.Events as HE @@ -19,111 +20,135 @@ import Halogen.HTML.Properties as HP import Utils.FormValidation as V import View.FormElement (class_) import View.FormElement as UI +import Web.File.File (File) ----- --- Event form - +-- PutNft form -- Form types +type SubmittedNft + = { name :: String + , description :: String + , subcategories :: Array String + , file :: File + } -type Event = { | EventRow F.OutputType } +getSubmittedNft :: File -> { | PutNftRow F.OutputType } -> SubmittedNft +getSubmittedNft file form = + { name: form.name + , description: form.description + , subcategories: _.category <$> form.subcategories + , file: file + } -newtype EventForm r f = EventForm (r (EventRow f)) -derive instance newtypeEventForm :: Newtype (EventForm r f) _ +newtype PutNftForm r f + = PutNftForm (r (PutNftRow f)) -type EventRow f = - ( name :: f Void String String - , description :: f Void String String - , subcategories :: f V.FieldError (Maybe (Array CategoryInfo)) (Array CategoryInfo) - ) +derive instance newtypePutNftForm :: Newtype (PutNftForm r f) _ --- Form component types +type PutNftRow f + = ( name :: f Void String String + , description :: f Void String String + , subcategories :: f V.FieldError (Maybe (Array CategoryInfo)) (Array CategoryInfo) + ) -type Slot = - H.Slot (F.Query EventForm (Const Void) ChildSlots) Event +-- Form component types +type Slot + = H.Slot (F.Query PutNftForm (Const Void) ChildSlots) SubmittedNft -type State = - ( formIds :: Array Int - , nextId :: Int - ) +type State + = ( formIds :: Array Int + , nextId :: Int + , file :: Maybe File + ) data Action = AddCategoryForm | SubmitAll | HandleCategoryForm Int CategoryFormMessage + | HandleFile File -type ChildSlots = - ( categoryForm :: CategoryFormSlot Int ) +type ChildSlots + = ( categoryForm :: CategoryFormSlot Int ) -- Form spec - -eventComponent :: forall m. +putNftComponent :: + forall m. MonadAff m => - F.Component EventForm (Const Void) ChildSlots Unit Event m -eventComponent = F.component (const eventFormInput) $ F.defaultSpec - { render = render - , handleAction = handleAction - , handleEvent = handleEvent - } - where - eventFormInput :: F.Input EventForm State m - eventFormInput = - { validators: EventForm - { name: F.noValidation - , description: F.noValidation - , subcategories: F.hoistFn_ (fromMaybe []) + F.Component PutNftForm (Const Void) ChildSlots Unit SubmittedNft m +putNftComponent = + F.component (const putNftFormInput) + $ F.defaultSpec + { render = render + , handleAction = handleAction + , handleEvent = handleEvent } + where + putNftFormInput :: F.Input PutNftForm State m + putNftFormInput = + { validators: + PutNftForm + { name: F.noValidation + , description: F.noValidation + , subcategories: F.hoistFn_ (fromMaybe []) + } , initialInputs: Nothing , formIds: [] , nextId: 0 + , file: Nothing } handleAction = case _ of HandleCategoryForm ix Destroy -> do H.modify_ \st -> st { formIds = filter (_ /= ix) st.formIds } eval $ F.set _subcategories Nothing - AddCategoryForm -> - H.modify_ \st -> st - { nextId = st.nextId + 1, formIds = st.formIds `snoc` st.nextId } - + H.modify_ \st -> + st + { nextId = st.nextId + 1, formIds = st.formIds `snoc` st.nextId } SubmitAll -> do st <- H.get - res <- H.queryAll _categoryForm $ H.request F.submitReply - case map F.unwrapOutputFields $ catMaybes $ toUnfoldable $ M.values res of - [] -> eval F.submit - subcategories -> eval (F.set _subcategories (Just subcategories)) *> eval F.submit - + when (isJust st.file) do + res <- H.queryAll _categoryForm $ H.request F.submitReply + case map F.unwrapOutputFields $ catMaybes $ toUnfoldable $ M.values res of + [] -> eval F.submit + subcategories -> eval (F.set _subcategories (Just subcategories)) *> eval F.submit + HandleFile file -> do + H.modify_ _ { file = Just file } where eval act = F.handleAction handleAction handleEvent act + _subcategories = SProxy :: _ "subcategories" + _categoryForm = SProxy :: _ "categoryForm" handleEvent = case _ of - F.Submitted outputs -> - H.raise (F.unwrapOutputFields outputs) + F.Submitted outputs -> do + file <- (H.gets _.file) >>= maybe (liftEffect $ throw "No file provided") pure + H.raise (getSubmittedNft file $ F.unwrapOutputFields outputs) _ -> pure unit render st = HH.div_ [ HH.div - [ class_ "field is-grouped" ] - [ HH.div - [ class_ "control" ] - [ UI.button - [ HE.onClick \_ -> Just $ F.injAction AddCategoryForm ] - [ HH.text "Add Category Form" ] - ] - , HH.div - [ class_ "control" ] - [ UI.buttonPrimary - [ HE.onClick \_ -> Just $ F.injAction SubmitAll ] - [ HH.text "Submit" ] - ] - ] + [ class_ "field is-grouped" ] + [ HH.div + [ class_ "control" ] + [ UI.button + [ HE.onClick \_ -> Just $ F.injAction AddCategoryForm ] + [ HH.text "Add Category Form" ] + ] + , HH.div + [ class_ "control" ] + [ UI.buttonPrimary + [ HE.onClick \_ -> Just $ F.injAction SubmitAll ] + [ HH.text "Submit" ] + ] + ] , UI.input { label: "NFT Name" - , help: Right - "Provide an NFT name" + , help: + Right + "Provide an NFT name" , placeholder: "My NFT" } [ HP.value $ F.getInput _name st.form @@ -131,8 +156,9 @@ eventComponent = F.component (const eventFormInput) $ F.defaultSpec ] , UI.input { label: "NFT Description" - , help: Right - "Provide an NFT description" + , help: + Right + "Provide an NFT description" , placeholder: "Beautiful artwork" } [ HP.value $ F.getInput _description st.form @@ -140,54 +166,74 @@ eventComponent = F.component (const eventFormInput) $ F.defaultSpec ] , HH.div_ (mkCategoryForm <$> st.formIds) + , UI.field + { label: "Select File", help: Right "Upload a file corresponding to the token" } + [ HH.input + $ [ HP.type_ HP.InputFile + , class_ "input" + , HE.onFileUpload onFileUpload + ] + ] ] where mkCategoryForm i = do - let handler = Just <<< F.injAction <<< HandleCategoryForm i + let + handler = Just <<< F.injAction <<< HandleCategoryForm i HH.slot _categoryForm i categoryFormComponent unit handler + onFileUpload = case _ of + [ file ] -> Just $ F.injAction $ HandleFile file + _ -> Nothing + _name = SProxy :: SProxy "name" + _description = SProxy :: SProxy "description" - _categoryForm = SProxy :: SProxy "categoryForm" + _categoryForm = SProxy :: SProxy "categoryForm" ----- -- Category form, nested inside ----- - -- Form types +type CategoryInfo + = { | CategoryRow F.OutputType } -type CategoryInfo = { | CategoryRow F.OutputType } +newtype CategoryForm r f + = CategoryForm (r (CategoryRow f)) -newtype CategoryForm r f = CategoryForm (r (CategoryRow f)) derive instance newtypeCategoryForm :: Newtype (CategoryForm r f) _ -type CategoryRow f = - ( category :: f Void String String - ) +type CategoryRow f + = ( category :: f Void String String + ) -- Form component types +type CategoryFormSlot + = H.Slot (F.Query' CategoryForm) CategoryFormMessage -type CategoryFormSlot = - H.Slot (F.Query' CategoryForm) CategoryFormMessage +data CategoryFormAction + = RemoveMe -data CategoryFormAction = RemoveMe -data CategoryFormMessage = Destroy +data CategoryFormMessage + = Destroy -- Form spec - -categoryFormComponent :: forall m. +categoryFormComponent :: + forall m. MonadAff m => F.Component CategoryForm (Const Void) () Unit CategoryFormMessage m -categoryFormComponent = F.component (const categoryFormInput) $ F.defaultSpec - { render = render - , handleAction = handleAction - } +categoryFormComponent = + F.component (const categoryFormInput) + $ F.defaultSpec + { render = render + , handleAction = handleAction + } where categoryFormInput :: F.Input' CategoryForm m categoryFormInput = - { validators: CategoryForm - { category: F.noValidation - } + { validators: + CategoryForm + { category: F.noValidation + } , initialInputs: Nothing } @@ -195,22 +241,21 @@ categoryFormComponent = F.component (const categoryFormInput) $ F.defaultSpec RemoveMe -> H.raise Destroy render st = - UI.formContent_ - [ HH.div - [ class_ "field" ] - [ UI.buttonPrimary - [ HE.onClick \_ -> Just $ F.injAction RemoveMe ] - [ HH.text "Remove Me" ] - ] - - , UI.input - { label: "Inner category" - , help: Right "Provide any additional category you'd like." - , placeholder: "photos" - } - [ HP.value $ F.getInput _category st.form - , HE.onValueInput $ Just <<< F.set _category - ] - ] + UI.formContent_ + [ HH.div + [ class_ "field" ] + [ UI.buttonPrimary + [ HE.onClick \_ -> Just $ F.injAction RemoveMe ] + [ HH.text "Remove Me" ] + ] + , UI.input + { label: "Inner category" + , help: Right "Provide any additional category you'd like." + , placeholder: "photos" + } + [ HP.value $ F.getInput _category st.form + , HE.onValueInput $ Just <<< F.set _category + ] + ] where _category = SProxy :: SProxy "category" diff --git a/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs b/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs index f6efe3dfe..265b5f1cd 100644 --- a/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs +++ b/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs @@ -3,7 +3,6 @@ module Component.MainPage where import Data.Route import Data.Unit import Prelude - import Business.Marketplace (getMarketplaceContracts) import Business.Marketplace as Marketplace import Business.MarketplaceInfo (InfoContractId, getInfoContractId) diff --git a/MetaLamp/nft-marketplace/client/src/Component/UserPage.purs b/MetaLamp/nft-marketplace/client/src/Component/UserPage.purs index 1cbb599fa..be0fd468a 100644 --- a/MetaLamp/nft-marketplace/client/src/Component/UserPage.purs +++ b/MetaLamp/nft-marketplace/client/src/Component/UserPage.purs @@ -1,7 +1,6 @@ module Component.UserPage where import Prelude - import Business.MarketplaceInfo (InfoContractId) import Business.MarketplaceUser (UserContractId) import Business.MarketplaceUser as MarketplaceUser @@ -46,11 +45,10 @@ type State data Action = Initialize | Reinitialize Input - | CreateNft File.File - | HandleEventForm CreateNftForm.Event + | CreateNft CreateNftForm.SubmittedNft -type Slots = - ( eventForm :: CreateNftForm.Slot Unit ) +type Slots + = ( createNftForm :: CreateNftForm.Slot Unit ) component :: forall query output m. @@ -77,8 +75,7 @@ component = render _ = HH.div_ [ HH.h3_ [ HH.text "Create NFT from file: " ] - , HH.input [ HP.type_ HP.InputFile, HE.onFileUpload onNftUpload ] - , HH.slot (SProxy :: _ "eventForm") unit CreateNftForm.eventComponent unit (Just <<< HandleEventForm) + , HH.slot (SProxy :: _ "createNftForm") unit CreateNftForm.putNftComponent unit (Just <<< CreateNft) ] handleAction :: Action -> H.HalogenM State Action Slots output m Unit @@ -89,9 +86,9 @@ component = Reinitialize st -> do H.put st handleAction Initialize - CreateNft file -> do + CreateNft nft -> do ipfsCid <- - IPFS.pinFile file + IPFS.pinFile nft.file >>= case _ of Left e -> do let @@ -106,16 +103,10 @@ component = MarketplaceUser.createNft contractId $ MarketplaceUser.CreateNftParams { cnpIpfsCid: ipfsCid - , cnpNftName: "String" - , cnpNftDescription: "String" - , cnpNftCategory: [ "String" ] + , cnpNftName: nft.name + , cnpNftDescription: nft.description + , cnpNftCategory: nft.subcategories , cnpRevealIssuer: true } logInfo $ "Marketplace nft created: " <> show resp pure unit - HandleEventForm event -> logInfo $ show event - -onNftUpload :: Array File.File → Maybe Action -onNftUpload = case _ of - [ file ] -> Just $ CreateNft file - _ -> Nothing diff --git a/MetaLamp/nft-marketplace/client/src/Utils/FormValidation.purs b/MetaLamp/nft-marketplace/client/src/Utils/FormValidation.purs index 1ae95444f..cc82a238a 100644 --- a/MetaLamp/nft-marketplace/client/src/Utils/FormValidation.purs +++ b/MetaLamp/nft-marketplace/client/src/Utils/FormValidation.purs @@ -1,7 +1,6 @@ module Utils.FormValidation where import Prelude - import Data.Either (Either(..)) import Data.Foldable (length) as Foldable import Data.Generic.Rep (class Generic) @@ -28,6 +27,7 @@ data FieldError | NotEnoughMoney derive instance genericFieldError :: Generic FieldError _ + instance showFieldError :: Show FieldError where show = genericShow @@ -42,13 +42,20 @@ instance toTextFieldError :: ToText FieldError where toText (NotEnoughMoney) = "You don't have that much money." -- | Some useful types we'll parse to -newtype Name = Name String +newtype Name + = Name String + derive instance newtypeName :: Newtype Name _ + derive newtype instance showName :: Show Name -newtype Email = Email String +newtype Email + = Email String + derive instance newtypeEmail :: Newtype Email _ + derive newtype instance eqEmail :: Eq Email + derive newtype instance showEmail :: Show Email -- | Unpacks errors to render as a string @@ -64,23 +71,32 @@ instance toTextString :: ToText String where -------------------- -- Formless Validation -------------------- - emailFormat :: ∀ form m. Monad m => Validation form m FieldError String Email -emailFormat = hoistFnE_ $ \str -> - if contains (Pattern "@") str - then pure $ Email str - else Left InvalidEmail +emailFormat = + hoistFnE_ + $ \str -> + if contains (Pattern "@") str then + pure $ Email str + else + Left InvalidEmail minLength :: ∀ form m. Monad m => Int -> Validation form m FieldError String String -minLength n = hoistFnE_ $ \str -> - let n' = length str - in if n' < n then Left (TooShort n) else Right str +minLength n = + hoistFnE_ + $ \str -> + let + n' = length str + in + if n' < n then Left (TooShort n) else Right str -- | The opposite of minLength. maxLength :: ∀ form m. Monad m => Int -> Validation form m FieldError String String -maxLength n = hoistFnE_ \str -> - let n' = length str - in if n' > n then Left (TooLong n) else Right str +maxLength n = + hoistFnE_ \str -> + let + n' = length str + in + if n' > n then Left (TooLong n) else Right str exists :: ∀ form m a. Monad m => Validation form m FieldError (Maybe a) a exists = hoistFnE_ $ maybe (Left EmptyField) Right @@ -89,34 +105,44 @@ strIsInt :: ∀ form m. Monad m => Validation form m FieldError String Int strIsInt = hoistFnE_ $ \str -> maybe (Left $ InvalidInt str) Right (Int.fromString str) nonEmptyArray :: ∀ form m a. Monad m => Validation form m FieldError (Array a) (Array a) -nonEmptyArray = hoistFnE_ \arr -> - if Foldable.length arr > 0 - then Right arr - else Left EmptyField +nonEmptyArray = + hoistFnE_ \arr -> + if Foldable.length arr > 0 then + Right arr + else + Left EmptyField -- | Validate that an input string is not empty nonEmptyStr :: ∀ form m. Monad m => Validation form m FieldError String String -nonEmptyStr = hoistFnE_ $ \str -> - if null str - then Left EmptyField - else Right str +nonEmptyStr = + hoistFnE_ + $ \str -> + if null str then + Left EmptyField + else + Right str -------------------- -- Formless Async Validation -------------------- - emailIsUsed :: ∀ form m. MonadAff m => Validation form m FieldError Email Email -emailIsUsed = Validation \_ e@(Email e') -> do - -- Perhaps we hit the server to if the email is in use - _ <- liftAff $ delay $ Milliseconds 1000.0 - pure $ if (contains (Pattern "t") e') - then Left EmailInUse - else pure e +emailIsUsed = + Validation \_ e@(Email e') -> do + -- Perhaps we hit the server to if the email is in use + _ <- liftAff $ delay $ Milliseconds 1000.0 + pure + $ if (contains (Pattern "t") e') then + Left EmailInUse + else + pure e enoughMoney :: ∀ form m. MonadAff m => Validation form m FieldError Int Int -enoughMoney = Validation \_ i -> do - -- Let's check if we have enough money... - _ <- liftAff $ delay $ Milliseconds 5000.0 - pure $ if (i > 1000) - then Left NotEnoughMoney - else pure i \ No newline at end of file +enoughMoney = + Validation \_ i -> do + -- Let's check if we have enough money... + _ <- liftAff $ delay $ Milliseconds 5000.0 + pure + $ if (i > 1000) then + Left NotEnoughMoney + else + pure i diff --git a/MetaLamp/nft-marketplace/client/src/View/FormElement.purs b/MetaLamp/nft-marketplace/client/src/View/FormElement.purs index 834133756..bbc63fcff 100644 --- a/MetaLamp/nft-marketplace/client/src/View/FormElement.purs +++ b/MetaLamp/nft-marketplace/client/src/View/FormElement.purs @@ -1,7 +1,6 @@ module View.FormElement where import Prelude - import DOM.HTML.Indexed (HTMLa, HTMLbutton, HTMLinput, HTMLtextarea) import DOM.HTML.Indexed.InputType (InputType(..)) import Data.Either (Either(..), either) @@ -21,14 +20,14 @@ import Record.Builder as Builder import Web.Event.Event (Event) import Web.UIEvent.FocusEvent (FocusEvent) -type Plain i p = Array (HH.HTML i p) -> HH.HTML i p +type Plain i p + = Array (HH.HTML i p) -> HH.HTML i p class_ :: forall r t. String -> HH.IProp ( "class" :: String | r ) t class_ = HP.class_ <<< HH.ClassName ---------- -- Typography - h1_ :: forall i p. Plain i p h1_ = HH.h1 [ class_ "title" ] @@ -43,7 +42,6 @@ a props = HH.a ([ class_ "has-text-blue" ] <> props) ---------- -- Layout - section_ :: forall i p. Plain i p section_ content = HH.section @@ -67,7 +65,6 @@ content_ = HH.div [ class_ "content" ] ---------- -- Buttons - button :: forall i p. Array (HH.IProp HTMLbutton p) -> Plain i p button props = HH.button ([ class_ "button is-light" ] <> props) @@ -79,12 +76,11 @@ buttonPrimary props = HH.button ([ class_ "button is-link" ] <> props) ---------- -- Form - grouped_ :: forall i p. Plain i p grouped_ array = HH.div [ class_ "field is-grouped" ] - ( wrap <$> array ) + (wrap <$> array) where wrap x = HH.p [ class_ "control" ] [ x ] @@ -103,12 +99,12 @@ field config contents = Right str -> help_ str ] where - help_ str = HH.p [ class_ "help" ] [ HH.text str ] - helpError_ str = HH.p [ class_ "help is-danger" ] [ HH.text str ] + help_ str = HH.p [ class_ "help" ] [ HH.text str ] + + helpError_ str = HH.p [ class_ "help is-danger" ] [ HH.text str ] ---------- -- Formless - -- Render a result as help text resultToHelp :: forall t e. ToText e => String -> FormFieldResult e t -> Either String String resultToHelp str = case _ of @@ -117,74 +113,76 @@ resultToHelp str = case _ of other -> maybe (Right str) Left $ V.showError other -- Provide your own label, error or help text, and placeholder -type FieldConfig' = - { label :: String - , help :: Either String String - , placeholder :: String - } +type FieldConfig' + = { label :: String + , help :: Either String String + , placeholder :: String + } -- Provide a label, help text, placeholder, and symbol to have Formless wire everything -- up on your behalf. -type FieldConfig sym = - { label :: String - , help :: String - , placeholder :: String - , sym :: SProxy sym - } +type FieldConfig sym + = { label :: String + , help :: String + , placeholder :: String + , sym :: SProxy sym + } input :: forall i p. FieldConfig' -> Array (HH.IProp HTMLinput p) -> HH.HTML i p input config props = field { label: config.label, help: config.help } - [ HH.input $ - [ HP.type_ InputText - , either (const $ class_ "input is-danger") (const $ class_ "input") config.help - , HP.placeholder config.placeholder - ] <> props + [ HH.input + $ [ HP.type_ InputText + , either (const $ class_ "input is-danger") (const $ class_ "input") config.help + , HP.placeholder config.placeholder + ] + <> props ] textarea :: forall i p. FieldConfig' -> Array (HH.IProp HTMLtextarea p) -> HH.HTML i p textarea config props = field { label: config.label, help: config.help } - [ HH.textarea $ - [ config.help # either - (const $ class_ "textarea is-danger") - (const $ class_ "textarea") - , HP.placeholder config.placeholder - ] <> props + [ HH.textarea + $ [ config.help + # either + (const $ class_ "textarea is-danger") + (const $ class_ "textarea") + , HP.placeholder config.placeholder + ] + <> props ] -- Already ready to work with Formless -formlessField - :: forall form st act ps m sym e o t0 t1 r fields inputs - . IsSymbol sym - => ToText e - => Newtype (form Record F.FormField) { | fields } - => Newtype (form Variant F.InputFunction) (Variant inputs) - => Cons sym (F.FormField e String o) t0 fields - => Cons sym (F.InputFunction e String o) t1 inputs - => (FieldConfig' - -> Array (HH.IProp - (value :: String, onBlur :: FocusEvent, onInput :: Event | r) - (F.Action form act) - ) - -> F.ComponentHTML form act ps m - ) - -> FieldConfig sym - -> F.PublicState form st - -> F.ComponentHTML form act ps m +formlessField :: + forall form st act ps m sym e o t0 t1 r fields inputs. + IsSymbol sym => + ToText e => + Newtype (form Record F.FormField) { | fields } => + Newtype (form Variant F.InputFunction) (Variant inputs) => + Cons sym (F.FormField e String o) t0 fields => + Cons sym (F.InputFunction e String o) t1 inputs => + ( FieldConfig' -> + Array + ( HH.IProp + ( value :: String, onBlur :: FocusEvent, onInput :: Event | r ) + (F.Action form act) + ) -> + F.ComponentHTML form act ps m + ) -> + FieldConfig sym -> + F.PublicState form st -> + F.ComponentHTML form act ps m formlessField fieldType config state = fieldType (Builder.build config' config) props where - config' = - Builder.delete (SProxy :: SProxy "sym") - >>> Builder.modify (SProxy :: SProxy "help") (const help') + config' = + Builder.delete (SProxy :: SProxy "sym") + >>> Builder.modify (SProxy :: SProxy "help") (const help') - help' = - maybe (Right config.help) (Left <<< toText) (F.getError config.sym state.form) - - props = - [ HP.value (F.getInput config.sym state.form) - , HE.onValueInput (Just <<< F.setValidate config.sym) - ] + help' = maybe (Right config.help) (Left <<< toText) (F.getError config.sym state.form) + props = + [ HP.value (F.getInput config.sym state.form) + , HE.onValueInput (Just <<< F.setValidate config.sym) + ] From a5e0a32fcb73fb6557a6d479614a9376b72b5b84 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Wed, 8 Sep 2021 22:44:25 +0700 Subject: [PATCH 090/217] rm start chrome script --- MetaLamp/nft-marketplace/client/package.json | 1 - MetaLamp/nft-marketplace/client/scripts/start-chrome.sh | 1 - 2 files changed, 2 deletions(-) delete mode 100755 MetaLamp/nft-marketplace/client/scripts/start-chrome.sh diff --git a/MetaLamp/nft-marketplace/client/package.json b/MetaLamp/nft-marketplace/client/package.json index 8e1155d8e..1c0d5d1bd 100644 --- a/MetaLamp/nft-marketplace/client/package.json +++ b/MetaLamp/nft-marketplace/client/package.json @@ -12,7 +12,6 @@ "fetch-plutus-purs": "sh ./scripts/fetch-plutus-purs.sh", "generate-purs-only": "sh ./scripts/generate-purs.sh", "generate-purs": "npm run fetch-plutus-purs && npm run generate-purs-only", - "start-chrome": "sh ./scripts/start-chrome.sh", "start": "npm run purs:compile && npm run webpack:server" }, "dependencies": { diff --git a/MetaLamp/nft-marketplace/client/scripts/start-chrome.sh b/MetaLamp/nft-marketplace/client/scripts/start-chrome.sh deleted file mode 100755 index 15694ecd5..000000000 --- a/MetaLamp/nft-marketplace/client/scripts/start-chrome.sh +++ /dev/null @@ -1 +0,0 @@ -google-chrome --disable-web-security --user-data-dir=/chrome-temp \ No newline at end of file From 010e1c3822ce41ecc24cc3701d40bb50c709055f Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Wed, 8 Sep 2021 23:11:51 +0700 Subject: [PATCH 091/217] add reveal issuer checkbox --- .../client/src/Component/CreateNftForm.purs | 21 +++++++++++++++++++ .../client/src/Component/UserPage.purs | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/MetaLamp/nft-marketplace/client/src/Component/CreateNftForm.purs b/MetaLamp/nft-marketplace/client/src/Component/CreateNftForm.purs index 119deb57d..149134333 100644 --- a/MetaLamp/nft-marketplace/client/src/Component/CreateNftForm.purs +++ b/MetaLamp/nft-marketplace/client/src/Component/CreateNftForm.purs @@ -30,6 +30,7 @@ type SubmittedNft , description :: String , subcategories :: Array String , file :: File + , revealIssuer :: Boolean } getSubmittedNft :: File -> { | PutNftRow F.OutputType } -> SubmittedNft @@ -38,6 +39,7 @@ getSubmittedNft file form = , description: form.description , subcategories: _.category <$> form.subcategories , file: file + , revealIssuer: form.revealIssuer } newtype PutNftForm r f @@ -49,6 +51,7 @@ type PutNftRow f = ( name :: f Void String String , description :: f Void String String , subcategories :: f V.FieldError (Maybe (Array CategoryInfo)) (Array CategoryInfo) + , revealIssuer :: f Void Boolean Boolean ) -- Form component types @@ -89,6 +92,7 @@ putNftComponent = PutNftForm { name: F.noValidation , description: F.noValidation + , revealIssuer: F.noValidation , subcategories: F.hoistFn_ (fromMaybe []) } , initialInputs: Nothing @@ -174,6 +178,21 @@ putNftComponent = , HE.onFileUpload onFileUpload ] ] + , UI.field + { label: "Reveal Issuer" + , help: Right "Do you want to reveal you are the issuer of the token?" + } + [ HH.label + [ class_ "checkbox" ] + [ HH.input + [ class_ "checkbox" + , HP.type_ HP.InputCheckbox + , HP.checked $ F.getInput _revealIssuer st.form + , HE.onChange \_ -> Just $ F.modify _revealIssuer not + ] + , HH.text " Toggle reveal issuer" + ] + ] ] where mkCategoryForm i = do @@ -191,6 +210,8 @@ putNftComponent = _categoryForm = SProxy :: SProxy "categoryForm" + _revealIssuer = SProxy :: SProxy "revealIssuer" + ----- -- Category form, nested inside ----- diff --git a/MetaLamp/nft-marketplace/client/src/Component/UserPage.purs b/MetaLamp/nft-marketplace/client/src/Component/UserPage.purs index be0fd468a..5b3c442d3 100644 --- a/MetaLamp/nft-marketplace/client/src/Component/UserPage.purs +++ b/MetaLamp/nft-marketplace/client/src/Component/UserPage.purs @@ -106,7 +106,7 @@ component = , cnpNftName: nft.name , cnpNftDescription: nft.description , cnpNftCategory: nft.subcategories - , cnpRevealIssuer: true + , cnpRevealIssuer: nft.revealIssuer } logInfo $ "Marketplace nft created: " <> show resp pure unit From 8ee1b94251c3bc448162a66429120645c26365a8 Mon Sep 17 00:00:00 2001 From: stanislav-az Date: Thu, 9 Sep 2021 02:36:56 +0700 Subject: [PATCH 092/217] add funds and marketplace user state --- .../nft-marketplace/client/src/AppAff.purs | 27 ++++----- .../client/src/Component/MainPage.purs | 26 ++------- .../client/src/Component/UserPage.purs | 55 ++++++++++++++----- .../client/src/Component/WalletSelector.purs | 2 +- 4 files changed, 60 insertions(+), 50 deletions(-) diff --git a/MetaLamp/nft-marketplace/client/src/AppAff.purs b/MetaLamp/nft-marketplace/client/src/AppAff.purs index f5bb0067c..8097aa45f 100644 --- a/MetaLamp/nft-marketplace/client/src/AppAff.purs +++ b/MetaLamp/nft-marketplace/client/src/AppAff.purs @@ -1,12 +1,11 @@ module AppAff where -import Capability.Navigate -import Data.Route +import Capability.Navigate (class Navigate) +import Data.Route (routeCodec) import Prelude -import Utils.APIError +import Utils.APIError (APIError(..)) import Affjax (Error, Response, defaultRequest, printError, request) -import Affjax.RequestBody (RequestBody, formData, string) -import Affjax.RequestHeader (RequestHeader(..)) +import Affjax.RequestBody (RequestBody, string) import Affjax.ResponseFormat as ResponseFormat import AjaxUtils (renderForeignErrors) import Capability.Contract (class Contract, ContractId(..), Endpoint(..)) @@ -15,31 +14,27 @@ import Capability.LogMessages (class LogMessages) import Capability.PollContract (class PollContract) import Control.Monad.Except (ExceptT, runExcept, runExceptT) import Control.Monad.Reader.Trans (class MonadAsk, ReaderT, asks, runReaderT) -import Data.Argonaut.Core (Json, stringify) -import Data.Bifunctor (bimap, lmap) +import Data.Bifunctor (bimap) import Data.Either (Either(..), note) import Data.HTTP.Method (Method(..), fromString) import Data.Maybe (Maybe(..)) -import Data.MediaType.Common (multipartFormData) import Data.Newtype (wrap) import Effect (Effect) -import Effect.Aff (Aff, Milliseconds(..), delay, launchAff) +import Effect.Aff (Aff, Milliseconds(..), delay) import Effect.Aff.Class (class MonadAff, liftAff) import Effect.Class (class MonadEffect, liftEffect) import Effect.Console as Console -import Foreign (renderForeignError) import Foreign.Generic (class Decode, Foreign, F, decode, encodeJSON) import Foreign.JSON (decodeJSONWith) -import Foreign.NullOrUndefined (undefined) -import Routing.Duplex as Routing -import Routing.Hash as Routing +import Routing.Duplex (print) as Routing +import Routing.Hash (setHash) as Routing import Servant.PureScript.Ajax (AjaxError(..), ErrorDescription(..), ajax) import Type.Equality (class TypeEquals, from) import Web.File.File as File import Web.XHR.FormData as FormData -import Web.XHR.ReadyState as XHR -import Web.XHR.ResponseType as XHR -import Web.XHR.XMLHttpRequest as XHR +import Web.XHR.ReadyState (ReadyState(..)) as XHR +import Web.XHR.ResponseType (string) as XHR +import Web.XHR.XMLHttpRequest (XMLHttpRequest, open, readyState, response, sendFormData, status, statusText, xmlHttpRequest) as XHR type Env = { ipfsServer :: ServerInfo, pabServer :: ServerInfo } diff --git a/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs b/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs index 265b5f1cd..0f3ac5f99 100644 --- a/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs +++ b/MetaLamp/nft-marketplace/client/src/Component/MainPage.purs @@ -1,54 +1,40 @@ module Component.MainPage where -import Data.Route -import Data.Unit +import Data.Route (Route(..), routeCodec) import Prelude import Business.Marketplace (getMarketplaceContracts) -import Business.Marketplace as Marketplace import Business.MarketplaceInfo (InfoContractId, getInfoContractId) -import Business.MarketplaceInfo as MarketplaceInfo -import Business.MarketplaceUser (UserContractId, getUserContractId, ownPubKey) +import Business.MarketplaceUser (getUserContractId, ownPubKey) import Capability.IPFS as IPFS import Capability.LogMessages (class LogMessages, logInfo) import Capability.Navigate (class Navigate, navigate) import Capability.PollContract (class PollContract) -import Clipboard (handleAction) import Component.MarketPage as Market import Component.UserPage as User import Component.Utils (runRD) import Component.WalletSelector as WalletSelector import Control.Monad.Except (lift, runExceptT, throwError) import Control.Parallel (parTraverse) -import Data.Array (catMaybes, findMap, groupBy, mapWithIndex, take) -import Data.Array.NonEmpty as NEA -import Data.BigInteger (BigInteger) +import Data.Array (catMaybes) import Data.Either (either, hush) -import Data.Json.JsonTuple (JsonTuple(..)) import Data.Lens (Lens') import Data.Lens.Record (prop) -import Data.Maybe (Maybe(..), fromMaybe, maybe) +import Data.Maybe (Maybe(..), fromMaybe) import Data.Symbol (SProxy(..)) -import Data.Tuple (Tuple(..)) import Data.UserInstance (UserInstance) import Effect.Aff.Class (class MonadAff) import Effect.Class (class MonadEffect) import Halogen as H import Halogen.HTML as HH import Halogen.HTML.Events as HE -import Halogen.HTML.Properties (classes) import Halogen.HTML.Properties as HP import Network.RemoteData (RemoteData(..)) import Network.RemoteData as RD import Network.RemoteData as RemoteData import Plutus.PAB.Simulation (MarketplaceContracts) import Plutus.PAB.Webserver.Types (ContractInstanceClientState) -import Plutus.V1.Ledger.Crypto (PubKeyHash) -import Plutus.V1.Ledger.Value (AssetClass, Value) -import PlutusTx.AssocMap as Map -import Routing.Duplex as Routing -import Routing.Hash as Routing -import Utils.BEM as BEM -import View.RemoteDataState (remoteDataState) +import Routing.Duplex (parse) as Routing +import Routing.Hash (getHash) as Routing import Web.Event.Event (preventDefault) import Web.UIEvent.MouseEvent (MouseEvent, toEvent) diff --git a/MetaLamp/nft-marketplace/client/src/Component/UserPage.purs b/MetaLamp/nft-marketplace/client/src/Component/UserPage.purs index 5b3c442d3..fff35f5a9 100644 --- a/MetaLamp/nft-marketplace/client/src/Component/UserPage.purs +++ b/MetaLamp/nft-marketplace/client/src/Component/UserPage.purs @@ -2,29 +2,30 @@ module Component.UserPage where import Prelude import Business.MarketplaceInfo (InfoContractId) -import Business.MarketplaceUser (UserContractId) -import Business.MarketplaceUser as MarketplaceUser +import Business.MarketplaceInfo as MarketplaceInfo +import Business.MarketplaceUser (createNft) as MarketplaceUser import Capability.IPFS as IPFS import Capability.LogMessages (class LogMessages, logError, logInfo) import Capability.PollContract (class PollContract) -import Chain.State (handleAction) import Component.CreateNftForm as CreateNftForm -import Control.Monad.Error.Class (throwError) +import Component.Utils (runRD) +import Data.Bifunctor (lmap) import Data.Either (Either(..)) +import Data.Lens (Lens') +import Data.Lens.Record (prop) import Data.Maybe (Maybe(..)) import Data.Symbol (SProxy(..)) import Data.UserInstance (UserInstance) import Effect.Aff.Class (class MonadAff) import Effect.Class (class MonadEffect) import Effect.Exception (throw) -import Halogen (Component, lift, liftEffect) +import Halogen (liftEffect) import Halogen as H import Halogen.HTML as HH -import Halogen.HTML.Events as HE -import Halogen.HTML.Properties as HP -import Plutus.Contracts.NftMarketplace.OffChain.User as MarketplaceUser -import Plutus.V1.Ledger.Crypto (PubKeyHash) -import Web.File.File as File +import Network.RemoteData (RemoteData(..)) +import Plutus.Contracts.NftMarketplace.OffChain.User (CreateNftParams(..)) as MarketplaceUser +import Plutus.Contracts.NftMarketplace.OnChain.Core.StateMachine (MarketplaceDatum) +import Plutus.V1.Ledger.Value (Value) type Slot id = forall query. H.Slot query Void id @@ -40,11 +41,21 @@ type Input type State = { userInstance :: UserInstance , infoInstance :: InfoContractId + , userFunds :: RemoteData String Value + , marketplaceState :: RemoteData String MarketplaceDatum } +_userFunds :: Lens' State (RemoteData String Value) +_userFunds = prop (SProxy :: SProxy "userFunds") + +_marketplaceState :: Lens' State (RemoteData String MarketplaceDatum) +_marketplaceState = prop (SProxy :: SProxy "marketplaceState") + data Action = Initialize | Reinitialize Input + | GetUserFunds + | GetMarketplaceState | CreateNft CreateNftForm.SubmittedNft type Slots @@ -60,7 +71,7 @@ component :: H.Component HH.HTML query Input output m component = H.mkComponent - { initialState: identity + { initialState: initialState , render , eval: H.mkEval @@ -71,6 +82,14 @@ component = } } where + initialState :: Input -> State + initialState i = + { userInstance: i.userInstance + , infoInstance: i.infoInstance + , userFunds: NotAsked + , marketplaceState: NotAsked + } + render :: State -> H.ComponentHTML Action Slots m render _ = HH.div_ @@ -81,11 +100,21 @@ component = handleAction :: Action -> H.HalogenM State Action Slots output m Unit handleAction = case _ of Initialize -> do + handleAction GetUserFunds + handleAction GetMarketplaceState state <- H.get logInfo $ show state - Reinitialize st -> do - H.put st + Reinitialize i -> do + H.put $ initialState i handleAction Initialize + GetUserFunds -> do + state <- H.get + runRD _userFunds $ map (lmap show) + $ MarketplaceInfo.fundsAt state.infoInstance state.userInstance.userPubKey + GetMarketplaceState -> do + state <- H.get + runRD _marketplaceState $ map (lmap show) + $ MarketplaceInfo.marketplaceStore state.infoInstance CreateNft nft -> do ipfsCid <- IPFS.pinFile nft.file diff --git a/MetaLamp/nft-marketplace/client/src/Component/WalletSelector.purs b/MetaLamp/nft-marketplace/client/src/Component/WalletSelector.purs index 5bc5e1d3b..57f466b05 100644 --- a/MetaLamp/nft-marketplace/client/src/Component/WalletSelector.purs +++ b/MetaLamp/nft-marketplace/client/src/Component/WalletSelector.purs @@ -1,7 +1,7 @@ module Component.WalletSelector where import Prelude -import Data.Maybe (Maybe(..), fromMaybe, maybe) +import Data.Maybe (Maybe(..)) import Halogen as H import Halogen.HTML as HH import Halogen.HTML.Events as HE From db636db6e7697722856961dfad6a64d4b9225ba3 Mon Sep 17 00:00:00 2001 From: olgaklimenko Date: Thu, 9 Sep 2021 14:17:07 +0700 Subject: [PATCH 093/217] add spec document --- MetaLamp/nft-marketplace/Spec.md | 75 ++++++++++++++++++ .../readme-src/NFTMarketplaceArchitecture.png | Bin 0 -> 30095 bytes .../readme-src/NFTMarketplaceFlow.png | Bin 0 -> 68762 bytes 3 files changed, 75 insertions(+) create mode 100644 MetaLamp/nft-marketplace/Spec.md create mode 100644 MetaLamp/nft-marketplace/readme-src/NFTMarketplaceArchitecture.png create mode 100644 MetaLamp/nft-marketplace/readme-src/NFTMarketplaceFlow.png diff --git a/MetaLamp/nft-marketplace/Spec.md b/MetaLamp/nft-marketplace/Spec.md new file mode 100644 index 000000000..5013bdb50 --- /dev/null +++ b/MetaLamp/nft-marketplace/Spec.md @@ -0,0 +1,75 @@ +# NFT Marketplace + +## Brief description + +NFT Marketplace Service provides an ability to create NFT tokens for any file and put them up to for sale or auction. Marketplace allows to combine NFT tokens into the bundle and operate them as a single unit when sell or buy it. + +## Monetization + +The marketplace provider receives a tip from NFT selling and auctioning. + +## Glossary + +**Bundle** - a collection of tokens provided as a single unit. + +**Sale** - a protocol when NFT owner sets a fix price for NFT and opens sale for NFT buyers. NFT seller can cancel the sale if NFT isn't bought at that moment. Works for bundles as well. + +**Auction** - a protocol when NFT owner puts up an NFT for auction by 0 Ada, and sets a timeout of auction's duration. NFT bidders can bid their price before the timeout. The winner is bidder who made a last bid. If there are no bids on timeout, NFT returns to its seller. Works for bundles as well. + +**Marketplace tips** - a marketplace provider profit by carrying auctions and sales. + +## Users categories + +**Marketplace provider** - an actor who started a marketplace smart contract + +**NFT owner** - an actor who owns an NFT token + +**NFT seller** - an NFT owner who put his NFT up to the sale or auction + +**NFT buyer/NFT bidder** - an actor who try to buy an NFT on the Sale/make a bid on the Auction + +## System architecture + +![alt tag](readme-src/NFTMarketplaceArchitecture.png) + +## Example of the flow + +![alt tag](readme-src/NFTMarketplaceFlow.png) + +## Features list + +### Marketplace + +`start()` - Start a marketplace smart contract. + +### Marketplace user + +`createNft()` - Mint NFT token and add it to the marketplace. + +`openSale()` - Opens sale for NFT. + +`buyItem()` - Buy NFT. + +`closeSale()` - Close sale and receive the token back. + +`startAnAuction()` - Start an auction for specified NFT. + +`completeAnAuction()` - Complete auction before the timeout. + +`bundleUp()` - Create a bundle from specified NFTs. + +`unbundle()` - Unbundle specified NFTs. + +`ownPubKey()` - Get pubKeyHash for pubKey belonging to the wallet of a marketplace provider. + +`ownPubKeyBalance()` - Get balance on marketplace provider address. + +### Marketplace info + +`fundsAt()` - Get all UTxOs belonging to a user. + +`marketplaceFunds()` - Get all UTxOs belonging to the Marketplace. + +`marketplaceStore()` - Get current marketplace store state. + +`getAuctionState()` - Get current auction state for specified NFT diff --git a/MetaLamp/nft-marketplace/readme-src/NFTMarketplaceArchitecture.png b/MetaLamp/nft-marketplace/readme-src/NFTMarketplaceArchitecture.png new file mode 100644 index 0000000000000000000000000000000000000000..8cb29dc35d694e11c367488fe09e25628c48cd06 GIT binary patch literal 30095 zcmeFZbySpX`!)&|1{RJY7SaqL-3Ukw3^~jUjevA_x2=qXv`Fbt(t?CEB1lL`h=hcI zbSd5Y9DUyJ{oePt|JiHpzxKD+de-AGbI&!`eO_lD$9V^;smh-`e(5+F8QDn%v@DK{ z>;Q_4jNIzz5qKwj!dvjUor9^Bxs{pe-)nfed3kPdi@<;C+yYE|Qha>yKOP=VK|yZa zztX3tKbNyL>3Pm4EgOmstI?O9cg<8)_;hx~hhn zE}8=TRyQ=&1pY2!Z)Eq+B5JVF8$5p(5zw_ZHgRMjJyeXet?e-; zMyAdx+_E@TAznPz1?MEKEhFcI;TAEM!D4lADmt*Pw5+X@iddn#*%h_4jnTfy$GCC?= zMoMmc76P8O>e5O=HX8h1f@VfW+*+RYC@XDiWeX>FVQV)&4;@QmSWrR8*j$bqPC-se z8E#UJUr|U`TU|{>n_I)%T}w$s%NgZ>vX|FXv{Kb@F|~jnFjKbEMd_g76-LTcOVCD1 zQJI(98>K00swQKkX^peASMwBB#(HWBIpEPQP8hU1%E*h`%uYobzM?H;>>#ZrmQm3% z5pntS0C<>cWa6J=XB5iK4q9dAJqbpc&htTaEj zf~Gl^$IIH-*uvZvB;&+yf%Q=H5;5bIv*$K9LrZI0%Q$Oum3#=wF^3W;k}Jy?Nq&ljN}y^9i0SGipEx&0zz0t4ZH?cP0HOy zNY2_w-BVLh!N!f-MoUlukF#>(;Zt|gka4wjGPBiHu=f&CLc1utS;#oc8G}GQ&D@o^ z)!guoMwSXXZfb6(-c}$FFD+#i5f2wHVOdiLFE3sv9T!V!4IvpDbtg|HC0z|&TMfLa zf(8yP?SQtm)X|hTcEMT;$?Di*fEkEb}Gtt>VmSyW-0<` zIcX~oRcX9{r?eHHvyzIXfQ6ctvj|QVW9B5sE#PXV;f#^CP#3^DYVq<3pjEV-UDZYK z)+jA^ew;JPL|4RGMMp|V$C<~%9iu4cin4HV2RA2R>J9?6@NhJ9F;{T1cM;UW=(^j> z;;rFbVYnM57gxMIzmpMG$W{i$@67FO@8PT?WZ`P#q-*M6=Viog;$iJ=BEqd?D&r_9 zFOP$-fLoK(Rq}MfpzOWvc~q=-8 z8eA#-`!8?$$F;)W{{#v?lteCXA{p6rG6h*Fb6p&*k57;3@9)$&0g_vZ%%MpOn4&plZ`;U9)@P53Y4YCA`VHNf&YdqWC?#?ok3Flc|rECH^{>1 z|6FqDRtlPQ=^^s}LoVUIm}gLF+?k>f$*y%uNlEGW1&&5)L*EbiCJnrHm-F-U>pp!l z@82q{`TY3=3aedgt3y~;&s0B#!oGK(zw~@_`zkfHysa(AuV24ni&dr0D4zyNa-SJ2 zbCba$I5;?#R#xhon&5J7A)(2+zIU+&!MtY8uUL({IV=?q*d*?a$8(HnZjbsZgN#l*xe@mtn3MX=o4=R`8q4G$;1e=nS-94kHDnVMfv zQ2X)YgRzFttO3ka&P!n@`jC(iVKK3>`T17(O2+L{-kzQwe%s#XZ{NN}tlN=<%8I>z z)9d%h=;-~J7;3nUnz(BThe|gl;vA-W^BKr2+7na#cei|(K2RW#xQF)+Qc%&*9NS?* zb)~5!qn=&p?eE8>DxOzOlE|^=`}OnP_}b+=4Ix+6voue#v$HoxB0mz&v%5A|`tN$P zY2{}7S9|_4PMBvg{S-vAvAvzldOR^Papw2u=o@et6%`e@!EYlYcPK0mlh%xk6SomI=Us5vpYpmglsO!_(M<=o>IWUPgjaH^QK+I8W z8dLp6?6kg1mNB( zZ9X2Ye+IX~XVG?O@7!T6EiDA_Qps>`|8tIgzpkr5uCSQ7*u*=iL%F>#~VKs+YONQR(QQ*du8aq z$pjl?CDcx|#vP-iOmQ0fl(}@f(px|u7wKt8E8+XJvQk`AQ`2?fdu)G^4eUsAZ^I;Q zC+(0FHwQiH{+;X6GBQ@*a<4{5M~jM|ZEI`W*xEY4?L3YKWuEKJCo8lYxPZb6dn{bR zVzJ{bvFA>lIH8&#dbkGe@7M3&IG-dcLpefV;D^pmDjOS{L~)-ZH6TSdMiwdIy{%a; zvZ~!Rz14@Omez*@;McERyVkVO)tRa^{k;@q6Hk7pCtC*uovxa2cVG6kT=1ob4L;)@;E-ULKG1Z!KR?kj*g9KAg#O7PLg4gB$ynAD#jbb9%$tmkSjP8`fva4vq