From 2e6889a6c05865bc19c422bd5c9f90a402bf7440 Mon Sep 17 00:00:00 2001 From: Benjamin Fuentes Date: Wed, 8 Nov 2023 17:07:31 +0100 Subject: [PATCH] sync with docs.tezos.com --- README.md | 2779 ++++++++++--------- solution/.taq/config.json | 6 +- solution/.taq/state.json | 131 +- solution/.taq/testing-state.json | 40 +- solution/app/.env | 2 +- solution/app/src/nft.code.ts | 2 +- solution/artifacts/nft.default_storage.tz | 2 +- solution/contracts/nft.jsligo | 20 +- solution/contracts/nft.parameterList.jsligo | 8 +- solution/contracts/nft.storageList.jsligo | 26 +- solution/package-lock.json | 705 ++++- solution/package.json | 9 +- 12 files changed, 2184 insertions(+), 1546 deletions(-) diff --git a/README.md b/README.md index 60e7c73..56a9543 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,16 @@ --- -id: build-an-nft-marketplace -title: NFT Marketplace Part 3 -lastUpdated: 11th October 2023 +title: "Part 3: Managing tokens with quantities" +lastUpdated: 8th November 2023 --- -## Introduction +Because only one of each NFT can exist, they are not the right token type to represent wine bottles, which have a type and a quantity of bottles of that type. +So in this part, you change the application to use a single-asset template, which lets you create a single token ID with a quantity that you define. -![https://vinepair.com/wp-content/uploads/2016/06/Cellar-HEAD.jpg](https://vinepair.com/wp-content/uploads/2016/06/Cellar-HEAD.jpg) +Of course, a wine store has many different bottles of wine with different quantities, so in the next part you use a multi-asset template to represent bottles in that situation. -This time, let's use the single asset template. It is the opposite of the previous NFT template because: +You can continue from your code from part 2 or start from the completed version here: https://github.com/marigold-dev/training-nft-2/tree/main/solution. -- you have a unique `token_id`, so only 1 wine collection -- you have a certain quantity of items in the same collection - -To sum up, you are producing wine bottles from the same collection with `n` quantity. - -Keep your code from previous training or get the solution [here](https://github.com/marigold-dev/training-nft-2/tree/main/solution) - -> If you clone/fork a repo, rebuild in local +If you start from the completed version, run these commands to install dependencies for the web application: ```bash npm i @@ -26,1378 +19,1386 @@ yarn install cd .. ``` -## Smart Contract - -Point to the new template changing the first import line of your `nft.jsligo` file to - -```ligolang -#import "@ligo/fa/lib/fa2/asset/single_asset.impl.jsligo" "FA2Impl" -``` - -On nft.jsligo file, query/replace any reference to `NFT` namespace by `SingleAsset` one - -Change the `offer` and `storage` definitions - -```ligolang -export type offer = { quantity: nat, price: nat }; - -export type storage = { - administrators: set
, - totalSupply: nat, - offers: map, //user sells an offer - - ledger: FA2Impl.SingleAsset.ledger, - metadata: FA2Impl.TZIP16.metadata, - token_metadata: FA2Impl.TZIP12.tokenMetadata, - operators: FA2Impl.SingleAsset.operators, -}; -``` - -Explanation: - -- `offers` is now a `map`, because you don't have to store `token_id` as a key, now the key is the owner's address. Each owner can sell a part of the unique collection -- `offer` requires a quantity, each owner is selling a part of the unique collection -- `totalSupply` is set while minting in order to track the global quantity of minted items on the collection. It makes it unnecessary to recalculate each time the quantity from each owner's holdings (this value is constant) - -Edit the `mint` function to add the `quantity` extra param, and finally change the `return` - -```ligolang -@entry -const mint = ( - [quantity, name, description, symbol, ipfsUrl]: [ - nat, - bytes, - bytes, - bytes, - bytes - ], - s: storage -): ret => { - if (quantity <= (0 as nat)) return failwith("0"); - if (! Set.mem(Tezos.get_sender(), s.administrators)) return failwith("1"); - const token_info: map = - Map.literal( - list( - [ - ["name", name], - ["description", description], - ["interfaces", (bytes `["TZIP-12"]`)], - ["artifactUri", ipfsUrl], - ["displayUri", ipfsUrl], - ["thumbnailUri", ipfsUrl], - ["symbol", symbol], - ["decimals", (bytes `0`)] - ] - ) - ) as map; - return [ - list([]) as list, - { - ...s, - totalSupply: quantity, - ledger: Big_map.literal(list([[Tezos.get_sender(), quantity as nat]])) as - FA2Impl.SingleAsset.ledger, - token_metadata: Big_map.add( - 0 as nat, - { token_id: 0 as nat, token_info: token_info }, - s.token_metadata - ), - operators: Big_map.empty as FA2Impl.SingleAsset.operators, - } - ] -}; -``` - -Edit the `sell` function to replace `token_id` by `quantity`, add/override an offer for the user - -```ligolang -@entry -const sell = ([quantity, price]: [nat, nat], s: storage): ret => { - //check balance of seller - - const sellerBalance = - FA2Impl.SingleAsset.get_amount_for_owner( - { - ledger: s.ledger, - metadata: s.metadata, - operators: s.operators, - token_metadata: s.token_metadata, - } - )(Tezos.get_source()); - if (quantity > sellerBalance) return failwith("2"); - //need to allow the contract itself to be an operator on behalf of the seller - - const newOperators = - FA2Impl.SingleAsset.add_operator(s.operators)(Tezos.get_source())( - Tezos.get_self_address() - ); - //DECISION CHOICE: if offer already exists, we just override it - - return [ - list([]) as list, - { - ...s, - offers: Map.add( - Tezos.get_source(), - { quantity: quantity, price: price }, - s.offers - ), - operators: newOperators - } - ] -}; -``` - -Also edit the `buy` function to replace `token_id` by `quantity`, check quantities, check final price is enough and update the current offer - -```ligolang -@entry -const buy = ([quantity, seller]: [nat, address], s: storage): ret => { - //search for the offer - - return match(Map.find_opt(seller, s.offers)) { - when (None()): - failwith("3") - when (Some(offer)): - do { - //check if quantity is enough - - if (quantity > offer.quantity) return failwith("4"); - //check if amount have been paid enough - - if (Tezos.get_amount() < (offer.price * quantity) * (1 as mutez)) return failwith( - "5" - ); - // prepare transfer of XTZ to seller - - const op = - Tezos.transaction( - unit, - (offer.price * quantity) * (1 as mutez), - Tezos.get_contract_with_error(seller, "6") - ); - //transfer tokens from seller to buyer - - let ledger = - FA2Impl.SingleAsset.decrease_token_amount_for_user(s.ledger)(seller)( - quantity - ); - ledger - = FA2Impl.SingleAsset.increase_token_amount_for_user(ledger)( - Tezos.get_source() - )(quantity); - //update new offer - - const newOffer = { ...offer, quantity: abs(offer.quantity - quantity) }; - return [ - list([op]) as list, - { - ...s, - offers: Map.update(seller, Some(newOffer), s.offers), - ledger: ledger, - } - ] - } - } -}; -``` - -Edit the storage file `nft.storageList.jsligo` as it. (:warning: you can change the `administrator` address to your own address or keep `alice`) - -```ligolang -#import "nft.jsligo" "Contract" - -const default_storage: Contract.storage = { - administrators: Set.literal( - list(["tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb" as address]) - ) as set
, - totalSupply: 0 as nat, - offers: Map.empty as map, - ledger: Big_map.empty as Contract.FA2Impl.SingleAsset.ledger, - metadata: Big_map.literal( - list( - [ - ["", bytes `tezos-storage:data`], - [ - "data", - bytes - `{ - "name":"FA2 NFT Marketplace", - "description":"Example of FA2 implementation", - "version":"0.0.1", - "license":{"name":"MIT"}, - "authors":["Marigold"], - "homepage":"https://marigold.dev", - "source":{ - "tools":["Ligo"], - "location":"https://github.com/ligolang/contract-catalogue/tree/main/lib/fa2"}, - "interfaces":["TZIP-012"], - "errors": [], - "views": [] - }` - ] - ] - ) - ) as Contract.FA2Impl.TZIP16.metadata, - token_metadata: Big_map.empty as Contract.FA2Impl.TZIP12.tokenMetadata, - operators: Big_map.empty as Contract.FA2Impl.SingleAsset.operators, -}; -``` - -Compile again and deploy to ghostnet. - -```bash -TAQ_LIGO_IMAGE=ligolang/ligo:1.0.0 taq compile nft.jsligo -taq deploy nft.tz -e "testing" -``` - -```logs -┌──────────┬──────────────────────────────────────┬───────┬──────────────────┬────────────────────────────────┐ -│ Contract │ Address │ Alias │ Balance In Mutez │ Destination │ -├──────────┼──────────────────────────────────────┼───────┼──────────────────┼────────────────────────────────┤ -│ nft.tz │ KT1EUWEeR9RHMb5q5jeW5jbhxBFHbLTqQgiZ │ nft │ 0 │ https://ghostnet.ecadinfra.com │ -└──────────┴──────────────────────────────────────┴───────┴──────────────────┴────────────────────────────────┘ -``` - -**The smart contract! _(backend)_ is finished** - -## NFT Marketplace front - -Generate Typescript classes and go to the frontend to run the server - -```bash -taq generate types ./app/src -cd ./app -yarn install -yarn dev -``` - -### Update in `App.tsx` - -Fetch the `token_id == 0`. -Replace the function `refreshUserContextOnPageReload` by - -```typescript -const refreshUserContextOnPageReload = async () => { - console.log("refreshUserContext"); - //CONTRACT - try { - let c = await Tezos.contract.at(nftContractAddress, tzip12); - console.log("nftContractAddress", nftContractAddress); - - let nftContrat: NftWalletType = await Tezos.wallet.at( - nftContractAddress - ); - const storage = (await nftContrat.storage()) as Storage; - - try { - let tokenMetadata: TZIP21TokenMetadata = (await c - .tzip12() - .getTokenMetadata(0)) as TZIP21TokenMetadata; - nftContratTokenMetadataMap.set("0", tokenMetadata); - - setNftContratTokenMetadataMap(new Map(nftContratTokenMetadataMap)); //new Map to force refresh - } catch (error) { - console.log("error refreshing nftContratTokenMetadataMap: "); - } - - setNftContrat(nftContrat); - setStorage(storage); - } catch (error) { - console.log("error refreshing nft contract: ", error); - } - - //USER - const activeAccount = await wallet.client.getActiveAccount(); - if (activeAccount) { - setUserAddress(activeAccount.address); - const balance = await Tezos.tz.getBalance(activeAccount.address); - setUserBalance(balance.toNumber()); - } - - console.log("refreshUserContext ended."); -}; -``` - -### Update in `MintPage.tsx` - -The quantity field is added and the `token_id` field is removed. Replace the full file by the following content: - -```typescript -import OpenWithIcon from "@mui/icons-material/OpenWith"; -import { - Button, - CardHeader, - CardMedia, - MobileStepper, - Stack, - SwipeableDrawer, - TextField, - Toolbar, - useMediaQuery, -} from "@mui/material"; -import Box from "@mui/material/Box"; -import Card from "@mui/material/Card"; -import CardContent from "@mui/material/CardContent"; -import Paper from "@mui/material/Paper"; -import Typography from "@mui/material/Typography"; -import { BigNumber } from "bignumber.js"; -import { useSnackbar } from "notistack"; -import React, { useEffect, useState } from "react"; -import { TZIP21TokenMetadata, UserContext, UserContextType } from "./App"; -import { TransactionInvalidBeaconError } from "./TransactionInvalidBeaconError"; - -import { - AddCircleOutlined, - Close, - KeyboardArrowLeft, - KeyboardArrowRight, -} from "@mui/icons-material"; -import { char2Bytes } from "@taquito/utils"; -import { useFormik } from "formik"; -import SwipeableViews from "react-swipeable-views"; -import * as yup from "yup"; -import { address, bytes, nat } from "./type-aliases"; -export default function MintPage() { - const { - userAddress, - storage, - nftContrat, - refreshUserContextOnPageReload, - nftContratTokenMetadataMap, - } = React.useContext(UserContext) as UserContextType; - const { enqueueSnackbar } = useSnackbar(); - const [pictureUrl, setPictureUrl] = useState(""); - const [file, setFile] = useState(null); - - const [activeStep, setActiveStep] = React.useState(0); - - const handleNext = () => { - setActiveStep((prevActiveStep) => prevActiveStep + 1); - }; - - const handleBack = () => { - setActiveStep((prevActiveStep) => prevActiveStep - 1); - }; - - const handleStepChange = (step: number) => { - setActiveStep(step); - }; - const validationSchema = yup.object({ - name: yup.string().required("Name is required"), - description: yup.string().required("Description is required"), - symbol: yup.string().required("Symbol is required"), - quantity: yup - .number() - .required("Quantity is required") - .positive("ERROR: The number must be greater than 0!"), - }); - - const formik = useFormik({ - initialValues: { - name: "", - description: "", - token_id: 0, - symbol: "WINE", - quantity: 1, - } as TZIP21TokenMetadata & { quantity: number }, - validationSchema: validationSchema, - onSubmit: (values) => { - mint(values); - }, - }); - - //open mint drawer if admin - useEffect(() => { - if (storage && storage!.administrators.indexOf(userAddress! as address) < 0) - setFormOpen(false); - else setFormOpen(true); - }, [userAddress]); - - const mint = async ( - newTokenDefinition: TZIP21TokenMetadata & { quantity: number } - ) => { - try { - //IPFS - if (file) { - const formData = new FormData(); - formData.append("file", file); - - const requestHeaders: HeadersInit = new Headers(); - requestHeaders.set( - "pinata_api_key", - `${import.meta.env.VITE_PINATA_API_KEY}` - ); - requestHeaders.set( - "pinata_secret_api_key", - `${import.meta.env.VITE_PINATA_API_SECRET}` - ); - - const resFile = await fetch( - "https://api.pinata.cloud/pinning/pinFileToIPFS", - { - method: "post", - body: formData, - headers: requestHeaders, - } - ); - - const responseJson = await resFile.json(); - console.log("responseJson", responseJson); - - const thumbnailUri = `ipfs://${responseJson.IpfsHash}`; - setPictureUrl( - `https://gateway.pinata.cloud/ipfs/${responseJson.IpfsHash}` - ); - - const op = await nftContrat!.methods - .mint( - new BigNumber(newTokenDefinition.quantity) as nat, - char2Bytes(newTokenDefinition.name!) as bytes, - char2Bytes(newTokenDefinition.description!) as bytes, - char2Bytes(newTokenDefinition.symbol!) as bytes, - char2Bytes(thumbnailUri) as bytes - ) - .send(); - - //close directly the form - setFormOpen(false); - enqueueSnackbar( - "Wine collection is minting ... it will be ready on next block, wait for the confirmation message before minting another collection", - { variant: "info" } - ); - - await op.confirmation(2); - - enqueueSnackbar("Wine collection minted", { variant: "success" }); - - refreshUserContextOnPageReload(); //force all app to refresh the context - } - } catch (error) { - console.table(`Error: ${JSON.stringify(error, null, 2)}`); - let tibe: TransactionInvalidBeaconError = - new TransactionInvalidBeaconError(error); - enqueueSnackbar(tibe.data_message, { - variant: "error", - autoHideDuration: 10000, - }); - } - }; - - const [formOpen, setFormOpen] = useState(false); - - const toggleDrawer = - (open: boolean) => (event: React.KeyboardEvent | React.MouseEvent) => { - if ( - event.type === "keydown" && - ((event as React.KeyboardEvent).key === "Tab" || - (event as React.KeyboardEvent).key === "Shift") - ) { - return; - } - setFormOpen(open); - }; - - const isTablet = useMediaQuery("(min-width:600px)"); - - return ( - - {storage ? ( - - ) : ( - "" - )} - - - - - -
- - Mint a new collection - - - - - - - - - {pictureUrl ? ( - - ) : ( - "" - )} - - - - -
-
-
- - Mint your wine collection - - {nftContratTokenMetadataMap.size != 0 ? ( - - - {Array.from(nftContratTokenMetadataMap!.entries()).map( - ([token_id, token]) => ( - - - - - - - - {"ID : " + token_id} - {"Symbol : " + token.symbol} - - {"Description : " + token.description} - - - - - ) - )} - - - Next - - - } - backButton={ - - } - /> - - ) : ( - - Sorry, there is not NFT yet, you need to mint bottles first - - )} -
- ); -} -``` - -### Update in `OffersPage.tsx` - -The quantity field is added and the `token_id` filed is removed. Replace the full file with the following content: - -```typescript -import { InfoOutlined } from "@mui/icons-material"; -import SellIcon from "@mui/icons-material/Sell"; -import * as api from "@tzkt/sdk-api"; - -import { - Box, - Button, - Card, - CardActions, - CardContent, - CardHeader, - CardMedia, - ImageList, - InputAdornment, - Pagination, - TextField, - Tooltip, - Typography, - useMediaQuery, -} from "@mui/material"; -import Paper from "@mui/material/Paper"; -import BigNumber from "bignumber.js"; -import { useFormik } from "formik"; -import { useSnackbar } from "notistack"; -import React, { Fragment, useEffect, useState } from "react"; -import * as yup from "yup"; -import { UserContext, UserContextType } from "./App"; -import ConnectButton from "./ConnectWallet"; -import { TransactionInvalidBeaconError } from "./TransactionInvalidBeaconError"; -import { address, nat } from "./type-aliases"; - -const itemPerPage: number = 6; - -const validationSchema = yup.object({ - price: yup - .number() - .required("Price is required") - .positive("ERROR: The number must be greater than 0!"), - quantity: yup - .number() - .required("Quantity is required") - .positive("ERROR: The number must be greater than 0!"), -}); - -type Offer = { - price: nat; - quantity: nat; -}; - -export default function OffersPage() { - api.defaults.baseUrl = "https://api.ghostnet.tzkt.io"; - - const [selectedTokenId, setSelectedTokenId] = React.useState(0); - const [currentPageIndex, setCurrentPageIndex] = useState(1); - - let [ownerOffers, setOwnerOffers] = React.useState(null); - let [ownerBalance, setOwnerBalance] = React.useState(0); - - const { - nftContrat, - nftContratTokenMetadataMap, - userAddress, - storage, - refreshUserContextOnPageReload, - Tezos, - setUserAddress, - setUserBalance, - wallet, - } = React.useContext(UserContext) as UserContextType; - - const { enqueueSnackbar } = useSnackbar(); - - const formik = useFormik({ - initialValues: { - price: 0, - quantity: 1, - }, - validationSchema: validationSchema, - onSubmit: (values) => { - console.log("onSubmit: (values)", values, selectedTokenId); - sell(selectedTokenId, values.quantity, values.price); - }, - }); - - const initPage = async () => { - if (storage) { - console.log("context is not empty, init page now"); - - const ledgerBigMapId = ( - storage.ledger as unknown as { id: BigNumber } - ).id.toNumber(); - - const ownersKeys = await api.bigMapsGetKeys(ledgerBigMapId, { - micheline: "Json", - active: true, - }); - - await Promise.all( - ownersKeys.map(async (ownerKey) => { - if (ownerKey.key === userAddress) { - const ownerBalance = await storage.ledger.get( - userAddress as address - ); - setOwnerBalance(ownerBalance.toNumber()); - const ownerOffers = await storage.offers.get( - userAddress as address - ); - if (ownerOffers && ownerOffers.quantity != BigNumber(0)) - setOwnerOffers(ownerOffers!); - - console.log( - "found for " + - ownerKey.key + - " on token_id " + - 0 + - " with balance " + - ownerBalance - ); - } else { - console.log("skip to next owner"); - } - }) - ); - } else { - console.log("context is empty, wait for parent and retry ..."); - } - }; - - useEffect(() => { - (async () => { - console.log("after a storage changed"); - await initPage(); - })(); - }, [storage]); - - useEffect(() => { - (async () => { - console.log("on Page init"); - await initPage(); - })(); - }, []); - - const sell = async (token_id: number, quantity: number, price: number) => { - try { - const op = await nftContrat?.methods - .sell( - BigNumber(quantity) as nat, - BigNumber(price * 1000000) as nat //to mutez - ) - .send(); - - await op?.confirmation(2); - - enqueueSnackbar( - "Wine collection (token_id=" + - token_id + - ") offer for " + - quantity + - " units at price of " + - price + - " XTZ", - { variant: "success" } - ); - - refreshUserContextOnPageReload(); //force all app to refresh the context - } catch (error) { - console.table(`Error: ${JSON.stringify(error, null, 2)}`); - let tibe: TransactionInvalidBeaconError = - new TransactionInvalidBeaconError(error); - enqueueSnackbar(tibe.data_message, { - variant: "error", - autoHideDuration: 10000, - }); - } - }; - - const isDesktop = useMediaQuery("(min-width:1100px)"); - const isTablet = useMediaQuery("(min-width:600px)"); - return ( - - - Sell my bottles - - {ownerBalance != 0 ? ( - - setCurrentPageIndex(value)} - count={Math.ceil(1 / itemPerPage)} - showFirstButton - showLastButton - /> - - - - - {"ID : " + 0} - - {"Description : " + - nftContratTokenMetadataMap.get("0")?.description} - - - } - > - - - } - title={nftContratTokenMetadataMap.get("0")?.name} - /> - - - - - - {"Owned : " + ownerBalance} - - - {ownerOffers - ? "Traded : " + - ownerOffers?.quantity + - " (price : " + - ownerOffers?.price.dividedBy(1000000) + - " Tz/b)" - : ""} - - - - - - {!userAddress ? ( - - - - ) : ( -
{ - setSelectedTokenId(0); - formik.handleSubmit(values); - }} - > - - - - - - ), - }} - /> - -
- )} -
-
-
-
- ) : ( - - Sorry, you don't own any bottles, buy or mint some first - - )} -
- ); -} -``` - -### Update in `WineCataloguePage.tsx` - -The quantity field is added and `token_id` filed is removed. Replace the full file with the following content: - -```typescript -import { InfoOutlined } from "@mui/icons-material"; -import ShoppingCartIcon from "@mui/icons-material/ShoppingCart"; -import { - Button, - Card, - CardActions, - CardContent, - CardHeader, - CardMedia, - ImageList, - InputAdornment, - Pagination, - TextField, - Tooltip, - Typography, - useMediaQuery, -} from "@mui/material"; -import Box from "@mui/material/Box"; -import Paper from "@mui/material/Paper"; -import BigNumber from "bignumber.js"; -import { useFormik } from "formik"; -import { useSnackbar } from "notistack"; -import React, { Fragment, useState } from "react"; -import * as yup from "yup"; -import { UserContext, UserContextType } from "./App"; -import ConnectButton from "./ConnectWallet"; -import { TransactionInvalidBeaconError } from "./TransactionInvalidBeaconError"; -import { address, nat } from "./type-aliases"; - -const itemPerPage: number = 6; - -type OfferEntry = [address, Offer]; - -type Offer = { - price: nat; - quantity: nat; -}; - -const validationSchema = yup.object({ - quantity: yup - .number() - .required("Quantity is required") - .positive("ERROR: The number must be greater than 0!"), -}); - -export default function WineCataloguePage() { - const { - Tezos, - nftContratTokenMetadataMap, - setUserAddress, - setUserBalance, - wallet, - userAddress, - nftContrat, - refreshUserContextOnPageReload, - storage, - } = React.useContext(UserContext) as UserContextType; - const [selectedOfferEntry, setSelectedOfferEntry] = - React.useState(null); - - const formik = useFormik({ - initialValues: { - quantity: 1, - }, - validationSchema: validationSchema, - onSubmit: (values) => { - console.log("onSubmit: (values)", values, selectedOfferEntry); - buy(values.quantity, selectedOfferEntry!); - }, - }); - const { enqueueSnackbar } = useSnackbar(); - const [currentPageIndex, setCurrentPageIndex] = useState(1); - - const buy = async (quantity: number, selectedOfferEntry: OfferEntry) => { - try { - const op = await nftContrat?.methods - .buy(BigNumber(quantity) as nat, selectedOfferEntry[0]) - .send({ - amount: - selectedOfferEntry[1].quantity.toNumber() * - selectedOfferEntry[1].price.toNumber(), - mutez: true, - }); - - await op?.confirmation(2); - - enqueueSnackbar( - "Bought " + - quantity + - " unit of Wine collection (token_id:" + - selectedOfferEntry[0][1] + - ")", - { - variant: "success", - } - ); - - refreshUserContextOnPageReload(); //force all app to refresh the context - } catch (error) { - console.table(`Error: ${JSON.stringify(error, null, 2)}`); - let tibe: TransactionInvalidBeaconError = - new TransactionInvalidBeaconError(error); - enqueueSnackbar(tibe.data_message, { - variant: "error", - autoHideDuration: 10000, - }); - } - }; - const isDesktop = useMediaQuery("(min-width:1100px)"); - const isTablet = useMediaQuery("(min-width:600px)"); - - return ( - - - Wine catalogue - - - {storage?.offers && storage?.offers.size != 0 ? ( - - setCurrentPageIndex(value)} - count={Math.ceil( - Array.from(storage?.offers.entries()).filter(([_, offer]) => - offer.quantity.isGreaterThan(0) - ).length / itemPerPage - )} - showFirstButton - showLastButton - /> - - {Array.from(storage?.offers.entries()) - .filter(([_, offer]) => offer.quantity.isGreaterThan(0)) - .filter((_, index) => - index >= currentPageIndex * itemPerPage - itemPerPage && - index < currentPageIndex * itemPerPage - ? true - : false - ) - .map(([owner, offer]) => ( - - - {"ID : " + 0} - - {"Description : " + - nftContratTokenMetadataMap.get("0") - ?.description} - - {"Seller : " + owner} - - } - > - - - } - title={nftContratTokenMetadataMap.get("0")?.name} - /> - - - - - - {"Price : " + - offer.price.dividedBy(1000000) + - " XTZ/bottle"} - - - {"Available units : " + offer.quantity} - - - - - - {!userAddress ? ( - - - - ) : ( -
{ - setSelectedOfferEntry([owner, offer]); - formik.handleSubmit(values); - }} - > - - - - ), - }} - /> - - )} -
-
- ))} -
-
- ) : ( - - Sorry, there is not NFT to buy yet, you need to mint or sell bottles - first - - )} -
- ); -} -``` - -### Let's play - -- Connect with your wallet and choose **alice** account (or one of the administrators you set on the smart contract earlier). You are redirected to the Administration/mint page as there is no minted NFT yet -- Create an asset, for example: - - `name`: Saint Emilion - Franc la Rose - - `symbol`: SEMIL - - `description`: Grand cru 2007 - - `quantity`: 1000 -- Click on **Upload an image** and select a bottle picture on your computer -- Click on the Mint button - -![minting.png](/images/minting.png) - -Your picture is pushed to IPFS and displayed, then your wallet asks you to sign the `mint` operation. - -- Confirm operation -- Wait less than 1 minute to get the confirmation notification, the page is automatically refreshed. - -![minted.png](/images/minted.png) - -Now you can see the **Trading** menu and the **Bottle offers** sub menu - -Click on the sub-menu entry - -You are the owner of this bottle so you can make an offer on it - -- Enter a quantity -- Enter a price offer -- Click on the **SELL** button -- Wait a bit for the confirmation, then once automatically refreshed you have an offer attached to your NFT - -![offer.png](/images/offer.png) - -For buying, - -- Disconnect from your user and connect with another account _(who has enough XTZ to buy at least 1 bottle)_ -- The buyer will see that alice is selling some bottles from the unique collection -- Buy some bottles while clicking on **BUY** button -- Wait for the confirmation, then the offer is updated on the market _(depending how many bottle you bought)_ -- Click on **bottle offers** sub menu -- You are now the owner of some bottles, you can resell a part of it at your own price, etc ... - -![buy.png](/images/buy_part3.png) - -## Conclusion - -You are now able to play with a unique NFT collection from the Ligo library. - -In the next lesson, you will use the last template **multi-asset** that will allow multiple NFT collections on the same contract - -To continue, let's go to [Part 4](/tutorials/build-an-nft-marketplace/part-4). +## Updating the smart contract + +To use the single-asset template, you must change the code that your smart contract imports from the NFT template to the single-asset template: + +1. In the `nft.jsligo` file, change the first line to this code: + + ```jsligo + #import "@ligo/fa/lib/fa2/asset/single_asset.impl.jsligo" "FA2Impl" + ``` + +1. Change the offer type to store a quantity and a price, as in this code: + + ```ligolang + export type offer = { quantity: nat, price: nat }; + ``` + +1. Change the storage type to use the data types from the single-asset template instead of the NFT template: + + ```ligolang + export type storage = { + administrators: set
, + totalSupply: nat, + offers: map, //user sells an offer + + ledger: FA2Impl.SingleAsset.ledger, + metadata: FA2Impl.TZIP16.metadata, + token_metadata: FA2Impl.TZIP12.tokenMetadata, + operators: FA2Impl.SingleAsset.operators, + }; + ``` + + Now the offers value is indexed on the address of the seller instead of the token ID because there is only one token ID. + Also, the storage now keeps track of the total number of tokens in the `totalSupply` value. + +1. Replace all other references to `FA2Impl.NFT` in the contract with `FA2Impl.SingleAsset`. + You can do a find-replace in the contract to change these values. + +1. Replace the `mint` entrypoint with this code: + + ```ligolang + @entry + const mint = ( + [quantity, name, description, symbol, ipfsUrl]: [ + nat, + bytes, + bytes, + bytes, + bytes + ], + s: storage + ): ret => { + if (quantity <= (0 as nat)) return failwith("0"); + if (! Set.mem(Tezos.get_sender(), s.administrators)) return failwith("1"); + const token_info: map = + Map.literal( + list( + [ + ["name", name], + ["description", description], + ["interfaces", (bytes `["TZIP-12"]`)], + ["artifactUri", ipfsUrl], + ["displayUri", ipfsUrl], + ["thumbnailUri", ipfsUrl], + ["symbol", symbol], + ["decimals", (bytes `0`)] + ] + ) + ) as map; + return [ + list([]) as list, + { + ...s, + totalSupply: quantity, + ledger: Big_map.literal(list([[Tezos.get_sender(), quantity as nat]])) as + FA2Impl.SingleAsset.ledger, + token_metadata: Big_map.add( + 0 as nat, + { token_id: 0 as nat, token_info: token_info }, + s.token_metadata + ), + operators: Big_map.empty as FA2Impl.SingleAsset.operators, + } + ] + }; + ``` + + This updated entrypoint accepts a parameter for the number of tokens to mint and returns that number as the `totalSupply` value. + +1. Replace the `sell` entrypoint with this code: + + ```ligolang + @entry + const sell = ([quantity, price]: [nat, nat], s: storage): ret => { + //check balance of seller + + const sellerBalance = + FA2Impl.SingleAsset.get_amount_for_owner( + { + ledger: s.ledger, + metadata: s.metadata, + operators: s.operators, + token_metadata: s.token_metadata, + } + )(Tezos.get_source()); + if (quantity > sellerBalance) return failwith("2"); + //need to allow the contract itself to be an operator on behalf of the seller + + const newOperators = + FA2Impl.SingleAsset.add_operator(s.operators)(Tezos.get_source())( + Tezos.get_self_address() + ); + //DECISION CHOICE: if offer already exists, we just override it + + return [ + list([]) as list, + { + ...s, + offers: Map.add( + Tezos.get_source(), + { quantity: quantity, price: price }, + s.offers + ), + operators: newOperators + } + ] + }; + ``` + + This updated entrypoint accepts a quantity to offer for sale instead of a token ID. + It also overrides any existing offers for the token. + +1. Replace the `buy` entrypoint with this code: + + ```ligolang + @entry + const buy = ([quantity, seller]: [nat, address], s: storage): ret => { + //search for the offer + + return match(Map.find_opt(seller, s.offers)) { + when (None()): + failwith("3") + when (Some(offer)): + do { + //check if quantity is enough + + if (quantity > offer.quantity) return failwith("4"); + //check if amount have been paid enough + + if (Tezos.get_amount() < (offer.price * quantity) * (1 as mutez)) return failwith( + "5" + ); + // prepare transfer of XTZ to seller + + const op = + Tezos.transaction( + unit, + (offer.price * quantity) * (1 as mutez), + Tezos.get_contract_with_error(seller, "6") + ); + //transfer tokens from seller to buyer + + let ledger = + FA2Impl.SingleAsset.decrease_token_amount_for_user(s.ledger)(seller)( + quantity + ); + ledger + = FA2Impl.SingleAsset.increase_token_amount_for_user(ledger)( + Tezos.get_source() + )(quantity); + //update new offer + + const newOffer = { ...offer, quantity: abs(offer.quantity - quantity) }; + return [ + list([op]) as list, + { + ...s, + offers: Map.update(seller, Some(newOffer), s.offers), + ledger: ledger, + } + ] + } + } + }; + ``` + + This updated entrypoint accepts the quantity of tokens to buy, verifies that the quantity is less than or equal to the quantity offered for sale, verifies the sale price, and updates the offer. + It allows a buyer to buy the full amount of tokens for sale or fewer than the offered amount. + +1. Replace the content of the `nft.storageList.jsligo` file with this code: + + ```ligolang + #import "nft.jsligo" "Contract" + + const default_storage: Contract.storage = { + administrators: Set.literal( + list(["tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb" as address]) + ) as set
, + totalSupply: 0 as nat, + offers: Map.empty as map, + ledger: Big_map.empty as Contract.FA2Impl.SingleAsset.ledger, + metadata: Big_map.literal( + list( + [ + ["", bytes `tezos-storage:data`], + [ + "data", + bytes + `{ + "name":"FA2 NFT Marketplace", + "description":"Example of FA2 implementation", + "version":"0.0.1", + "license":{"name":"MIT"}, + "authors":["Marigold"], + "homepage":"https://marigold.dev", + "source":{ + "tools":["Ligo"], + "location":"https://github.com/ligolang/contract-catalogue/tree/main/lib/fa2"}, + "interfaces":["TZIP-012"], + "errors": [], + "views": [] + }` + ] + ] + ) + ) as Contract.FA2Impl.TZIP16.metadata, + token_metadata: Big_map.empty as Contract.FA2Impl.TZIP12.tokenMetadata, + operators: Big_map.empty as Contract.FA2Impl.SingleAsset.operators, + }; + ``` + +1. As in the previous parts, update the administrators to include addresses that you have access to. + +1. Compile and deploy the new contract: + + ```bash + TAQ_LIGO_IMAGE=ligolang/ligo:1.1.0 taq compile nft.jsligo + taq deploy nft.tz -e "testing" + ``` + +## Updating the frontend + +1. Generate the TypeScript classes and start the server: + + ```bash + taq generate types ./app/src + cd ./app + yarn dev + ``` + +1. In the file `src/App.tsx`, replace the function `refreshUserContextOnPageReload` with this code: + + ```typescript + const refreshUserContextOnPageReload = async () => { + console.log("refreshUserContext"); + //CONTRACT + try { + let c = await Tezos.contract.at(nftContractAddress, tzip12); + console.log("nftContractAddress", nftContractAddress); + + let nftContrat: NftWalletType = await Tezos.wallet.at( + nftContractAddress + ); + const storage = (await nftContrat.storage()) as Storage; + + try { + let tokenMetadata: TZIP21TokenMetadata = (await c + .tzip12() + .getTokenMetadata(0)) as TZIP21TokenMetadata; + nftContratTokenMetadataMap.set("0", tokenMetadata); + + setNftContratTokenMetadataMap(new Map(nftContratTokenMetadataMap)); //new Map to force refresh + } catch (error) { + console.log("error refreshing nftContratTokenMetadataMap: "); + } + + setNftContrat(nftContrat); + setStorage(storage); + } catch (error) { + console.log("error refreshing nft contract: ", error); + } + + //USER + const activeAccount = await wallet.client.getActiveAccount(); + if (activeAccount) { + setUserAddress(activeAccount.address); + const balance = await Tezos.tz.getBalance(activeAccount.address); + setUserBalance(balance.toNumber()); + } + + console.log("refreshUserContext ended."); + }; + ``` + + This update shows information about the single-asset token correctly in the UI. + +1. Replace the content of the `src/MintPage.tsx` file with this code: + + ```typescript + import OpenWithIcon from "@mui/icons-material/OpenWith"; + import { + Button, + CardHeader, + CardMedia, + MobileStepper, + Stack, + SwipeableDrawer, + TextField, + Toolbar, + useMediaQuery, + } from "@mui/material"; + import Box from "@mui/material/Box"; + import Card from "@mui/material/Card"; + import CardContent from "@mui/material/CardContent"; + import Paper from "@mui/material/Paper"; + import Typography from "@mui/material/Typography"; + import { BigNumber } from "bignumber.js"; + import { useSnackbar } from "notistack"; + import React, { useEffect, useState } from "react"; + import { TZIP21TokenMetadata, UserContext, UserContextType } from "./App"; + import { TransactionInvalidBeaconError } from "./TransactionInvalidBeaconError"; + + import { + AddCircleOutlined, + Close, + KeyboardArrowLeft, + KeyboardArrowRight, + } from "@mui/icons-material"; + import { char2Bytes } from "@taquito/utils"; + import { useFormik } from "formik"; + import SwipeableViews from "react-swipeable-views"; + import * as yup from "yup"; + import { address, bytes, nat } from "./type-aliases"; + export default function MintPage() { + const { + userAddress, + storage, + nftContrat, + refreshUserContextOnPageReload, + nftContratTokenMetadataMap, + } = React.useContext(UserContext) as UserContextType; + const { enqueueSnackbar } = useSnackbar(); + const [pictureUrl, setPictureUrl] = useState(""); + const [file, setFile] = useState(null); + + const [activeStep, setActiveStep] = React.useState(0); + + const handleNext = () => { + setActiveStep((prevActiveStep) => prevActiveStep + 1); + }; + + const handleBack = () => { + setActiveStep((prevActiveStep) => prevActiveStep - 1); + }; + + const handleStepChange = (step: number) => { + setActiveStep(step); + }; + const validationSchema = yup.object({ + name: yup.string().required("Name is required"), + description: yup.string().required("Description is required"), + symbol: yup.string().required("Symbol is required"), + quantity: yup + .number() + .required("Quantity is required") + .positive("ERROR: The number must be greater than 0!"), + }); + + const formik = useFormik({ + initialValues: { + name: "", + description: "", + token_id: 0, + symbol: "WINE", + quantity: 1, + } as TZIP21TokenMetadata & { quantity: number }, + validationSchema: validationSchema, + onSubmit: (values) => { + mint(values); + }, + }); + + //open mint drawer if admin + useEffect(() => { + if ( + storage && + storage!.administrators.indexOf(userAddress! as address) < 0 + ) + setFormOpen(false); + else setFormOpen(true); + }, [userAddress]); + + const mint = async ( + newTokenDefinition: TZIP21TokenMetadata & { quantity: number } + ) => { + try { + //IPFS + if (file) { + const formData = new FormData(); + formData.append("file", file); + + const requestHeaders: HeadersInit = new Headers(); + requestHeaders.set( + "pinata_api_key", + `${import.meta.env.VITE_PINATA_API_KEY}` + ); + requestHeaders.set( + "pinata_secret_api_key", + `${import.meta.env.VITE_PINATA_API_SECRET}` + ); + + const resFile = await fetch( + "https://api.pinata.cloud/pinning/pinFileToIPFS", + { + method: "post", + body: formData, + headers: requestHeaders, + } + ); + + const responseJson = await resFile.json(); + console.log("responseJson", responseJson); + + const thumbnailUri = `ipfs://${responseJson.IpfsHash}`; + setPictureUrl( + `https://gateway.pinata.cloud/ipfs/${responseJson.IpfsHash}` + ); + + const op = await nftContrat!.methods + .mint( + new BigNumber(newTokenDefinition.quantity) as nat, + char2Bytes(newTokenDefinition.name!) as bytes, + char2Bytes(newTokenDefinition.description!) as bytes, + char2Bytes(newTokenDefinition.symbol!) as bytes, + char2Bytes(thumbnailUri) as bytes + ) + .send(); + + //close directly the form + setFormOpen(false); + enqueueSnackbar( + "Wine collection is minting ... it will be ready on next block, wait for the confirmation message before minting another collection", + { variant: "info" } + ); + + await op.confirmation(2); + + enqueueSnackbar("Wine collection minted", { variant: "success" }); + + refreshUserContextOnPageReload(); //force all app to refresh the context + } + } catch (error) { + console.table(`Error: ${JSON.stringify(error, null, 2)}`); + let tibe: TransactionInvalidBeaconError = + new TransactionInvalidBeaconError(error); + enqueueSnackbar(tibe.data_message, { + variant: "error", + autoHideDuration: 10000, + }); + } + }; + + const [formOpen, setFormOpen] = useState(false); + + const toggleDrawer = + (open: boolean) => (event: React.KeyboardEvent | React.MouseEvent) => { + if ( + event.type === "keydown" && + ((event as React.KeyboardEvent).key === "Tab" || + (event as React.KeyboardEvent).key === "Shift") + ) { + return; + } + setFormOpen(open); + }; + + const isTablet = useMediaQuery("(min-width:600px)"); + + return ( + + {storage ? ( + + ) : ( + "" + )} + + + + + +
+ + Mint a new collection + + + + + + + + + {pictureUrl ? ( + + ) : ( + "" + )} + + + + +
+
+
+ + Mint your wine collection + + {nftContratTokenMetadataMap.size != 0 ? ( + + + {Array.from(nftContratTokenMetadataMap!.entries()).map( + ([token_id, token]) => ( + + + + + + + + {"ID : " + token_id} + {"Symbol : " + token.symbol} + + {"Description : " + token.description} + + + + + ) + )} + + + Next + + + } + backButton={ + + } + /> + + ) : ( + + Sorry, there is not NFT yet, you need to mint bottles first + + )} +
+ ); + } + ``` + + This update changes the mint function to add a quantity field and remove the token ID field. + +1. Replace the content of the `src/OffersPage.tsx` file with this code: + + ```typescript + import { InfoOutlined } from "@mui/icons-material"; + import SellIcon from "@mui/icons-material/Sell"; + import * as api from "@tzkt/sdk-api"; + + import { + Box, + Button, + Card, + CardActions, + CardContent, + CardHeader, + CardMedia, + ImageList, + InputAdornment, + Pagination, + TextField, + Tooltip, + Typography, + useMediaQuery, + } from "@mui/material"; + import Paper from "@mui/material/Paper"; + import BigNumber from "bignumber.js"; + import { useFormik } from "formik"; + import { useSnackbar } from "notistack"; + import React, { Fragment, useEffect, useState } from "react"; + import * as yup from "yup"; + import { UserContext, UserContextType } from "./App"; + import ConnectButton from "./ConnectWallet"; + import { TransactionInvalidBeaconError } from "./TransactionInvalidBeaconError"; + import { address, nat } from "./type-aliases"; + + const itemPerPage: number = 6; + + const validationSchema = yup.object({ + price: yup + .number() + .required("Price is required") + .positive("ERROR: The number must be greater than 0!"), + quantity: yup + .number() + .required("Quantity is required") + .positive("ERROR: The number must be greater than 0!"), + }); + + type Offer = { + price: nat; + quantity: nat; + }; + + export default function OffersPage() { + api.defaults.baseUrl = "https://api.ghostnet.tzkt.io"; + + const [selectedTokenId, setSelectedTokenId] = React.useState(0); + const [currentPageIndex, setCurrentPageIndex] = useState(1); + + let [ownerOffers, setOwnerOffers] = React.useState(null); + let [ownerBalance, setOwnerBalance] = React.useState(0); + + const { + nftContrat, + nftContratTokenMetadataMap, + userAddress, + storage, + refreshUserContextOnPageReload, + Tezos, + setUserAddress, + setUserBalance, + wallet, + } = React.useContext(UserContext) as UserContextType; + + const { enqueueSnackbar } = useSnackbar(); + + const formik = useFormik({ + initialValues: { + price: 0, + quantity: 1, + }, + validationSchema: validationSchema, + onSubmit: (values) => { + console.log("onSubmit: (values)", values, selectedTokenId); + sell(selectedTokenId, values.quantity, values.price); + }, + }); + + const initPage = async () => { + if (storage) { + console.log("context is not empty, init page now"); + + const ledgerBigMapId = ( + storage.ledger as unknown as { id: BigNumber } + ).id.toNumber(); + + const ownersKeys = await api.bigMapsGetKeys(ledgerBigMapId, { + micheline: "Json", + active: true, + }); + + await Promise.all( + ownersKeys.map(async (ownerKey) => { + if (ownerKey.key === userAddress) { + const ownerBalance = await storage.ledger.get( + userAddress as address + ); + setOwnerBalance(ownerBalance.toNumber()); + const ownerOffers = await storage.offers.get( + userAddress as address + ); + if (ownerOffers && ownerOffers.quantity != BigNumber(0)) + setOwnerOffers(ownerOffers!); + + console.log( + "found for " + + ownerKey.key + + " on token_id " + + 0 + + " with balance " + + ownerBalance + ); + } else { + console.log("skip to next owner"); + } + }) + ); + } else { + console.log("context is empty, wait for parent and retry ..."); + } + }; + + useEffect(() => { + (async () => { + console.log("after a storage changed"); + await initPage(); + })(); + }, [storage]); + + useEffect(() => { + (async () => { + console.log("on Page init"); + await initPage(); + })(); + }, []); + + const sell = async (token_id: number, quantity: number, price: number) => { + try { + const op = await nftContrat?.methods + .sell( + BigNumber(quantity) as nat, + BigNumber(price * 1000000) as nat //to mutez + ) + .send(); + + await op?.confirmation(2); + + enqueueSnackbar( + "Wine collection (token_id=" + + token_id + + ") offer for " + + quantity + + " units at price of " + + price + + " XTZ", + { variant: "success" } + ); + + refreshUserContextOnPageReload(); //force all app to refresh the context + } catch (error) { + console.table(`Error: ${JSON.stringify(error, null, 2)}`); + let tibe: TransactionInvalidBeaconError = + new TransactionInvalidBeaconError(error); + enqueueSnackbar(tibe.data_message, { + variant: "error", + autoHideDuration: 10000, + }); + } + }; + + const isDesktop = useMediaQuery("(min-width:1100px)"); + const isTablet = useMediaQuery("(min-width:600px)"); + return ( + + + Sell my bottles + + {ownerBalance != 0 ? ( + + setCurrentPageIndex(value)} + count={Math.ceil(1 / itemPerPage)} + showFirstButton + showLastButton + /> + + + + + {"ID : " + 0} + + {"Description : " + + nftContratTokenMetadataMap.get("0")?.description} + + + } + > + + + } + title={nftContratTokenMetadataMap.get("0")?.name} + /> + + + + + + {"Owned : " + ownerBalance} + + + {ownerOffers + ? "Traded : " + + ownerOffers?.quantity + + " (price : " + + ownerOffers?.price.dividedBy(1000000) + + " Tz/b)" + : ""} + + + + + + {!userAddress ? ( + + + + ) : ( +
{ + setSelectedTokenId(0); + formik.handleSubmit(values); + }} + > + + + + + + ), + }} + /> + +
+ )} +
+
+
+
+ ) : ( + + Sorry, you don't own any bottles, buy or mint some first + + )} +
+ ); + } + ``` + + This update changes the offers page to allow owners to specify the quantity of tokens to offer for sale. + +1. Replace the content of the `src/WineCataloguePage.tsx` with this code: + + ```typescript + import { InfoOutlined } from "@mui/icons-material"; + import ShoppingCartIcon from "@mui/icons-material/ShoppingCart"; + import { + Button, + Card, + CardActions, + CardContent, + CardHeader, + CardMedia, + ImageList, + InputAdornment, + Pagination, + TextField, + Tooltip, + Typography, + useMediaQuery, + } from "@mui/material"; + import Box from "@mui/material/Box"; + import Paper from "@mui/material/Paper"; + import BigNumber from "bignumber.js"; + import { useFormik } from "formik"; + import { useSnackbar } from "notistack"; + import React, { Fragment, useState } from "react"; + import * as yup from "yup"; + import { UserContext, UserContextType } from "./App"; + import ConnectButton from "./ConnectWallet"; + import { TransactionInvalidBeaconError } from "./TransactionInvalidBeaconError"; + import { address, nat } from "./type-aliases"; + + const itemPerPage: number = 6; + + type OfferEntry = [address, Offer]; + + type Offer = { + price: nat; + quantity: nat; + }; + + const validationSchema = yup.object({ + quantity: yup + .number() + .required("Quantity is required") + .positive("ERROR: The number must be greater than 0!"), + }); + + export default function WineCataloguePage() { + const { + Tezos, + nftContratTokenMetadataMap, + setUserAddress, + setUserBalance, + wallet, + userAddress, + nftContrat, + refreshUserContextOnPageReload, + storage, + } = React.useContext(UserContext) as UserContextType; + const [selectedOfferEntry, setSelectedOfferEntry] = + React.useState(null); + + const formik = useFormik({ + initialValues: { + quantity: 1, + }, + validationSchema: validationSchema, + onSubmit: (values) => { + console.log("onSubmit: (values)", values, selectedOfferEntry); + buy(values.quantity, selectedOfferEntry!); + }, + }); + const { enqueueSnackbar } = useSnackbar(); + const [currentPageIndex, setCurrentPageIndex] = useState(1); + + const buy = async (quantity: number, selectedOfferEntry: OfferEntry) => { + try { + const op = await nftContrat?.methods + .buy(BigNumber(quantity) as nat, selectedOfferEntry[0]) + .send({ + amount: + selectedOfferEntry[1].quantity.toNumber() * + selectedOfferEntry[1].price.toNumber(), + mutez: true, + }); + + await op?.confirmation(2); + + enqueueSnackbar( + "Bought " + + quantity + + " unit of Wine collection (token_id:" + + selectedOfferEntry[0][1] + + ")", + { + variant: "success", + } + ); + + refreshUserContextOnPageReload(); //force all app to refresh the context + } catch (error) { + console.table(`Error: ${JSON.stringify(error, null, 2)}`); + let tibe: TransactionInvalidBeaconError = + new TransactionInvalidBeaconError(error); + enqueueSnackbar(tibe.data_message, { + variant: "error", + autoHideDuration: 10000, + }); + } + }; + const isDesktop = useMediaQuery("(min-width:1100px)"); + const isTablet = useMediaQuery("(min-width:600px)"); + + return ( + + + Wine catalogue + + + {storage?.offers && storage?.offers.size != 0 ? ( + + setCurrentPageIndex(value)} + count={Math.ceil( + Array.from(storage?.offers.entries()).filter(([_, offer]) => + offer.quantity.isGreaterThan(0) + ).length / itemPerPage + )} + showFirstButton + showLastButton + /> + + {Array.from(storage?.offers.entries()) + .filter(([_, offer]) => offer.quantity.isGreaterThan(0)) + .filter((_, index) => + index >= currentPageIndex * itemPerPage - itemPerPage && + index < currentPageIndex * itemPerPage + ? true + : false + ) + .map(([owner, offer]) => ( + + + {"ID : " + 0} + + {"Description : " + + nftContratTokenMetadataMap.get("0") + ?.description} + + {"Seller : " + owner} + + } + > + + + } + title={nftContratTokenMetadataMap.get("0")?.name} + /> + + + + + + {"Price : " + + offer.price.dividedBy(1000000) + + " XTZ/bottle"} + + + {"Available units : " + offer.quantity} + + + + + + {!userAddress ? ( + + + + ) : ( +
{ + setSelectedOfferEntry([owner, offer]); + formik.handleSubmit(values); + }} + > + + + + ), + }} + /> + + )} +
+
+ ))} +
+
+ ) : ( + + Sorry, there is not NFT to buy yet, you need to mint or sell + bottles first + + )} +
+ ); + } + ``` + + Like the other files, this update removes the token ID and adds the quantity field. + +1. As you did in the previous part, connect an administrator's wallet to the application. + +1. Create a token and specify a quantity to mint. + For example, you can use this information: + + - `name`: Saint Emilion - Franc la Rose + - `symbol`: SEMIL + - `description`: Grand cru 2007 + - `quantity`: 1000 + + ![The minting page, showing the creation of 1000 tokens](/img/tutorials/nft-marketplace-3-minting.png) + + When you approve the transaction in your wallet and the transaction completes, the page refreshes automatically and shows the new token. + +1. Click **Trading > Sell bottles**, set the quantity to offer and the price per token, as shown in this picture: + + ![Setting the price and quantity for the offer](/img/tutorials/nft-marketplace-3-offer.png) + +1. Click **Sell** and approve the transaction in your wallet. + +1. When the transaction completes, connect with a different account, click **Trading > Wine catalogue**, and buy some bottles of wine, as shown in this picture: + + ![Buying bottles of wine](/img/tutorials/nft-marketplace-3-buy.png) + +1. When the transaction completes, you can see that the different account owns the tokens and can offer them for sale for a different price. + + You can also get the address of the contract from the `.taq/config.local.testing.json` file and look up the contract in a block explorer. + Because the contract is still FA2 compliant, the block explorer shows the token holders and the quantity of the tokens they have, such as in this picture from the [tzkt.io](https://ghostnet.tzkt.io/) block explorer: + + ![The block explorer showing the accounts that own the token](/img/tutorials/nft-marketplace-3-token-holders.png) + +## Summary + +Now you can manage tokens that have a quantity, but the app can manage only one type of token. + +For the complete content of the contract and web app at the end of this part, see the completed part 3 app at https://github.com/marigold-dev/training-nft-3. + +In the next part, you update the application to create a multi-asset contract that can handle multiple types of tokens with different quantities. + +To continue, go to [Part 4: Handling multi-asset tokens](./part-4). diff --git a/solution/.taq/config.json b/solution/.taq/config.json index 6f7a497..633483d 100644 --- a/solution/.taq/config.json +++ b/solution/.taq/config.json @@ -44,7 +44,7 @@ "testing": { "type": "simple", "label": "ghostnet", - "rpcUrl": "https://ghostnet.ecadinfra.com" + "rpcUrl": "https://ghostnet.tezos.marigold.dev" }, "production": { "type": "simple", @@ -61,6 +61,10 @@ "type": "npm", "name": "@taqueria/plugin-taquito" }, + { + "type": "npm", + "name": "@taqueria/plugin-octez-client" + }, { "type": "npm", "name": "@taqueria/plugin-contract-types" diff --git a/solution/.taq/state.json b/solution/.taq/state.json index e7ca694..a2ffc05 100644 --- a/solution/.taq/state.json +++ b/solution/.taq/state.json @@ -1,7 +1,7 @@ // WARNING: This file is autogenerated and should NOT be modified { - "build": "dcdfa46", - "configHash": "0ba15639dfdc6101118723048dea6ea22f23fc2c217abc1fc2b30003cd67effb", + "build": "0b37e42", + "configHash": "546923f2c7515a39aa0ef0d5d1dc817d38aa9a407997749f10419cd2e7b5bc3f", "tasks": { "ligo": { "type": "npm", @@ -20,8 +20,24 @@ "name": "@taqueria/plugin-ligo" }, "get-image": { - "type": "npm", - "name": "@taqueria/plugin-ligo" + "task": "get-image", + "command": "get-image", + "description": "Provided by more than one plugin. The option --plugin is required.", + "hidden": true, + "handler": "proxy", + "options": [ + { + "flag": "plugin", + "description": "Specify which plugin should be used to execute this task", + "required": true, + "choices": [ + "@taqueria/plugin-ligo", + "ligo", + "@taqueria/plugin-octez-client", + "tezos-client" + ] + } + ] }, "deploy": { "type": "npm", @@ -39,6 +55,22 @@ "type": "npm", "name": "@taqueria/plugin-taquito" }, + "client": { + "type": "npm", + "name": "@taqueria/plugin-octez-client" + }, + "typecheck": { + "type": "npm", + "name": "@taqueria/plugin-octez-client" + }, + "typecheck-all": { + "type": "npm", + "name": "@taqueria/plugin-octez-client" + }, + "simulate": { + "type": "npm", + "name": "@taqueria/plugin-octez-client" + }, "generate types": { "type": "npm", "name": "@taqueria/plugin-contract-types" @@ -57,7 +89,7 @@ "version": "0.1", "schema": "1.0", "alias": "ligo", - "postInstall": "node /home/zamrokk/training-nft-3/solution/node_modules/@taqueria/plugin-ligo/postinstall.js", + "postInstall": "node /home/zamrokk/training-nft-3/solution/node_modules/@taqueria/lib-ligo/postinstall.js", "tasks": [ { "task": "ligo", @@ -264,6 +296,95 @@ "operations": [], "templates": [] }, + { + "name": "@taqueria/plugin-octez-client", + "version": "0.1", + "schema": "1.0", + "alias": "tezos-client", + "tasks": [ + { + "task": "client", + "command": "client", + "description": "This task allows you to run arbitrary octez-client native commands. Note that they might not benefit from the abstractions provided by Taqueria", + "encoding": "none", + "handler": "proxy", + "options": [ + { + "shortFlag": "c", + "flag": "command", + "description": "The command to be passed to the underlying octez-client binary, wrapped in quotes", + "type": "string", + "required": true + } + ] + }, + { + "task": "typecheck", + "command": "typecheck ", + "aliases": [ + "tc" + ], + "description": "Typecheck a Michelson contract", + "encoding": "json", + "handler": "proxy", + "positionals": [ + { + "placeholder": "sourceFile", + "description": "The name of the Michelson contract you wish to typecheck" + } + ] + }, + { + "task": "typecheck-all", + "command": "typecheck-all", + "description": "Typecheck all Michelson contracts in the artifacts directory", + "encoding": "json", + "handler": "proxy" + }, + { + "task": "simulate", + "command": "simulate ", + "aliases": [ + "sim" + ], + "description": "Run a Michelson contract as a simulation", + "encoding": "json", + "handler": "proxy", + "options": [ + { + "flag": "storage", + "description": "Name of the storage file that contains the storage value as a Michelson expression, in the artifacts directory, used for originating a contract", + "required": false + }, + { + "flag": "param", + "description": "Name of the parameter file that contains the parameter value as a Michelson expression, in the artifacts directory, used for invoking a deployed contract", + "required": true + }, + { + "flag": "entrypoint", + "description": "You may explicitly specify an entrypoint to make the parameter value shorter, without having to specify a chain of (Left (Right ... 14 ...))", + "required": false + } + ], + "positionals": [ + { + "placeholder": "sourceFile", + "description": "The name of the Michelson contract you wish to simulate" + } + ] + }, + { + "task": "get-image", + "command": "get-image", + "description": "Gets the name of the image to be used", + "hidden": true, + "handler": "proxy" + } + ], + "operations": [], + "templates": [] + }, { "name": "@taqueria/plugin-contract-types", "version": "0.1", diff --git a/solution/.taq/testing-state.json b/solution/.taq/testing-state.json index d829fb4..9d5b78a 100644 --- a/solution/.taq/testing-state.json +++ b/solution/.taq/testing-state.json @@ -1,45 +1,53 @@ { "operations": {}, "tasks": { - "@taqueria/plugin-taquito.deploy.1696518968789": { + "@taqueria/plugin-taquito.deploy.1699456432249": { "task": "deploy", "plugin": "@taqueria/plugin-taquito", - "time": 1696518968789, + "time": 1699456432249, "output": [ { "contract": "nft.tz", - "address": "KT18sgGX5nu4BzwV2JtpQy4KCqc8cZU5MwnN", + "address": "KT1UhE1Jd1bJqJGfW3kHChXCB7CA3UTkqpkb", "alias": "nft", "balanceInMutez": "0", - "destination": "https://ghostnet.ecadinfra.com" + "destination": "https://ghostnet.tezos.marigold.dev" } ] }, - "@taqueria/plugin-taquito.deploy.1696852728145": { + "@taqueria/plugin-taquito.deploy.1699456678660": { "task": "deploy", "plugin": "@taqueria/plugin-taquito", - "time": 1696852728145, + "time": 1699456678660, "output": [ { "contract": "nft.tz", - "address": "KT1KyV1Hprert33AAz5B94CLkqAHdKZU56dq", - "alias": "nft", - "balanceInMutez": "0", - "destination": "https://ghostnet.ecadinfra.com" + "address": "KT1F56e28pcdTFJot999SWdXr7vuDPu7yYSo", + "alias": "nft" } ] }, - "@taqueria/plugin-taquito.deploy.1696938882527": { + "@taqueria/plugin-taquito.deploy.1699458045314": { "task": "deploy", "plugin": "@taqueria/plugin-taquito", - "time": 1696938882528, + "time": 1699458045314, "output": [ { "contract": "nft.tz", - "address": "KT1EUWEeR9RHMb5q5jeW5jbhxBFHbLTqQgiZ", - "alias": "nft", - "balanceInMutez": "0", - "destination": "https://ghostnet.ecadinfra.com" + "address": "KT18nfu16UutxZNazugLwdnFce78PRTPPTon", + "alias": "nft" + } + ] + }, + "@taqueria/plugin-taquito.deploy.1699458834528": { + "task": "deploy", + "plugin": "@taqueria/plugin-taquito", + "time": 1699458834528, + "output": [ + { + "contract": "nft.tz", + "address": "KT1TLy8qo1rfGtGmraUUQFWeBDSeM6vAQ8f5", + "alias": "nft" } ] } diff --git a/solution/app/.env b/solution/app/.env index 66cec80..a0db4a8 100755 --- a/solution/app/.env +++ b/solution/app/.env @@ -1,4 +1,4 @@ -VITE_CONTRACT_ADDRESS=KT1EUWEeR9RHMb5q5jeW5jbhxBFHbLTqQgiZ +VITE_CONTRACT_ADDRESS=KT1TLy8qo1rfGtGmraUUQFWeBDSeM6vAQ8f5 VITE_PINATA_API_KEY=701d3e9951e2ad5e7d63 VITE_PINATA_API_SECRET=adc566d96514e6ddd076ad531c36b536e7b6f33b36625c136f2100d2909da880 VITE_TEZOS_NODE=https://ghostnet.tezos.marigold.dev diff --git a/solution/app/src/nft.code.ts b/solution/app/src/nft.code.ts index 84a6916..a355d98 100644 --- a/solution/app/src/nft.code.ts +++ b/solution/app/src/nft.code.ts @@ -2,5 +2,5 @@ export const NftCode: { __type: 'NftCode', protocol: string, code: object[] } = { __type: 'NftCode', protocol: 'PtEdo2ZkT9oKpimTah6x2embF25oss54njMuPzkJTEi5RqfdZFA', - code: JSON.parse(`[{"prim":"parameter","args":[{"prim":"or","args":[{"prim":"pair","annots":["%buy"],"args":[{"prim":"nat"},{"prim":"address"}]},{"prim":"or","args":[{"prim":"pair","annots":["%sell"],"args":[{"prim":"nat"},{"prim":"nat"}]},{"prim":"or","args":[{"prim":"pair","annots":["%mint"],"args":[{"prim":"nat"},{"prim":"bytes"},{"prim":"bytes"},{"prim":"bytes"},{"prim":"bytes"}]},{"prim":"or","args":[{"prim":"list","annots":["%update_operators"],"args":[{"prim":"or","args":[{"prim":"pair","annots":["%add_operator"],"args":[{"prim":"address","annots":["%owner"]},{"prim":"address","annots":["%operator"]},{"prim":"nat","annots":["%token_id"]}]},{"prim":"pair","annots":["%remove_operator"],"args":[{"prim":"address","annots":["%owner"]},{"prim":"address","annots":["%operator"]},{"prim":"nat","annots":["%token_id"]}]}]}]},{"prim":"or","args":[{"prim":"pair","annots":["%balance_of"],"args":[{"prim":"list","annots":["%requests"],"args":[{"prim":"pair","args":[{"prim":"address","annots":["%owner"]},{"prim":"nat","annots":["%token_id"]}]}]},{"prim":"contract","annots":["%callback"],"args":[{"prim":"list","args":[{"prim":"pair","args":[{"prim":"pair","annots":["%request"],"args":[{"prim":"address","annots":["%owner"]},{"prim":"nat","annots":["%token_id"]}]},{"prim":"nat","annots":["%balance"]}]}]}]}]},{"prim":"list","annots":["%transfer"],"args":[{"prim":"pair","args":[{"prim":"address","annots":["%from_"]},{"prim":"list","annots":["%txs"],"args":[{"prim":"pair","args":[{"prim":"address","annots":["%to_"]},{"prim":"nat","annots":["%token_id"]},{"prim":"nat","annots":["%amount"]}]}]}]}]}]}]}]}]}]}]},{"prim":"storage","args":[{"prim":"pair","args":[{"prim":"set","annots":["%administrators"],"args":[{"prim":"address"}]},{"prim":"nat","annots":["%totalSupply"]},{"prim":"map","annots":["%offers"],"args":[{"prim":"address"},{"prim":"pair","args":[{"prim":"nat","annots":["%quantity"]},{"prim":"nat","annots":["%price"]}]}]},{"prim":"big_map","annots":["%ledger"],"args":[{"prim":"address"},{"prim":"nat"}]},{"prim":"big_map","annots":["%metadata"],"args":[{"prim":"string"},{"prim":"bytes"}]},{"prim":"big_map","annots":["%token_metadata"],"args":[{"prim":"nat"},{"prim":"pair","args":[{"prim":"nat","annots":["%token_id"]},{"prim":"map","annots":["%token_info"],"args":[{"prim":"string"},{"prim":"bytes"}]}]}]},{"prim":"big_map","annots":["%operators"],"args":[{"prim":"address"},{"prim":"set","args":[{"prim":"address"}]}]}]}]},{"prim":"code","args":[[{"prim":"LAMBDA","args":[{"prim":"address"},{"prim":"unit"},[{"prim":"PUSH","args":[{"prim":"string"},{"string":"The sender can only manage operators for his own token"}]},{"prim":"SENDER"},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"COMPARE"},{"prim":"EQ"},{"prim":"IF","args":[[{"prim":"DROP"},{"prim":"UNIT"}],[{"prim":"FAILWITH"}]]}]]},{"prim":"LAMBDA","args":[{"prim":"pair","args":[{"prim":"lambda","args":[{"prim":"address"},{"prim":"unit"}]},{"prim":"pair","args":[{"prim":"big_map","args":[{"prim":"address"},{"prim":"set","args":[{"prim":"address"}]}]},{"prim":"address"},{"prim":"address"}]}]},{"prim":"big_map","args":[{"prim":"address"},{"prim":"set","args":[{"prim":"address"}]}]},[{"prim":"UNPAIR"},{"prim":"SWAP"},{"prim":"UNPAIR","args":[{"int":"3"}]},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"COMPARE"},{"prim":"EQ"},{"prim":"IF","args":[[{"prim":"SWAP"},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"DIG","args":[{"int":"3"}]},{"prim":"DROP","args":[{"int":"3"}]}],[{"prim":"DUP","args":[{"int":"2"}]},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"SWAP"},{"prim":"EXEC"},{"prim":"DROP"},{"prim":"DUP"},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"GET"},{"prim":"IF_NONE","args":[[{"prim":"EMPTY_SET","args":[{"prim":"address"}]}],[]]},{"prim":"DIG","args":[{"int":"3"}]},{"prim":"PUSH","args":[{"prim":"bool"},{"prim":"True"}]},{"prim":"SWAP"},{"prim":"UPDATE"},{"prim":"SOME"},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"UPDATE"}]]}]]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"APPLY"},{"prim":"LAMBDA","args":[{"prim":"pair","args":[{"prim":"big_map","args":[{"prim":"address"},{"prim":"nat"}]},{"prim":"address"}]},{"prim":"nat"},[{"prim":"UNPAIR"},{"prim":"SWAP"},{"prim":"GET"},{"prim":"IF_NONE","args":[[{"prim":"PUSH","args":[{"prim":"nat"},{"int":"0"}]}],[]]}]]},{"prim":"LAMBDA","args":[{"prim":"pair","args":[{"prim":"big_map","args":[{"prim":"address"},{"prim":"nat"}]},{"prim":"address"},{"prim":"nat"}]},{"prim":"big_map","args":[{"prim":"address"},{"prim":"nat"}]},[{"prim":"UNPAIR","args":[{"int":"3"}]},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"SOME"},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"UPDATE"}]]},{"prim":"LAMBDA","args":[{"prim":"pair","args":[{"prim":"pair","args":[{"prim":"lambda","args":[{"prim":"pair","args":[{"prim":"big_map","args":[{"prim":"address"},{"prim":"nat"}]},{"prim":"address"},{"prim":"nat"}]},{"prim":"big_map","args":[{"prim":"address"},{"prim":"nat"}]}]},{"prim":"lambda","args":[{"prim":"pair","args":[{"prim":"big_map","args":[{"prim":"address"},{"prim":"nat"}]},{"prim":"address"}]},{"prim":"nat"}]}]},{"prim":"pair","args":[{"prim":"big_map","args":[{"prim":"address"},{"prim":"nat"}]},{"prim":"address"},{"prim":"nat"}]}]},{"prim":"big_map","args":[{"prim":"address"},{"prim":"nat"}]},[{"prim":"UNPAIR"},{"prim":"UNPAIR"},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"UNPAIR","args":[{"int":"3"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"PAIR"},{"prim":"DIG","args":[{"int":"5"}]},{"prim":"SWAP"},{"prim":"EXEC"},{"prim":"PUSH","args":[{"prim":"string"},{"string":"FA2_INSUFFICIENT_BALANCE"}]},{"prim":"DUP","args":[{"int":"5"}]},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"COMPARE"},{"prim":"GE"},{"prim":"IF","args":[[{"prim":"DROP"}],[{"prim":"FAILWITH"}]]},{"prim":"DIG","args":[{"int":"3"}]},{"prim":"SWAP"},{"prim":"SUB"},{"prim":"ABS"},{"prim":"DUG","args":[{"int":"2"}]},{"prim":"PAIR","args":[{"int":"3"}]},{"prim":"EXEC"}]]},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"PAIR"},{"prim":"APPLY"},{"prim":"LAMBDA","args":[{"prim":"pair","args":[{"prim":"pair","args":[{"prim":"lambda","args":[{"prim":"pair","args":[{"prim":"big_map","args":[{"prim":"address"},{"prim":"nat"}]},{"prim":"address"},{"prim":"nat"}]},{"prim":"big_map","args":[{"prim":"address"},{"prim":"nat"}]}]},{"prim":"lambda","args":[{"prim":"pair","args":[{"prim":"big_map","args":[{"prim":"address"},{"prim":"nat"}]},{"prim":"address"}]},{"prim":"nat"}]}]},{"prim":"pair","args":[{"prim":"big_map","args":[{"prim":"address"},{"prim":"nat"}]},{"prim":"address"},{"prim":"nat"}]}]},{"prim":"big_map","args":[{"prim":"address"},{"prim":"nat"}]},[{"prim":"UNPAIR"},{"prim":"UNPAIR"},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"UNPAIR","args":[{"int":"3"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"PAIR"},{"prim":"DIG","args":[{"int":"5"}]},{"prim":"SWAP"},{"prim":"EXEC"},{"prim":"DIG","args":[{"int":"3"}]},{"prim":"ADD"},{"prim":"DUG","args":[{"int":"2"}]},{"prim":"PAIR","args":[{"int":"3"}]},{"prim":"EXEC"}]]},{"prim":"DUP","args":[{"int":"4"}]},{"prim":"DUP","args":[{"int":"4"}]},{"prim":"PAIR"},{"prim":"APPLY"},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"DROP"},{"prim":"LAMBDA","args":[{"prim":"pair","args":[{"prim":"lambda","args":[{"prim":"pair","args":[{"prim":"big_map","args":[{"prim":"address"},{"prim":"nat"}]},{"prim":"address"}]},{"prim":"nat"}]},{"prim":"pair","args":[{"prim":"pair","args":[{"prim":"big_map","args":[{"prim":"address"},{"prim":"nat"}]},{"prim":"big_map","args":[{"prim":"address"},{"prim":"set","args":[{"prim":"address"}]}]},{"prim":"big_map","args":[{"prim":"nat"},{"prim":"pair","args":[{"prim":"nat"},{"prim":"map","args":[{"prim":"string"},{"prim":"bytes"}]}]}]},{"prim":"big_map","args":[{"prim":"string"},{"prim":"bytes"}]}]},{"prim":"address"}]}]},{"prim":"nat"},[{"prim":"UNPAIR"},{"prim":"SWAP"},{"prim":"UNPAIR"},{"prim":"CAR"},{"prim":"PAIR"},{"prim":"EXEC"}]]},{"prim":"DUP","args":[{"int":"4"}]},{"prim":"APPLY"},{"prim":"DIG","args":[{"int":"3"}]},{"prim":"DROP"},{"prim":"DIG","args":[{"int":"5"}]},{"prim":"UNPAIR"},{"prim":"IF_LEFT","args":[[{"prim":"DIG","args":[{"int":"2"}]},{"prim":"DIG","args":[{"int":"5"}]},{"prim":"DIG","args":[{"int":"6"}]},{"prim":"DROP","args":[{"int":"3"}]},{"prim":"UNPAIR"},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"GET","args":[{"int":"5"}]},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"GET"},{"prim":"IF_NONE","args":[[{"prim":"DROP","args":[{"int":"5"}]},{"prim":"PUSH","args":[{"prim":"string"},{"string":"3"}]},{"prim":"FAILWITH"}],[{"prim":"DUP"},{"prim":"CAR"},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"COMPARE"},{"prim":"GT"},{"prim":"IF","args":[[{"prim":"DROP","args":[{"int":"6"}]},{"prim":"PUSH","args":[{"prim":"string"},{"string":"4"}]},{"prim":"FAILWITH"}],[{"prim":"PUSH","args":[{"prim":"mutez"},{"int":"1"}]},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"CDR"},{"prim":"MUL"},{"prim":"MUL"},{"prim":"AMOUNT"},{"prim":"COMPARE"},{"prim":"LT"},{"prim":"IF","args":[[{"prim":"DROP","args":[{"int":"6"}]},{"prim":"PUSH","args":[{"prim":"string"},{"string":"5"}]},{"prim":"FAILWITH"}],[{"prim":"DUP","args":[{"int":"3"}]},{"prim":"CONTRACT","args":[{"prim":"unit"}]},{"prim":"IF_NONE","args":[[{"prim":"PUSH","args":[{"prim":"string"},{"string":"6"}]},{"prim":"FAILWITH"}],[]]},{"prim":"PUSH","args":[{"prim":"mutez"},{"int":"1"}]},{"prim":"DUP","args":[{"int":"4"}]},{"prim":"DUP","args":[{"int":"4"}]},{"prim":"CDR"},{"prim":"MUL"},{"prim":"MUL"},{"prim":"UNIT"},{"prim":"TRANSFER_TOKENS"},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"DUP","args":[{"int":"5"}]},{"prim":"DUP","args":[{"int":"7"}]},{"prim":"GET","args":[{"int":"7"}]},{"prim":"PAIR","args":[{"int":"3"}]},{"prim":"DIG","args":[{"int":"7"}]},{"prim":"SWAP"},{"prim":"EXEC"},{"prim":"DUP","args":[{"int":"4"}]},{"prim":"SOURCE"},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"PAIR","args":[{"int":"3"}]},{"prim":"DIG","args":[{"int":"6"}]},{"prim":"SWAP"},{"prim":"EXEC"},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"CAR"},{"prim":"SUB"},{"prim":"ABS"},{"prim":"UPDATE","args":[{"int":"1"}]},{"prim":"DUP","args":[{"int":"5"}]},{"prim":"DIG","args":[{"int":"5"}]},{"prim":"GET","args":[{"int":"5"}]},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"SOME"},{"prim":"DIG","args":[{"int":"5"}]},{"prim":"UPDATE"},{"prim":"UPDATE","args":[{"int":"5"}]},{"prim":"SWAP"},{"prim":"UPDATE","args":[{"int":"7"}]},{"prim":"NIL","args":[{"prim":"operation"}]},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"CONS"},{"prim":"PAIR"}]]}]]}]]}],[{"prim":"IF_LEFT","args":[[{"prim":"DIG","args":[{"int":"3"}]},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"DIG","args":[{"int":"6"}]},{"prim":"DROP","args":[{"int":"3"}]},{"prim":"UNPAIR"},{"prim":"SOURCE"},{"prim":"DUP","args":[{"int":"4"}]},{"prim":"GET","args":[{"int":"9"}]},{"prim":"DUP","args":[{"int":"5"}]},{"prim":"GET","args":[{"int":"11"}]},{"prim":"DUP","args":[{"int":"6"}]},{"prim":"GET","args":[{"int":"12"}]},{"prim":"DUP","args":[{"int":"7"}]},{"prim":"GET","args":[{"int":"7"}]},{"prim":"PAIR","args":[{"int":"4"}]},{"prim":"PAIR"},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"SWAP"},{"prim":"EXEC"},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"COMPARE"},{"prim":"GT"},{"prim":"IF","args":[[{"prim":"DROP","args":[{"int":"4"}]},{"prim":"PUSH","args":[{"prim":"string"},{"string":"2"}]},{"prim":"FAILWITH"}],[{"prim":"SELF_ADDRESS"},{"prim":"SOURCE"},{"prim":"DUP","args":[{"int":"5"}]},{"prim":"GET","args":[{"int":"12"}]},{"prim":"PAIR","args":[{"int":"3"}]},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"SWAP"},{"prim":"EXEC"},{"prim":"DUP","args":[{"int":"4"}]},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"GET","args":[{"int":"5"}]},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"PAIR"},{"prim":"SOURCE"},{"prim":"SWAP"},{"prim":"SOME"},{"prim":"SWAP"},{"prim":"UPDATE"},{"prim":"UPDATE","args":[{"int":"5"}]},{"prim":"SWAP"},{"prim":"UPDATE","args":[{"int":"12"}]},{"prim":"NIL","args":[{"prim":"operation"}]},{"prim":"PAIR"}]]}],[{"prim":"IF_LEFT","args":[[{"prim":"DIG","args":[{"int":"2"}]},{"prim":"DIG","args":[{"int":"3"}]},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"DIG","args":[{"int":"5"}]},{"prim":"DIG","args":[{"int":"6"}]},{"prim":"DROP","args":[{"int":"5"}]},{"prim":"UNPAIR","args":[{"int":"5"}]},{"prim":"PUSH","args":[{"prim":"nat"},{"int":"0"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"COMPARE"},{"prim":"LE"},{"prim":"IF","args":[[{"prim":"DROP","args":[{"int":"6"}]},{"prim":"PUSH","args":[{"prim":"string"},{"string":"0"}]},{"prim":"FAILWITH"}],[{"prim":"DUP","args":[{"int":"6"}]},{"prim":"CAR"},{"prim":"SENDER"},{"prim":"MEM"},{"prim":"NOT"},{"prim":"IF","args":[[{"prim":"DROP","args":[{"int":"6"}]},{"prim":"PUSH","args":[{"prim":"string"},{"string":"1"}]},{"prim":"FAILWITH"}],[{"prim":"DUP","args":[{"int":"6"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"UPDATE","args":[{"int":"3"}]},{"prim":"EMPTY_BIG_MAP","args":[{"prim":"address"},{"prim":"nat"}]},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"SOME"},{"prim":"SENDER"},{"prim":"UPDATE"},{"prim":"UPDATE","args":[{"int":"7"}]},{"prim":"DIG","args":[{"int":"5"}]},{"prim":"GET","args":[{"int":"11"}]},{"prim":"EMPTY_MAP","args":[{"prim":"string"},{"prim":"bytes"}]},{"prim":"PUSH","args":[{"prim":"bytes"},{"bytes":"30"}]},{"prim":"SOME"},{"prim":"PUSH","args":[{"prim":"string"},{"string":"decimals"}]},{"prim":"UPDATE"},{"prim":"DIG","args":[{"int":"5"}]},{"prim":"SOME"},{"prim":"PUSH","args":[{"prim":"string"},{"string":"symbol"}]},{"prim":"UPDATE"},{"prim":"DIG","args":[{"int":"5"}]},{"prim":"SOME"},{"prim":"PUSH","args":[{"prim":"string"},{"string":"thumbnailUri"}]},{"prim":"UPDATE"},{"prim":"PUSH","args":[{"prim":"bytes"},{"bytes":"5b22545a49502d3132225d"}]},{"prim":"SOME"},{"prim":"PUSH","args":[{"prim":"string"},{"string":"interfaces"}]},{"prim":"UPDATE"},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"SOME"},{"prim":"PUSH","args":[{"prim":"string"},{"string":"description"}]},{"prim":"UPDATE"},{"prim":"DIG","args":[{"int":"3"}]},{"prim":"SOME"},{"prim":"PUSH","args":[{"prim":"string"},{"string":"name"}]},{"prim":"UPDATE"},{"prim":"PUSH","args":[{"prim":"nat"},{"int":"0"}]},{"prim":"PAIR"},{"prim":"SOME"},{"prim":"PUSH","args":[{"prim":"nat"},{"int":"0"}]},{"prim":"UPDATE"},{"prim":"UPDATE","args":[{"int":"11"}]},{"prim":"EMPTY_BIG_MAP","args":[{"prim":"address"},{"prim":"set","args":[{"prim":"address"}]}]},{"prim":"UPDATE","args":[{"int":"12"}]},{"prim":"NIL","args":[{"prim":"operation"}]},{"prim":"PAIR"}]]}]]}],[{"prim":"IF_LEFT","args":[[{"prim":"DIG","args":[{"int":"2"}]},{"prim":"DIG","args":[{"int":"3"}]},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"DROP","args":[{"int":"3"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"GET","args":[{"int":"9"}]},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"GET","args":[{"int":"11"}]},{"prim":"DUP","args":[{"int":"4"}]},{"prim":"GET","args":[{"int":"12"}]},{"prim":"DUP","args":[{"int":"5"}]},{"prim":"GET","args":[{"int":"7"}]},{"prim":"PAIR","args":[{"int":"4"}]},{"prim":"DUP"},{"prim":"GET","args":[{"int":"3"}]},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"ITER","args":[[{"prim":"IF_LEFT","args":[[{"prim":"DUP"},{"prim":"GET","args":[{"int":"3"}]},{"prim":"SWAP"},{"prim":"CAR"},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"PAIR","args":[{"int":"3"}]},{"prim":"DUP","args":[{"int":"4"}]},{"prim":"SWAP"},{"prim":"EXEC"}],[{"prim":"DUP"},{"prim":"GET","args":[{"int":"3"}]},{"prim":"SWAP"},{"prim":"CAR"},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"COMPARE"},{"prim":"EQ"},{"prim":"IF","args":[[{"prim":"DROP","args":[{"int":"2"}]}],[{"prim":"DUP"},{"prim":"DUP","args":[{"int":"8"}]},{"prim":"SWAP"},{"prim":"EXEC"},{"prim":"DROP"},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"DIG","args":[{"int":"3"}]},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"GET"},{"prim":"IF_NONE","args":[[{"prim":"DIG","args":[{"int":"2"}]},{"prim":"DROP"},{"prim":"NONE","args":[{"prim":"set","args":[{"prim":"address"}]}]}],[{"prim":"PUSH","args":[{"prim":"nat"},{"int":"0"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"DUP","args":[{"int":"6"}]},{"prim":"PUSH","args":[{"prim":"bool"},{"prim":"False"}]},{"prim":"SWAP"},{"prim":"UPDATE"},{"prim":"SIZE"},{"prim":"COMPARE"},{"prim":"EQ"},{"prim":"IF","args":[[{"prim":"DIG","args":[{"int":"3"}]},{"prim":"DROP","args":[{"int":"2"}]},{"prim":"NONE","args":[{"prim":"set","args":[{"prim":"address"}]}]}],[{"prim":"DIG","args":[{"int":"3"}]},{"prim":"PUSH","args":[{"prim":"bool"},{"prim":"False"}]},{"prim":"SWAP"},{"prim":"UPDATE"},{"prim":"SOME"}]]}]]},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"UPDATE"}]]}]]}]]},{"prim":"DIG","args":[{"int":"3"}]},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"DROP","args":[{"int":"2"}]},{"prim":"UPDATE","args":[{"int":"3"}]},{"prim":"NIL","args":[{"prim":"operation"}]},{"prim":"PAIR"},{"prim":"SWAP"},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"CDR"},{"prim":"CAR"},{"prim":"UPDATE","args":[{"int":"7"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"CDR"},{"prim":"GET","args":[{"int":"6"}]},{"prim":"UPDATE","args":[{"int":"9"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"CDR"},{"prim":"GET","args":[{"int":"5"}]},{"prim":"UPDATE","args":[{"int":"11"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"CDR"},{"prim":"GET","args":[{"int":"3"}]},{"prim":"UPDATE","args":[{"int":"12"}]},{"prim":"SWAP"},{"prim":"CAR"}],[{"prim":"DIG","args":[{"int":"5"}]},{"prim":"DIG","args":[{"int":"6"}]},{"prim":"DROP","args":[{"int":"2"}]},{"prim":"IF_LEFT","args":[[{"prim":"DIG","args":[{"int":"3"}]},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"DROP","args":[{"int":"2"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"GET","args":[{"int":"9"}]},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"GET","args":[{"int":"11"}]},{"prim":"DUP","args":[{"int":"4"}]},{"prim":"GET","args":[{"int":"12"}]},{"prim":"DUP","args":[{"int":"5"}]},{"prim":"GET","args":[{"int":"7"}]},{"prim":"PAIR","args":[{"int":"4"}]},{"prim":"SWAP"},{"prim":"UNPAIR"},{"prim":"MAP","args":[[{"prim":"DUP"},{"prim":"CAR"},{"prim":"DUP","args":[{"int":"4"}]},{"prim":"PAIR"},{"prim":"DUP","args":[{"int":"6"}]},{"prim":"SWAP"},{"prim":"EXEC"},{"prim":"SWAP"},{"prim":"PAIR"}]]},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"DROP"},{"prim":"SWAP"},{"prim":"PUSH","args":[{"prim":"mutez"},{"int":"0"}]},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"TRANSFER_TOKENS"},{"prim":"SWAP"},{"prim":"NIL","args":[{"prim":"operation"}]},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"CONS"},{"prim":"PAIR"},{"prim":"SWAP"},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"CDR"},{"prim":"CAR"},{"prim":"UPDATE","args":[{"int":"7"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"CDR"},{"prim":"GET","args":[{"int":"6"}]},{"prim":"UPDATE","args":[{"int":"9"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"CDR"},{"prim":"GET","args":[{"int":"5"}]},{"prim":"UPDATE","args":[{"int":"11"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"CDR"},{"prim":"GET","args":[{"int":"3"}]},{"prim":"UPDATE","args":[{"int":"12"}]},{"prim":"SWAP"},{"prim":"CAR"}],[{"prim":"DIG","args":[{"int":"2"}]},{"prim":"DROP"},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"GET","args":[{"int":"9"}]},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"GET","args":[{"int":"11"}]},{"prim":"DUP","args":[{"int":"4"}]},{"prim":"GET","args":[{"int":"12"}]},{"prim":"DUP","args":[{"int":"5"}]},{"prim":"GET","args":[{"int":"7"}]},{"prim":"PAIR","args":[{"int":"4"}]},{"prim":"DUP"},{"prim":"CAR"},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"ITER","args":[[{"prim":"UNPAIR"},{"prim":"DUG","args":[{"int":"2"}]},{"prim":"ITER","args":[[{"prim":"UNPAIR","args":[{"int":"3"}]},{"prim":"SWAP"},{"prim":"DROP"},{"prim":"SENDER"},{"prim":"DUP","args":[{"int":"5"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"COMPARE"},{"prim":"EQ"},{"prim":"IF","args":[[{"prim":"DROP"}],[{"prim":"DUP","args":[{"int":"6"}]},{"prim":"GET","args":[{"int":"3"}]},{"prim":"DUP","args":[{"int":"6"}]},{"prim":"GET"},{"prim":"IF_NONE","args":[[{"prim":"EMPTY_SET","args":[{"prim":"address"}]}],[]]},{"prim":"SWAP"},{"prim":"MEM"},{"prim":"IF","args":[[],[{"prim":"PUSH","args":[{"prim":"string"},{"string":"FA2_NOT_OPERATOR"}]},{"prim":"FAILWITH"}]]}]]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"DUP","args":[{"int":"5"}]},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"PAIR","args":[{"int":"3"}]},{"prim":"DUP","args":[{"int":"8"}]},{"prim":"SWAP"},{"prim":"EXEC"},{"prim":"PAIR","args":[{"int":"3"}]},{"prim":"DUP","args":[{"int":"5"}]},{"prim":"SWAP"},{"prim":"EXEC"}]]},{"prim":"SWAP"},{"prim":"DROP"}]]},{"prim":"DIG","args":[{"int":"3"}]},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"DROP","args":[{"int":"2"}]},{"prim":"UPDATE","args":[{"int":"1"}]},{"prim":"NIL","args":[{"prim":"operation"}]},{"prim":"PAIR"},{"prim":"SWAP"},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"CDR"},{"prim":"CAR"},{"prim":"UPDATE","args":[{"int":"7"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"CDR"},{"prim":"GET","args":[{"int":"6"}]},{"prim":"UPDATE","args":[{"int":"9"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"CDR"},{"prim":"GET","args":[{"int":"5"}]},{"prim":"UPDATE","args":[{"int":"11"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"CDR"},{"prim":"GET","args":[{"int":"3"}]},{"prim":"UPDATE","args":[{"int":"12"}]},{"prim":"SWAP"},{"prim":"CAR"}]]}]]},{"prim":"PAIR"}]]}]]}]]}]]}]`) + code: JSON.parse(`[{"prim":"parameter","args":[{"prim":"or","args":[{"prim":"pair","annots":["%buy"],"args":[{"prim":"nat"},{"prim":"address"}]},{"prim":"or","args":[{"prim":"pair","annots":["%sell"],"args":[{"prim":"nat"},{"prim":"nat"}]},{"prim":"or","args":[{"prim":"pair","annots":["%mint"],"args":[{"prim":"nat"},{"prim":"bytes"},{"prim":"bytes"},{"prim":"bytes"},{"prim":"bytes"}]},{"prim":"or","args":[{"prim":"list","annots":["%update_operators"],"args":[{"prim":"or","args":[{"prim":"pair","annots":["%add_operator"],"args":[{"prim":"address","annots":["%owner"]},{"prim":"address","annots":["%operator"]},{"prim":"nat","annots":["%token_id"]}]},{"prim":"pair","annots":["%remove_operator"],"args":[{"prim":"address","annots":["%owner"]},{"prim":"address","annots":["%operator"]},{"prim":"nat","annots":["%token_id"]}]}]}]},{"prim":"or","args":[{"prim":"pair","annots":["%balance_of"],"args":[{"prim":"list","annots":["%requests"],"args":[{"prim":"pair","args":[{"prim":"address","annots":["%owner"]},{"prim":"nat","annots":["%token_id"]}]}]},{"prim":"contract","annots":["%callback"],"args":[{"prim":"list","args":[{"prim":"pair","args":[{"prim":"pair","annots":["%request"],"args":[{"prim":"address","annots":["%owner"]},{"prim":"nat","annots":["%token_id"]}]},{"prim":"nat","annots":["%balance"]}]}]}]}]},{"prim":"list","annots":["%transfer"],"args":[{"prim":"pair","args":[{"prim":"address","annots":["%from_"]},{"prim":"list","annots":["%txs"],"args":[{"prim":"pair","args":[{"prim":"address","annots":["%to_"]},{"prim":"nat","annots":["%token_id"]},{"prim":"nat","annots":["%amount"]}]}]}]}]}]}]}]}]}]}]},{"prim":"storage","args":[{"prim":"pair","args":[{"prim":"set","annots":["%administrators"],"args":[{"prim":"address"}]},{"prim":"nat","annots":["%totalSupply"]},{"prim":"map","annots":["%offers"],"args":[{"prim":"address"},{"prim":"pair","args":[{"prim":"nat","annots":["%quantity"]},{"prim":"nat","annots":["%price"]}]}]},{"prim":"big_map","annots":["%ledger"],"args":[{"prim":"address"},{"prim":"nat"}]},{"prim":"big_map","annots":["%metadata"],"args":[{"prim":"string"},{"prim":"bytes"}]},{"prim":"big_map","annots":["%token_metadata"],"args":[{"prim":"nat"},{"prim":"pair","args":[{"prim":"nat","annots":["%token_id"]},{"prim":"map","annots":["%token_info"],"args":[{"prim":"string"},{"prim":"bytes"}]}]}]},{"prim":"big_map","annots":["%operators"],"args":[{"prim":"address"},{"prim":"set","args":[{"prim":"address"}]}]}]}]},{"prim":"code","args":[[{"prim":"LAMBDA","args":[{"prim":"address"},{"prim":"unit"},[{"prim":"PUSH","args":[{"prim":"string"},{"string":"The sender can only manage operators for his own token"}]},{"prim":"SENDER"},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"COMPARE"},{"prim":"EQ"},{"prim":"IF","args":[[{"prim":"DROP"},{"prim":"UNIT"}],[{"prim":"FAILWITH"}]]}]]},{"prim":"LAMBDA","args":[{"prim":"pair","args":[{"prim":"lambda","args":[{"prim":"address"},{"prim":"unit"}]},{"prim":"pair","args":[{"prim":"big_map","args":[{"prim":"address"},{"prim":"set","args":[{"prim":"address"}]}]},{"prim":"address"},{"prim":"address"}]}]},{"prim":"big_map","args":[{"prim":"address"},{"prim":"set","args":[{"prim":"address"}]}]},[{"prim":"UNPAIR"},{"prim":"SWAP"},{"prim":"UNPAIR","args":[{"int":"3"}]},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"COMPARE"},{"prim":"EQ"},{"prim":"IF","args":[[{"prim":"SWAP"},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"DIG","args":[{"int":"3"}]},{"prim":"DROP","args":[{"int":"3"}]}],[{"prim":"DUP","args":[{"int":"2"}]},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"SWAP"},{"prim":"EXEC"},{"prim":"DROP"},{"prim":"DUP"},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"GET"},{"prim":"IF_NONE","args":[[{"prim":"EMPTY_SET","args":[{"prim":"address"}]}],[]]},{"prim":"DIG","args":[{"int":"3"}]},{"prim":"PUSH","args":[{"prim":"bool"},{"prim":"True"}]},{"prim":"SWAP"},{"prim":"UPDATE"},{"prim":"SOME"},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"UPDATE"}]]}]]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"APPLY"},{"prim":"LAMBDA","args":[{"prim":"pair","args":[{"prim":"big_map","args":[{"prim":"address"},{"prim":"nat"}]},{"prim":"address"}]},{"prim":"nat"},[{"prim":"UNPAIR"},{"prim":"SWAP"},{"prim":"GET"},{"prim":"IF_NONE","args":[[{"prim":"PUSH","args":[{"prim":"nat"},{"int":"0"}]}],[]]}]]},{"prim":"LAMBDA","args":[{"prim":"pair","args":[{"prim":"big_map","args":[{"prim":"address"},{"prim":"nat"}]},{"prim":"address"},{"prim":"nat"}]},{"prim":"big_map","args":[{"prim":"address"},{"prim":"nat"}]},[{"prim":"UNPAIR","args":[{"int":"3"}]},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"SOME"},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"UPDATE"}]]},{"prim":"LAMBDA","args":[{"prim":"pair","args":[{"prim":"pair","args":[{"prim":"lambda","args":[{"prim":"pair","args":[{"prim":"big_map","args":[{"prim":"address"},{"prim":"nat"}]},{"prim":"address"},{"prim":"nat"}]},{"prim":"big_map","args":[{"prim":"address"},{"prim":"nat"}]}]},{"prim":"lambda","args":[{"prim":"pair","args":[{"prim":"big_map","args":[{"prim":"address"},{"prim":"nat"}]},{"prim":"address"}]},{"prim":"nat"}]}]},{"prim":"pair","args":[{"prim":"big_map","args":[{"prim":"address"},{"prim":"nat"}]},{"prim":"address"},{"prim":"nat"}]}]},{"prim":"big_map","args":[{"prim":"address"},{"prim":"nat"}]},[{"prim":"UNPAIR"},{"prim":"UNPAIR"},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"UNPAIR","args":[{"int":"3"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"PAIR"},{"prim":"DIG","args":[{"int":"5"}]},{"prim":"SWAP"},{"prim":"EXEC"},{"prim":"PUSH","args":[{"prim":"string"},{"string":"FA2_INSUFFICIENT_BALANCE"}]},{"prim":"DUP","args":[{"int":"5"}]},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"COMPARE"},{"prim":"GE"},{"prim":"IF","args":[[{"prim":"DROP"}],[{"prim":"FAILWITH"}]]},{"prim":"DIG","args":[{"int":"3"}]},{"prim":"SWAP"},{"prim":"SUB"},{"prim":"ABS"},{"prim":"DUG","args":[{"int":"2"}]},{"prim":"PAIR","args":[{"int":"3"}]},{"prim":"EXEC"}]]},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"PAIR"},{"prim":"APPLY"},{"prim":"LAMBDA","args":[{"prim":"pair","args":[{"prim":"pair","args":[{"prim":"lambda","args":[{"prim":"pair","args":[{"prim":"big_map","args":[{"prim":"address"},{"prim":"nat"}]},{"prim":"address"},{"prim":"nat"}]},{"prim":"big_map","args":[{"prim":"address"},{"prim":"nat"}]}]},{"prim":"lambda","args":[{"prim":"pair","args":[{"prim":"big_map","args":[{"prim":"address"},{"prim":"nat"}]},{"prim":"address"}]},{"prim":"nat"}]}]},{"prim":"pair","args":[{"prim":"big_map","args":[{"prim":"address"},{"prim":"nat"}]},{"prim":"address"},{"prim":"nat"}]}]},{"prim":"big_map","args":[{"prim":"address"},{"prim":"nat"}]},[{"prim":"UNPAIR"},{"prim":"UNPAIR"},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"UNPAIR","args":[{"int":"3"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"PAIR"},{"prim":"DIG","args":[{"int":"5"}]},{"prim":"SWAP"},{"prim":"EXEC"},{"prim":"DIG","args":[{"int":"3"}]},{"prim":"ADD"},{"prim":"DUG","args":[{"int":"2"}]},{"prim":"PAIR","args":[{"int":"3"}]},{"prim":"EXEC"}]]},{"prim":"DUP","args":[{"int":"4"}]},{"prim":"DUP","args":[{"int":"4"}]},{"prim":"PAIR"},{"prim":"APPLY"},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"DROP"},{"prim":"LAMBDA","args":[{"prim":"pair","args":[{"prim":"lambda","args":[{"prim":"pair","args":[{"prim":"big_map","args":[{"prim":"address"},{"prim":"nat"}]},{"prim":"address"}]},{"prim":"nat"}]},{"prim":"pair","args":[{"prim":"pair","args":[{"prim":"big_map","args":[{"prim":"address"},{"prim":"nat"}]},{"prim":"big_map","args":[{"prim":"address"},{"prim":"set","args":[{"prim":"address"}]}]},{"prim":"big_map","args":[{"prim":"nat"},{"prim":"pair","args":[{"prim":"nat"},{"prim":"map","args":[{"prim":"string"},{"prim":"bytes"}]}]}]},{"prim":"big_map","args":[{"prim":"string"},{"prim":"bytes"}]}]},{"prim":"address"}]}]},{"prim":"nat"},[{"prim":"UNPAIR"},{"prim":"SWAP"},{"prim":"UNPAIR"},{"prim":"CAR"},{"prim":"PAIR"},{"prim":"EXEC"}]]},{"prim":"DUP","args":[{"int":"4"}]},{"prim":"APPLY"},{"prim":"DIG","args":[{"int":"3"}]},{"prim":"DROP"},{"prim":"DIG","args":[{"int":"5"}]},{"prim":"UNPAIR"},{"prim":"IF_LEFT","args":[[{"prim":"DIG","args":[{"int":"2"}]},{"prim":"DIG","args":[{"int":"5"}]},{"prim":"DIG","args":[{"int":"6"}]},{"prim":"DROP","args":[{"int":"3"}]},{"prim":"UNPAIR"},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"GET","args":[{"int":"5"}]},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"GET"},{"prim":"IF_NONE","args":[[{"prim":"DROP","args":[{"int":"5"}]},{"prim":"PUSH","args":[{"prim":"string"},{"string":"3"}]},{"prim":"FAILWITH"}],[{"prim":"DUP"},{"prim":"CAR"},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"COMPARE"},{"prim":"GT"},{"prim":"IF","args":[[{"prim":"DROP","args":[{"int":"6"}]},{"prim":"PUSH","args":[{"prim":"string"},{"string":"4"}]},{"prim":"FAILWITH"}],[{"prim":"PUSH","args":[{"prim":"mutez"},{"int":"1"}]},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"CDR"},{"prim":"MUL"},{"prim":"MUL"},{"prim":"AMOUNT"},{"prim":"COMPARE"},{"prim":"LT"},{"prim":"IF","args":[[{"prim":"DROP","args":[{"int":"6"}]},{"prim":"PUSH","args":[{"prim":"string"},{"string":"5"}]},{"prim":"FAILWITH"}],[{"prim":"DUP","args":[{"int":"3"}]},{"prim":"CONTRACT","args":[{"prim":"unit"}]},{"prim":"IF_NONE","args":[[{"prim":"PUSH","args":[{"prim":"string"},{"string":"6"}]},{"prim":"FAILWITH"}],[]]},{"prim":"PUSH","args":[{"prim":"mutez"},{"int":"1"}]},{"prim":"DUP","args":[{"int":"4"}]},{"prim":"DUP","args":[{"int":"4"}]},{"prim":"CDR"},{"prim":"MUL"},{"prim":"MUL"},{"prim":"UNIT"},{"prim":"TRANSFER_TOKENS"},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"DUP","args":[{"int":"5"}]},{"prim":"DUP","args":[{"int":"7"}]},{"prim":"GET","args":[{"int":"7"}]},{"prim":"PAIR","args":[{"int":"3"}]},{"prim":"DIG","args":[{"int":"7"}]},{"prim":"SWAP"},{"prim":"EXEC"},{"prim":"DUP","args":[{"int":"4"}]},{"prim":"SOURCE"},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"PAIR","args":[{"int":"3"}]},{"prim":"DIG","args":[{"int":"6"}]},{"prim":"SWAP"},{"prim":"EXEC"},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"CAR"},{"prim":"SUB"},{"prim":"ABS"},{"prim":"UPDATE","args":[{"int":"1"}]},{"prim":"DUP","args":[{"int":"5"}]},{"prim":"DIG","args":[{"int":"5"}]},{"prim":"GET","args":[{"int":"5"}]},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"SOME"},{"prim":"DIG","args":[{"int":"5"}]},{"prim":"UPDATE"},{"prim":"UPDATE","args":[{"int":"5"}]},{"prim":"SWAP"},{"prim":"UPDATE","args":[{"int":"7"}]},{"prim":"NIL","args":[{"prim":"operation"}]},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"CONS"},{"prim":"PAIR"}]]}]]}]]}],[{"prim":"IF_LEFT","args":[[{"prim":"DIG","args":[{"int":"3"}]},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"DIG","args":[{"int":"6"}]},{"prim":"DROP","args":[{"int":"3"}]},{"prim":"UNPAIR"},{"prim":"SOURCE"},{"prim":"DUP","args":[{"int":"4"}]},{"prim":"GET","args":[{"int":"9"}]},{"prim":"DUP","args":[{"int":"5"}]},{"prim":"GET","args":[{"int":"11"}]},{"prim":"DUP","args":[{"int":"6"}]},{"prim":"GET","args":[{"int":"12"}]},{"prim":"DUP","args":[{"int":"7"}]},{"prim":"GET","args":[{"int":"7"}]},{"prim":"PAIR","args":[{"int":"4"}]},{"prim":"PAIR"},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"SWAP"},{"prim":"EXEC"},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"COMPARE"},{"prim":"GT"},{"prim":"IF","args":[[{"prim":"DROP","args":[{"int":"4"}]},{"prim":"PUSH","args":[{"prim":"string"},{"string":"2"}]},{"prim":"FAILWITH"}],[{"prim":"SELF_ADDRESS"},{"prim":"SOURCE"},{"prim":"DUP","args":[{"int":"5"}]},{"prim":"GET","args":[{"int":"12"}]},{"prim":"PAIR","args":[{"int":"3"}]},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"SWAP"},{"prim":"EXEC"},{"prim":"DUP","args":[{"int":"4"}]},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"GET","args":[{"int":"5"}]},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"PAIR"},{"prim":"SOURCE"},{"prim":"SWAP"},{"prim":"SOME"},{"prim":"SWAP"},{"prim":"UPDATE"},{"prim":"UPDATE","args":[{"int":"5"}]},{"prim":"SWAP"},{"prim":"UPDATE","args":[{"int":"12"}]},{"prim":"NIL","args":[{"prim":"operation"}]},{"prim":"PAIR"}]]}],[{"prim":"IF_LEFT","args":[[{"prim":"DIG","args":[{"int":"2"}]},{"prim":"DIG","args":[{"int":"3"}]},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"DIG","args":[{"int":"5"}]},{"prim":"DIG","args":[{"int":"6"}]},{"prim":"DROP","args":[{"int":"5"}]},{"prim":"UNPAIR","args":[{"int":"5"}]},{"prim":"PUSH","args":[{"prim":"nat"},{"int":"0"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"COMPARE"},{"prim":"LE"},{"prim":"IF","args":[[{"prim":"DROP","args":[{"int":"6"}]},{"prim":"PUSH","args":[{"prim":"string"},{"string":"0"}]},{"prim":"FAILWITH"}],[{"prim":"DUP","args":[{"int":"6"}]},{"prim":"CAR"},{"prim":"SENDER"},{"prim":"MEM"},{"prim":"NOT"},{"prim":"IF","args":[[{"prim":"DROP","args":[{"int":"6"}]},{"prim":"PUSH","args":[{"prim":"string"},{"string":"1"}]},{"prim":"FAILWITH"}],[{"prim":"DUP","args":[{"int":"6"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"UPDATE","args":[{"int":"3"}]},{"prim":"EMPTY_BIG_MAP","args":[{"prim":"address"},{"prim":"nat"}]},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"SOME"},{"prim":"SENDER"},{"prim":"UPDATE"},{"prim":"UPDATE","args":[{"int":"7"}]},{"prim":"DIG","args":[{"int":"5"}]},{"prim":"GET","args":[{"int":"11"}]},{"prim":"EMPTY_MAP","args":[{"prim":"string"},{"prim":"bytes"}]},{"prim":"PUSH","args":[{"prim":"bytes"},{"bytes":"30"}]},{"prim":"SOME"},{"prim":"PUSH","args":[{"prim":"string"},{"string":"decimals"}]},{"prim":"UPDATE"},{"prim":"DIG","args":[{"int":"5"}]},{"prim":"SOME"},{"prim":"PUSH","args":[{"prim":"string"},{"string":"symbol"}]},{"prim":"UPDATE"},{"prim":"DUP","args":[{"int":"6"}]},{"prim":"SOME"},{"prim":"PUSH","args":[{"prim":"string"},{"string":"thumbnailUri"}]},{"prim":"UPDATE"},{"prim":"DUP","args":[{"int":"6"}]},{"prim":"SOME"},{"prim":"PUSH","args":[{"prim":"string"},{"string":"displayUri"}]},{"prim":"UPDATE"},{"prim":"DIG","args":[{"int":"5"}]},{"prim":"SOME"},{"prim":"PUSH","args":[{"prim":"string"},{"string":"artifactUri"}]},{"prim":"UPDATE"},{"prim":"PUSH","args":[{"prim":"bytes"},{"bytes":"5b22545a49502d3132225d"}]},{"prim":"SOME"},{"prim":"PUSH","args":[{"prim":"string"},{"string":"interfaces"}]},{"prim":"UPDATE"},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"SOME"},{"prim":"PUSH","args":[{"prim":"string"},{"string":"description"}]},{"prim":"UPDATE"},{"prim":"DIG","args":[{"int":"3"}]},{"prim":"SOME"},{"prim":"PUSH","args":[{"prim":"string"},{"string":"name"}]},{"prim":"UPDATE"},{"prim":"PUSH","args":[{"prim":"nat"},{"int":"0"}]},{"prim":"PAIR"},{"prim":"SOME"},{"prim":"PUSH","args":[{"prim":"nat"},{"int":"0"}]},{"prim":"UPDATE"},{"prim":"UPDATE","args":[{"int":"11"}]},{"prim":"EMPTY_BIG_MAP","args":[{"prim":"address"},{"prim":"set","args":[{"prim":"address"}]}]},{"prim":"UPDATE","args":[{"int":"12"}]},{"prim":"NIL","args":[{"prim":"operation"}]},{"prim":"PAIR"}]]}]]}],[{"prim":"IF_LEFT","args":[[{"prim":"DIG","args":[{"int":"2"}]},{"prim":"DIG","args":[{"int":"3"}]},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"DROP","args":[{"int":"3"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"GET","args":[{"int":"9"}]},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"GET","args":[{"int":"11"}]},{"prim":"DUP","args":[{"int":"4"}]},{"prim":"GET","args":[{"int":"12"}]},{"prim":"DUP","args":[{"int":"5"}]},{"prim":"GET","args":[{"int":"7"}]},{"prim":"PAIR","args":[{"int":"4"}]},{"prim":"DUP"},{"prim":"GET","args":[{"int":"3"}]},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"ITER","args":[[{"prim":"IF_LEFT","args":[[{"prim":"DUP"},{"prim":"GET","args":[{"int":"3"}]},{"prim":"SWAP"},{"prim":"CAR"},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"PAIR","args":[{"int":"3"}]},{"prim":"DUP","args":[{"int":"4"}]},{"prim":"SWAP"},{"prim":"EXEC"}],[{"prim":"DUP"},{"prim":"GET","args":[{"int":"3"}]},{"prim":"SWAP"},{"prim":"CAR"},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"COMPARE"},{"prim":"EQ"},{"prim":"IF","args":[[{"prim":"DROP","args":[{"int":"2"}]}],[{"prim":"DUP"},{"prim":"DUP","args":[{"int":"8"}]},{"prim":"SWAP"},{"prim":"EXEC"},{"prim":"DROP"},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"DIG","args":[{"int":"3"}]},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"GET"},{"prim":"IF_NONE","args":[[{"prim":"DIG","args":[{"int":"2"}]},{"prim":"DROP"},{"prim":"NONE","args":[{"prim":"set","args":[{"prim":"address"}]}]}],[{"prim":"PUSH","args":[{"prim":"nat"},{"int":"0"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"DUP","args":[{"int":"6"}]},{"prim":"PUSH","args":[{"prim":"bool"},{"prim":"False"}]},{"prim":"SWAP"},{"prim":"UPDATE"},{"prim":"SIZE"},{"prim":"COMPARE"},{"prim":"EQ"},{"prim":"IF","args":[[{"prim":"DIG","args":[{"int":"3"}]},{"prim":"DROP","args":[{"int":"2"}]},{"prim":"NONE","args":[{"prim":"set","args":[{"prim":"address"}]}]}],[{"prim":"DIG","args":[{"int":"3"}]},{"prim":"PUSH","args":[{"prim":"bool"},{"prim":"False"}]},{"prim":"SWAP"},{"prim":"UPDATE"},{"prim":"SOME"}]]}]]},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"UPDATE"}]]}]]}]]},{"prim":"DIG","args":[{"int":"3"}]},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"DROP","args":[{"int":"2"}]},{"prim":"UPDATE","args":[{"int":"3"}]},{"prim":"NIL","args":[{"prim":"operation"}]},{"prim":"PAIR"},{"prim":"SWAP"},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"CDR"},{"prim":"CAR"},{"prim":"UPDATE","args":[{"int":"7"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"CDR"},{"prim":"GET","args":[{"int":"6"}]},{"prim":"UPDATE","args":[{"int":"9"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"CDR"},{"prim":"GET","args":[{"int":"5"}]},{"prim":"UPDATE","args":[{"int":"11"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"CDR"},{"prim":"GET","args":[{"int":"3"}]},{"prim":"UPDATE","args":[{"int":"12"}]},{"prim":"SWAP"},{"prim":"CAR"}],[{"prim":"DIG","args":[{"int":"5"}]},{"prim":"DIG","args":[{"int":"6"}]},{"prim":"DROP","args":[{"int":"2"}]},{"prim":"IF_LEFT","args":[[{"prim":"DIG","args":[{"int":"3"}]},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"DROP","args":[{"int":"2"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"GET","args":[{"int":"9"}]},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"GET","args":[{"int":"11"}]},{"prim":"DUP","args":[{"int":"4"}]},{"prim":"GET","args":[{"int":"12"}]},{"prim":"DUP","args":[{"int":"5"}]},{"prim":"GET","args":[{"int":"7"}]},{"prim":"PAIR","args":[{"int":"4"}]},{"prim":"SWAP"},{"prim":"UNPAIR"},{"prim":"MAP","args":[[{"prim":"DUP"},{"prim":"CAR"},{"prim":"DUP","args":[{"int":"4"}]},{"prim":"PAIR"},{"prim":"DUP","args":[{"int":"6"}]},{"prim":"SWAP"},{"prim":"EXEC"},{"prim":"SWAP"},{"prim":"PAIR"}]]},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"DROP"},{"prim":"SWAP"},{"prim":"PUSH","args":[{"prim":"mutez"},{"int":"0"}]},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"TRANSFER_TOKENS"},{"prim":"SWAP"},{"prim":"NIL","args":[{"prim":"operation"}]},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"CONS"},{"prim":"PAIR"},{"prim":"SWAP"},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"CDR"},{"prim":"CAR"},{"prim":"UPDATE","args":[{"int":"7"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"CDR"},{"prim":"GET","args":[{"int":"6"}]},{"prim":"UPDATE","args":[{"int":"9"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"CDR"},{"prim":"GET","args":[{"int":"5"}]},{"prim":"UPDATE","args":[{"int":"11"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"CDR"},{"prim":"GET","args":[{"int":"3"}]},{"prim":"UPDATE","args":[{"int":"12"}]},{"prim":"SWAP"},{"prim":"CAR"}],[{"prim":"DIG","args":[{"int":"2"}]},{"prim":"DROP"},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"GET","args":[{"int":"9"}]},{"prim":"DUP","args":[{"int":"3"}]},{"prim":"GET","args":[{"int":"11"}]},{"prim":"DUP","args":[{"int":"4"}]},{"prim":"GET","args":[{"int":"12"}]},{"prim":"DUP","args":[{"int":"5"}]},{"prim":"GET","args":[{"int":"7"}]},{"prim":"PAIR","args":[{"int":"4"}]},{"prim":"DUP"},{"prim":"CAR"},{"prim":"DIG","args":[{"int":"2"}]},{"prim":"ITER","args":[[{"prim":"UNPAIR"},{"prim":"DUG","args":[{"int":"2"}]},{"prim":"ITER","args":[[{"prim":"UNPAIR","args":[{"int":"3"}]},{"prim":"SWAP"},{"prim":"DROP"},{"prim":"SENDER"},{"prim":"DUP","args":[{"int":"5"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"COMPARE"},{"prim":"EQ"},{"prim":"IF","args":[[{"prim":"DROP"}],[{"prim":"DUP","args":[{"int":"6"}]},{"prim":"GET","args":[{"int":"3"}]},{"prim":"DUP","args":[{"int":"6"}]},{"prim":"GET"},{"prim":"IF_NONE","args":[[{"prim":"EMPTY_SET","args":[{"prim":"address"}]}],[]]},{"prim":"SWAP"},{"prim":"MEM"},{"prim":"IF","args":[[],[{"prim":"PUSH","args":[{"prim":"string"},{"string":"FA2_NOT_OPERATOR"}]},{"prim":"FAILWITH"}]]}]]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"DUP","args":[{"int":"5"}]},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"PAIR","args":[{"int":"3"}]},{"prim":"DUP","args":[{"int":"8"}]},{"prim":"SWAP"},{"prim":"EXEC"},{"prim":"PAIR","args":[{"int":"3"}]},{"prim":"DUP","args":[{"int":"5"}]},{"prim":"SWAP"},{"prim":"EXEC"}]]},{"prim":"SWAP"},{"prim":"DROP"}]]},{"prim":"DIG","args":[{"int":"3"}]},{"prim":"DIG","args":[{"int":"4"}]},{"prim":"DROP","args":[{"int":"2"}]},{"prim":"UPDATE","args":[{"int":"1"}]},{"prim":"NIL","args":[{"prim":"operation"}]},{"prim":"PAIR"},{"prim":"SWAP"},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"CDR"},{"prim":"CAR"},{"prim":"UPDATE","args":[{"int":"7"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"CDR"},{"prim":"GET","args":[{"int":"6"}]},{"prim":"UPDATE","args":[{"int":"9"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"CDR"},{"prim":"GET","args":[{"int":"5"}]},{"prim":"UPDATE","args":[{"int":"11"}]},{"prim":"DUP","args":[{"int":"2"}]},{"prim":"CDR"},{"prim":"GET","args":[{"int":"3"}]},{"prim":"UPDATE","args":[{"int":"12"}]},{"prim":"SWAP"},{"prim":"CAR"}]]}]]},{"prim":"PAIR"}]]}]]}]]}]]}]`) }; diff --git a/solution/artifacts/nft.default_storage.tz b/solution/artifacts/nft.default_storage.tz index 8b6329c..f54a9e7 100644 --- a/solution/artifacts/nft.default_storage.tz +++ b/solution/artifacts/nft.default_storage.tz @@ -4,7 +4,7 @@ {} { Elt "" 0x74657a6f732d73746f726167653a64617461 ; Elt "data" - 0x7b0a202020202020226e616d65223a22464132204e4654204d61726b6574706c616365222c0a202020202020226465736372697074696f6e223a224578616d706c65206f662046413220696d706c656d656e746174696f6e222c0a2020202020202276657273696f6e223a22302e302e31222c0a202020202020226c6963656e7365223a7b226e616d65223a224d4954227d2c0a20202020202022617574686f7273223a5b224d617269676f6c643c636f6e74616374406d617269676f6c642e6465763e225d2c0a20202020202022686f6d6570616765223a2268747470733a2f2f6d617269676f6c642e646576222c0a20202020202022736f75726365223a7b0a202020202020202022746f6f6c73223a5b224c69676f225d2c0a2020202020202020226c6f636174696f6e223a2268747470733a2f2f6769746875622e636f6d2f6c69676f6c616e672f636f6e74726163742d636174616c6f6775652f747265652f6d61696e2f6c69622f666132227d2c0a20202020202022696e7465726661636573223a5b22545a49502d303132225d2c0a202020202020226572726f7273223a205b5d2c0a202020202020227669657773223a205b5d0a2020202020207d } + 0x7b0a202020202020202020226e616d65223a22464132204e4654204d61726b6574706c616365222c0a202020202020202020226465736372697074696f6e223a224578616d706c65206f662046413220696d706c656d656e746174696f6e222c0a2020202020202020202276657273696f6e223a22302e302e31222c0a202020202020202020226c6963656e7365223a7b226e616d65223a224d4954227d2c0a20202020202020202022617574686f7273223a5b224d617269676f6c643c636f6e74616374406d617269676f6c642e6465763e225d2c0a20202020202020202022686f6d6570616765223a2268747470733a2f2f6d617269676f6c642e646576222c0a20202020202020202022736f75726365223a7b0a202020202020202020202022746f6f6c73223a5b224c69676f225d2c0a2020202020202020202020226c6f636174696f6e223a2268747470733a2f2f6769746875622e636f6d2f6c69676f6c616e672f636f6e74726163742d636174616c6f6775652f747265652f6d61696e2f6c69622f666132227d2c0a20202020202020202022696e7465726661636573223a5b22545a49502d303132225d2c0a202020202020202020226572726f7273223a205b5d2c0a202020202020202020227669657773223a205b5d0a2020202020202020207d } {} {}) diff --git a/solution/contracts/nft.jsligo b/solution/contracts/nft.jsligo index 9ac764e..c5d97d0 100644 --- a/solution/contracts/nft.jsligo +++ b/solution/contracts/nft.jsligo @@ -1,16 +1,16 @@ #import "@ligo/fa/lib/fa2/asset/single_asset.impl.jsligo" "FA2Impl" /* ERROR MAP FOR UI DISPLAY or TESTS - const errorMap : map = Map.literal(list([ - ["0", "Enter a positive and not null amount"], - ["1", "Operation not allowed, you need to be administrator"], - ["2", "You cannot sell more than your current balance"], - ["3", "Cannot find the offer you entered for buying"], - ["4", "You entered a quantity to buy than is more than the offer quantity"], - ["5", "Not enough funds, you need to pay at least quantity * offer price to get the tokens"], - ["6", "Cannot find the contract relative to implicit address"], - ])); -*/ + const errorMap : map = Map.literal(list([ + ["0", "Enter a positive and not null amount"], + ["1", "Operation not allowed, you need to be administrator"], + ["2", "You cannot sell more than your current balance"], + ["3", "Cannot find the offer you entered for buying"], + ["4", "You entered a quantity to buy than is more than the offer quantity"], + ["5", "Not enough funds, you need to pay at least quantity * offer price to get the tokens"], + ["6", "Cannot find the contract relative to implicit address"], + ])); + */ export type offer = { quantity: nat, price: nat }; diff --git a/solution/contracts/nft.parameterList.jsligo b/solution/contracts/nft.parameterList.jsligo index 4d2bd5d..418053e 100644 --- a/solution/contracts/nft.parameterList.jsligo +++ b/solution/contracts/nft.parameterList.jsligo @@ -1,10 +1,6 @@ #import "nft.jsligo" "Contract" -// Define your parameter values as a list of LIGO variable definitions // When this file was created, the smart contract was defined with an entrypoint using `@entry` that was not within a namespace. As such, the examples below are written with that assumption in mind. -// If your parameter is a simple value, you can define it directly -// E.g. const default_parameter = 10 - -// For added type-safety, you can reference the type of your parameter from the contract -// E.g. const default_parameter : parameter_of Contract = 10 \ No newline at end of file +// IMPORTANT: We suggest always explicitly typing your parameter values: +// E.g.: `const parameter: int = 10` or `const parameter: Contract.parameter = 10` \ No newline at end of file diff --git a/solution/contracts/nft.storageList.jsligo b/solution/contracts/nft.storageList.jsligo index f798869..05fc540 100644 --- a/solution/contracts/nft.storageList.jsligo +++ b/solution/contracts/nft.storageList.jsligo @@ -15,19 +15,19 @@ const default_storage: Contract.storage = { "data", bytes `{ - "name":"FA2 NFT Marketplace", - "description":"Example of FA2 implementation", - "version":"0.0.1", - "license":{"name":"MIT"}, - "authors":["Marigold"], - "homepage":"https://marigold.dev", - "source":{ - "tools":["Ligo"], - "location":"https://github.com/ligolang/contract-catalogue/tree/main/lib/fa2"}, - "interfaces":["TZIP-012"], - "errors": [], - "views": [] - }` + "name":"FA2 NFT Marketplace", + "description":"Example of FA2 implementation", + "version":"0.0.1", + "license":{"name":"MIT"}, + "authors":["Marigold"], + "homepage":"https://marigold.dev", + "source":{ + "tools":["Ligo"], + "location":"https://github.com/ligolang/contract-catalogue/tree/main/lib/fa2"}, + "interfaces":["TZIP-012"], + "errors": [], + "views": [] + }` ] ] ) diff --git a/solution/package-lock.json b/solution/package-lock.json index 72e0622..73bad93 100644 --- a/solution/package-lock.json +++ b/solution/package-lock.json @@ -1,23 +1,24 @@ { - "name": "training", + "name": "nft-marketplace", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "training", + "name": "nft-marketplace", "version": "1.0.0", "license": "ISC", "devDependencies": { - "@taqueria/plugin-contract-types": "^0.40.0", - "@taqueria/plugin-ligo": "^0.40.0", - "@taqueria/plugin-taquito": "^0.40.0" + "@taqueria/plugin-contract-types": "^0.44.0", + "@taqueria/plugin-ligo": "^0.44.0", + "@taqueria/plugin-octez-client": "^0.42.0", + "@taqueria/plugin-taquito": "^0.42.0" } }, "node_modules/@babel/runtime": { - "version": "7.23.1", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.1.tgz", - "integrity": "sha512-hC2v6p8ZSI/W0HUzh3V8C5g+NwSKzKPtJwSpTjwl0o297GP9+ZLQSkdvHz46CM3LqyoXxq+5G9komY+eSqSO0g==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", + "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", "dev": true, "dependencies": { "regenerator-runtime": "^0.14.0" @@ -62,14 +63,14 @@ } }, "node_modules/@peculiar/asn1-schema": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.3.6.tgz", - "integrity": "sha512-izNRxPoaeJeg/AyH8hER6s+H7p4itk+03QCa4sbxI3lNdseQYCuxzgsuNK8bTXChtLTjpJz6NmXKA73qLa3rCA==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.3.8.tgz", + "integrity": "sha512-ULB1XqHKx1WBU/tTFIA+uARuRoBVZ4pNdOA878RDrRbBfBGcSzi5HBkdScC6ZbHn8z7L8gmKCgPC1LHRrP46tA==", "dev": true, "dependencies": { "asn1js": "^3.0.5", - "pvtsutils": "^1.3.2", - "tslib": "^2.4.0" + "pvtsutils": "^1.3.5", + "tslib": "^2.6.2" } }, "node_modules/@peculiar/json-schema": { @@ -270,19 +271,30 @@ "@stablelib/wipe": "^1.0.1" } }, + "node_modules/@taqueria/lib-ligo": { + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/@taqueria/lib-ligo/-/lib-ligo-0.44.0.tgz", + "integrity": "sha512-6inxp+RPdhYj8hEoFF8GCk2rbnGra/zLn/28WYhoNhOYz6AiuzoXWnE2fXxHrQXosMYKmqRUfnP9n79iranLzw==", + "dev": true, + "dependencies": { + "@taqueria/node-sdk": "^0.44.0", + "fast-glob": "^3.3.1" + } + }, "node_modules/@taqueria/node-sdk": { - "version": "0.40.0", - "resolved": "https://registry.npmjs.org/@taqueria/node-sdk/-/node-sdk-0.40.0.tgz", - "integrity": "sha512-oikiFN774OZTwpppxkdde6Kaj3T05zvSZZG2Cya0yjN+G06W7llnIZ+H8QD6gFEFfWnEKEADQDzkoXwz2z4IWg==", + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/@taqueria/node-sdk/-/node-sdk-0.44.0.tgz", + "integrity": "sha512-nCkbFzO9g/+CK/xhAQBJjdADFIYAcMR4KXcQ1aJolkCTgVYjSK0e34kBk/U+MU9HGSF9j5ADG+SHSTnA16GKtA==", "dev": true, "dependencies": { - "@taqueria/protocol": "^0.40.0", + "@taqueria/protocol": "^0.44.0", "@taquito/signer": "^17.3.1", "@taquito/taquito": "^17.3.1", "@taquito/utils": "^17.3.1", "i18next": "^23.5.1", "node-fetch": "^3.3.2", "rambda": "^8.3.0", + "shell-quote": "^1.8.1", "stacktrace-js": "^2.0.2", "ts-pattern": "^5.0.5", "why-is-node-running": "^2.2.2", @@ -291,39 +303,235 @@ } }, "node_modules/@taqueria/plugin-contract-types": { - "version": "0.40.0", - "resolved": "https://registry.npmjs.org/@taqueria/plugin-contract-types/-/plugin-contract-types-0.40.0.tgz", - "integrity": "sha512-JOj8SF3/1SZzksV+mAdRWWrhVhqwh9ayd3PStFQiDuEE5g9iGx2n8mB8t/eBiOIjOp944l5K80C+Q385QpIlrw==", + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/@taqueria/plugin-contract-types/-/plugin-contract-types-0.44.0.tgz", + "integrity": "sha512-tge5+0kpxq7g35dc8pZD6DYhFv68TMVLxKY8ct3EvVzZiRA6tGl8eeAzwXx0OrAHQ7QAQwLilwkA7Fb+szOBaQ==", "dev": true, "dependencies": { - "@taqueria/node-sdk": "^0.40.0", - "@taquito/michel-codec": "^17.3.1", - "@taquito/signer": "^17.3.1", - "@taquito/taquito": "^17.3.1", + "@taqueria/node-sdk": "^0.44.0", + "@taquito/michel-codec": "^18.0.0-RC.0", + "@taquito/signer": "^18.0.0-RC.0", + "@taquito/taquito": "^18.0.0-RC.0", "bignumber.js": "^9.1.2", "fast-glob": "^3.3.1" } }, + "node_modules/@taqueria/plugin-contract-types/node_modules/@taquito/core": { + "version": "18.0.0-RC.0", + "resolved": "https://registry.npmjs.org/@taquito/core/-/core-18.0.0-RC.0.tgz", + "integrity": "sha512-10TMPmBJ60ibsJCipnsDSgd9e+qk2sCmOYO9prabHXtpfw/OAHjBX5R4rAkUVAhm7YBODePesSdvppv7TaEpeA==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/@taqueria/plugin-contract-types/node_modules/@taquito/http-utils": { + "version": "18.0.0-RC.0", + "resolved": "https://registry.npmjs.org/@taquito/http-utils/-/http-utils-18.0.0-RC.0.tgz", + "integrity": "sha512-KhSlw2FWdD1u4Kz8DKLtwWAu0ihbKE0KoO48dvYkfBIGCAXsHz/g/JAudGhMCGbLlP4ddF49UKO567DJkXyBeQ==", + "dev": true, + "dependencies": { + "@taquito/core": "^18.0.0-RC.0", + "axios": "0.26.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@taqueria/plugin-contract-types/node_modules/@taquito/local-forging": { + "version": "18.0.0-RC.0", + "resolved": "https://registry.npmjs.org/@taquito/local-forging/-/local-forging-18.0.0-RC.0.tgz", + "integrity": "sha512-trCCK5fBLSb3g7j3HEJ+93LQ3FlU9QUfQU3DAL4DOC9tyFxzamXECrVrtRxvdVVPo1HYBUvFJb5WBZer17fuJQ==", + "dev": true, + "dependencies": { + "@taquito/core": "^18.0.0-RC.0", + "@taquito/utils": "^18.0.0-RC.0", + "bignumber.js": "^9.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@taqueria/plugin-contract-types/node_modules/@taquito/michel-codec": { + "version": "18.0.0-RC.0", + "resolved": "https://registry.npmjs.org/@taquito/michel-codec/-/michel-codec-18.0.0-RC.0.tgz", + "integrity": "sha512-5FzMU+C8ERpQ0e5WrRsB+o5OggO/1TyL2MVmfQmqel4jn1OEbBUEQ31QqxhZBp0fqCX9n5FYOjznXafutFzK2Q==", + "dev": true, + "dependencies": { + "@taquito/core": "^18.0.0-RC.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@taqueria/plugin-contract-types/node_modules/@taquito/michelson-encoder": { + "version": "18.0.0-RC.0", + "resolved": "https://registry.npmjs.org/@taquito/michelson-encoder/-/michelson-encoder-18.0.0-RC.0.tgz", + "integrity": "sha512-PeXU18uCMWwayfAFe31zhclsZrdW4+f6+PdRi5hsSYWlJ7pEPo2xambKC5gCL8jVTjqcj64BzR5crCH3xsN49Q==", + "dev": true, + "dependencies": { + "@taquito/core": "^18.0.0-RC.0", + "@taquito/rpc": "^18.0.0-RC.0", + "@taquito/utils": "^18.0.0-RC.0", + "bignumber.js": "^9.1.0", + "fast-json-stable-stringify": "^2.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@taqueria/plugin-contract-types/node_modules/@taquito/rpc": { + "version": "18.0.0-RC.0", + "resolved": "https://registry.npmjs.org/@taquito/rpc/-/rpc-18.0.0-RC.0.tgz", + "integrity": "sha512-JNvWlYHH5/kKaHoYrBmYqRuPafvars97ena1x9y4cksrapg4ZgvkAWudaVfJ9L8ahfY+IoPdmNP3G2FQ3WkeMg==", + "dev": true, + "dependencies": { + "@taquito/core": "^18.0.0-RC.0", + "@taquito/http-utils": "^18.0.0-RC.0", + "@taquito/utils": "^18.0.0-RC.0", + "bignumber.js": "^9.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@taqueria/plugin-contract-types/node_modules/@taquito/signer": { + "version": "18.0.0-RC.0", + "resolved": "https://registry.npmjs.org/@taquito/signer/-/signer-18.0.0-RC.0.tgz", + "integrity": "sha512-nUSNlyIne5PWcA2V/m06txudMGcGmlL1IfKV29kQ5UaS4iXFXB5Qgv7DRKp2GGoaY61bJKedbbd01+QEJ0bDQQ==", + "dev": true, + "dependencies": { + "@stablelib/blake2b": "^1.0.1", + "@stablelib/ed25519": "^1.0.3", + "@stablelib/hmac": "^1.0.1", + "@stablelib/nacl": "^1.0.4", + "@stablelib/pbkdf2": "^1.0.1", + "@stablelib/sha512": "^1.0.1", + "@taquito/core": "^18.0.0-RC.0", + "@taquito/taquito": "^18.0.0-RC.0", + "@taquito/utils": "^18.0.0-RC.0", + "@types/bn.js": "^5.1.1", + "bip39": "3.0.4", + "elliptic": "^6.5.4", + "pbkdf2": "^3.1.2", + "typedarray-to-buffer": "^4.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@taqueria/plugin-contract-types/node_modules/@taquito/taquito": { + "version": "18.0.0-RC.0", + "resolved": "https://registry.npmjs.org/@taquito/taquito/-/taquito-18.0.0-RC.0.tgz", + "integrity": "sha512-+TJSAqBmEElmLMwlNpA5UzhBtOyGYRdjqhA+A5+KvUFGXte8r/jEOlCtBHSgcZ3p+ckUVcrareaD2vUHvTnzyw==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@taquito/core": "^18.0.0-RC.0", + "@taquito/http-utils": "^18.0.0-RC.0", + "@taquito/local-forging": "^18.0.0-RC.0", + "@taquito/michel-codec": "^18.0.0-RC.0", + "@taquito/michelson-encoder": "^18.0.0-RC.0", + "@taquito/rpc": "^18.0.0-RC.0", + "@taquito/utils": "^18.0.0-RC.0", + "bignumber.js": "^9.1.0", + "rxjs": "^7.8.1" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@taqueria/plugin-contract-types/node_modules/@taquito/utils": { + "version": "18.0.0-RC.0", + "resolved": "https://registry.npmjs.org/@taquito/utils/-/utils-18.0.0-RC.0.tgz", + "integrity": "sha512-rn/wQ6BHpfMskOp06C4Re2mqMkM1d4PtQlJT602nWNAnpEoEnIWXrJ4Dxsy3xQZxL+Jx1YTtGag9iibYcu89Zw==", + "dev": true, + "dependencies": { + "@stablelib/blake2b": "^1.0.1", + "@stablelib/ed25519": "^1.0.3", + "@taquito/core": "^18.0.0-RC.0", + "@types/bs58check": "^2.1.0", + "bignumber.js": "^9.1.0", + "blakejs": "^1.2.1", + "bs58check": "^2.1.2", + "buffer": "^6.0.3", + "elliptic": "^6.5.4", + "typedarray-to-buffer": "^4.0.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@taqueria/plugin-ligo": { - "version": "0.40.0", - "resolved": "https://registry.npmjs.org/@taqueria/plugin-ligo/-/plugin-ligo-0.40.0.tgz", - "integrity": "sha512-nFR+88vl1aG+hPlqjfwURccHJYQ7BcTs12w4mV8mSxYxSY7ISvo6H98Y/4Rf48PaYdnUlFXLuB692OHpg1AdQQ==", + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/@taqueria/plugin-ligo/-/plugin-ligo-0.44.0.tgz", + "integrity": "sha512-s9Eirp7s/2zogFIrmk9qhUaengyG61v5hsGZ/JsT+dVEHZV3sWE4OQhTFBBWrzAmoDqlcHQXfP1u8oxKfU+Vcw==", "dev": true, "dependencies": { - "@taqueria/node-sdk": "^0.40.0", + "@taqueria/lib-ligo": "^0.44.0", + "@taqueria/node-sdk": "^0.44.0", "fast-glob": "^3.3.1" } }, - "node_modules/@taqueria/plugin-taquito": { - "version": "0.40.0", - "resolved": "https://registry.npmjs.org/@taqueria/plugin-taquito/-/plugin-taquito-0.40.0.tgz", - "integrity": "sha512-ecqctFHjcQlZGQJxz+Fx3BtgpzVqtf/lsFo6tQ6EAKNE9pPRFGKELYMRjhGpO/mbhFPHbt9kcKtD36yBV0fRlQ==", + "node_modules/@taqueria/plugin-octez-client": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@taqueria/plugin-octez-client/-/plugin-octez-client-0.42.0.tgz", + "integrity": "sha512-FB+UPJtalo/tE3TVQjGWw5LfPpXCeR7wjmLfqQ4HAKKlB5+iMCTU/3DLO3wlh3TGWvuAQopynUZEVrNKiHOoEA==", "dev": true, "dependencies": { - "@taqueria/node-sdk": "^0.40.0", - "@taquito/michel-codec": "^17.3.1", + "@taqueria/node-sdk": "^0.42.0", + "fast-glob": "^3.3.1" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@taqueria/plugin-octez-client/node_modules/@taqueria/node-sdk": { + "version": "0.42.13", + "resolved": "https://registry.npmjs.org/@taqueria/node-sdk/-/node-sdk-0.42.13.tgz", + "integrity": "sha512-/T6hUzTO/MnIoWOYCscNIUn2CjLe7jgiGR4brynEc2U+wUV4UyrVPNsSfuoe21SZVEzBMbhjwd8Vl57gfDtYlA==", + "dev": true, + "dependencies": { + "@taqueria/protocol": "^0.42.13", "@taquito/signer": "^17.3.1", "@taquito/taquito": "^17.3.1", + "@taquito/utils": "^17.3.1", + "i18next": "^23.5.1", + "node-fetch": "^3.3.2", + "rambda": "^8.3.0", + "shell-quote": "^1.8.1", + "stacktrace-js": "^2.0.2", + "ts-pattern": "^5.0.5", + "why-is-node-running": "^2.2.2", + "yargs": "^17.7.2", + "zod": "^3.22.2" + } + }, + "node_modules/@taqueria/plugin-octez-client/node_modules/@taqueria/protocol": { + "version": "0.42.13", + "resolved": "https://registry.npmjs.org/@taqueria/protocol/-/protocol-0.42.13.tgz", + "integrity": "sha512-Q9mV3KK4ZQrDI4CJ5UkLo3X3KmRGlLZrw1xV3josbxvjOYFZ/1Z0BG905Hll/ztItcaGyy4vKSKMf01jai8G/A==", + "dev": true, + "dependencies": { + "@peculiar/webcrypto": "^1.4.3", + "batching-toposort": "^1.2.0", + "fluture": "^14.0.0", + "i18next": "^23.5.1", + "rambda": "^8.3.0", + "rambdax": "^10.0.0", + "zod": "^3.22.2" + } + }, + "node_modules/@taqueria/plugin-taquito": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@taqueria/plugin-taquito/-/plugin-taquito-0.42.0.tgz", + "integrity": "sha512-ktV89iL4MmHwBi4iQw350hVkW/P5ceChHwQV/PUhaB5nJJ8PtyWva8FBw4scjBqvZCXQiAE9JXljR5XRwDYhzg==", + "dev": true, + "dependencies": { + "@taqueria/node-sdk": "^0.42.0", + "@taquito/michel-codec": "^18.0.0-RC.0", + "@taquito/signer": "^18.0.0-RC.0", + "@taquito/taquito": "^18.0.0-RC.0", "fast-glob": "^3.3.1", "wtfnode": "^0.9.1" }, @@ -331,10 +539,289 @@ "node": ">=16" } }, + "node_modules/@taqueria/plugin-taquito/node_modules/@taqueria/node-sdk": { + "version": "0.42.13", + "resolved": "https://registry.npmjs.org/@taqueria/node-sdk/-/node-sdk-0.42.13.tgz", + "integrity": "sha512-/T6hUzTO/MnIoWOYCscNIUn2CjLe7jgiGR4brynEc2U+wUV4UyrVPNsSfuoe21SZVEzBMbhjwd8Vl57gfDtYlA==", + "dev": true, + "dependencies": { + "@taqueria/protocol": "^0.42.13", + "@taquito/signer": "^17.3.1", + "@taquito/taquito": "^17.3.1", + "@taquito/utils": "^17.3.1", + "i18next": "^23.5.1", + "node-fetch": "^3.3.2", + "rambda": "^8.3.0", + "shell-quote": "^1.8.1", + "stacktrace-js": "^2.0.2", + "ts-pattern": "^5.0.5", + "why-is-node-running": "^2.2.2", + "yargs": "^17.7.2", + "zod": "^3.22.2" + } + }, + "node_modules/@taqueria/plugin-taquito/node_modules/@taqueria/node-sdk/node_modules/@taquito/michel-codec": { + "version": "17.3.2", + "resolved": "https://registry.npmjs.org/@taquito/michel-codec/-/michel-codec-17.3.2.tgz", + "integrity": "sha512-js/yWXINW5w1WIDgLnTtlXcabtF/N8mt3E+laaHvHSjzxc5BMQXiWcj+vCk0JyHc9Hm/RnVK6AW326R/Bpwj3Q==", + "dev": true, + "dependencies": { + "@taquito/core": "^17.3.2" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@taqueria/plugin-taquito/node_modules/@taqueria/node-sdk/node_modules/@taquito/signer": { + "version": "17.3.2", + "resolved": "https://registry.npmjs.org/@taquito/signer/-/signer-17.3.2.tgz", + "integrity": "sha512-5lFPomhVNgAmNqiTCzCJM6QbPT3XT6HSqcjD3WQx8kM1Xi5P3sLgAMPfucRDVGp2zopZL+MBbt8juIzb7KDy5A==", + "dev": true, + "dependencies": { + "@stablelib/blake2b": "^1.0.1", + "@stablelib/ed25519": "^1.0.3", + "@stablelib/hmac": "^1.0.1", + "@stablelib/nacl": "^1.0.4", + "@stablelib/pbkdf2": "^1.0.1", + "@stablelib/sha512": "^1.0.1", + "@taquito/core": "^17.3.2", + "@taquito/taquito": "^17.3.2", + "@taquito/utils": "^17.3.2", + "@types/bn.js": "^5.1.1", + "bip39": "3.0.4", + "elliptic": "^6.5.4", + "pbkdf2": "^3.1.2", + "typedarray-to-buffer": "^4.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@taqueria/plugin-taquito/node_modules/@taqueria/node-sdk/node_modules/@taquito/taquito": { + "version": "17.3.2", + "resolved": "https://registry.npmjs.org/@taquito/taquito/-/taquito-17.3.2.tgz", + "integrity": "sha512-q9+9i1LwqGl8MpWKzhJVWolnRY3tmYJL7cLfLA0z73X+/tEyZB+Me5VnzIk0rfssAIyXzdQq3KV0FUuprEolyg==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@taquito/core": "^17.3.2", + "@taquito/http-utils": "^17.3.2", + "@taquito/local-forging": "^17.3.2", + "@taquito/michel-codec": "^17.3.2", + "@taquito/michelson-encoder": "^17.3.2", + "@taquito/rpc": "^17.3.2", + "@taquito/utils": "^17.3.2", + "bignumber.js": "^9.1.0", + "rxjs": "^7.8.1" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@taqueria/plugin-taquito/node_modules/@taqueria/protocol": { + "version": "0.42.13", + "resolved": "https://registry.npmjs.org/@taqueria/protocol/-/protocol-0.42.13.tgz", + "integrity": "sha512-Q9mV3KK4ZQrDI4CJ5UkLo3X3KmRGlLZrw1xV3josbxvjOYFZ/1Z0BG905Hll/ztItcaGyy4vKSKMf01jai8G/A==", + "dev": true, + "dependencies": { + "@peculiar/webcrypto": "^1.4.3", + "batching-toposort": "^1.2.0", + "fluture": "^14.0.0", + "i18next": "^23.5.1", + "rambda": "^8.3.0", + "rambdax": "^10.0.0", + "zod": "^3.22.2" + } + }, + "node_modules/@taqueria/plugin-taquito/node_modules/@taquito/michel-codec": { + "version": "18.0.0-RC.0", + "resolved": "https://registry.npmjs.org/@taquito/michel-codec/-/michel-codec-18.0.0-RC.0.tgz", + "integrity": "sha512-5FzMU+C8ERpQ0e5WrRsB+o5OggO/1TyL2MVmfQmqel4jn1OEbBUEQ31QqxhZBp0fqCX9n5FYOjznXafutFzK2Q==", + "dev": true, + "dependencies": { + "@taquito/core": "^18.0.0-RC.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@taqueria/plugin-taquito/node_modules/@taquito/michel-codec/node_modules/@taquito/core": { + "version": "18.0.0-RC.0", + "resolved": "https://registry.npmjs.org/@taquito/core/-/core-18.0.0-RC.0.tgz", + "integrity": "sha512-10TMPmBJ60ibsJCipnsDSgd9e+qk2sCmOYO9prabHXtpfw/OAHjBX5R4rAkUVAhm7YBODePesSdvppv7TaEpeA==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/@taqueria/plugin-taquito/node_modules/@taquito/signer": { + "version": "18.0.0-RC.0", + "resolved": "https://registry.npmjs.org/@taquito/signer/-/signer-18.0.0-RC.0.tgz", + "integrity": "sha512-nUSNlyIne5PWcA2V/m06txudMGcGmlL1IfKV29kQ5UaS4iXFXB5Qgv7DRKp2GGoaY61bJKedbbd01+QEJ0bDQQ==", + "dev": true, + "dependencies": { + "@stablelib/blake2b": "^1.0.1", + "@stablelib/ed25519": "^1.0.3", + "@stablelib/hmac": "^1.0.1", + "@stablelib/nacl": "^1.0.4", + "@stablelib/pbkdf2": "^1.0.1", + "@stablelib/sha512": "^1.0.1", + "@taquito/core": "^18.0.0-RC.0", + "@taquito/taquito": "^18.0.0-RC.0", + "@taquito/utils": "^18.0.0-RC.0", + "@types/bn.js": "^5.1.1", + "bip39": "3.0.4", + "elliptic": "^6.5.4", + "pbkdf2": "^3.1.2", + "typedarray-to-buffer": "^4.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@taqueria/plugin-taquito/node_modules/@taquito/signer/node_modules/@taquito/core": { + "version": "18.0.0-RC.0", + "resolved": "https://registry.npmjs.org/@taquito/core/-/core-18.0.0-RC.0.tgz", + "integrity": "sha512-10TMPmBJ60ibsJCipnsDSgd9e+qk2sCmOYO9prabHXtpfw/OAHjBX5R4rAkUVAhm7YBODePesSdvppv7TaEpeA==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/@taqueria/plugin-taquito/node_modules/@taquito/signer/node_modules/@taquito/utils": { + "version": "18.0.0-RC.0", + "resolved": "https://registry.npmjs.org/@taquito/utils/-/utils-18.0.0-RC.0.tgz", + "integrity": "sha512-rn/wQ6BHpfMskOp06C4Re2mqMkM1d4PtQlJT602nWNAnpEoEnIWXrJ4Dxsy3xQZxL+Jx1YTtGag9iibYcu89Zw==", + "dev": true, + "dependencies": { + "@stablelib/blake2b": "^1.0.1", + "@stablelib/ed25519": "^1.0.3", + "@taquito/core": "^18.0.0-RC.0", + "@types/bs58check": "^2.1.0", + "bignumber.js": "^9.1.0", + "blakejs": "^1.2.1", + "bs58check": "^2.1.2", + "buffer": "^6.0.3", + "elliptic": "^6.5.4", + "typedarray-to-buffer": "^4.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@taqueria/plugin-taquito/node_modules/@taquito/taquito": { + "version": "18.0.0-RC.0", + "resolved": "https://registry.npmjs.org/@taquito/taquito/-/taquito-18.0.0-RC.0.tgz", + "integrity": "sha512-+TJSAqBmEElmLMwlNpA5UzhBtOyGYRdjqhA+A5+KvUFGXte8r/jEOlCtBHSgcZ3p+ckUVcrareaD2vUHvTnzyw==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@taquito/core": "^18.0.0-RC.0", + "@taquito/http-utils": "^18.0.0-RC.0", + "@taquito/local-forging": "^18.0.0-RC.0", + "@taquito/michel-codec": "^18.0.0-RC.0", + "@taquito/michelson-encoder": "^18.0.0-RC.0", + "@taquito/rpc": "^18.0.0-RC.0", + "@taquito/utils": "^18.0.0-RC.0", + "bignumber.js": "^9.1.0", + "rxjs": "^7.8.1" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@taqueria/plugin-taquito/node_modules/@taquito/taquito/node_modules/@taquito/core": { + "version": "18.0.0-RC.0", + "resolved": "https://registry.npmjs.org/@taquito/core/-/core-18.0.0-RC.0.tgz", + "integrity": "sha512-10TMPmBJ60ibsJCipnsDSgd9e+qk2sCmOYO9prabHXtpfw/OAHjBX5R4rAkUVAhm7YBODePesSdvppv7TaEpeA==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/@taqueria/plugin-taquito/node_modules/@taquito/taquito/node_modules/@taquito/http-utils": { + "version": "18.0.0-RC.0", + "resolved": "https://registry.npmjs.org/@taquito/http-utils/-/http-utils-18.0.0-RC.0.tgz", + "integrity": "sha512-KhSlw2FWdD1u4Kz8DKLtwWAu0ihbKE0KoO48dvYkfBIGCAXsHz/g/JAudGhMCGbLlP4ddF49UKO567DJkXyBeQ==", + "dev": true, + "dependencies": { + "@taquito/core": "^18.0.0-RC.0", + "axios": "0.26.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@taqueria/plugin-taquito/node_modules/@taquito/taquito/node_modules/@taquito/local-forging": { + "version": "18.0.0-RC.0", + "resolved": "https://registry.npmjs.org/@taquito/local-forging/-/local-forging-18.0.0-RC.0.tgz", + "integrity": "sha512-trCCK5fBLSb3g7j3HEJ+93LQ3FlU9QUfQU3DAL4DOC9tyFxzamXECrVrtRxvdVVPo1HYBUvFJb5WBZer17fuJQ==", + "dev": true, + "dependencies": { + "@taquito/core": "^18.0.0-RC.0", + "@taquito/utils": "^18.0.0-RC.0", + "bignumber.js": "^9.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@taqueria/plugin-taquito/node_modules/@taquito/taquito/node_modules/@taquito/michelson-encoder": { + "version": "18.0.0-RC.0", + "resolved": "https://registry.npmjs.org/@taquito/michelson-encoder/-/michelson-encoder-18.0.0-RC.0.tgz", + "integrity": "sha512-PeXU18uCMWwayfAFe31zhclsZrdW4+f6+PdRi5hsSYWlJ7pEPo2xambKC5gCL8jVTjqcj64BzR5crCH3xsN49Q==", + "dev": true, + "dependencies": { + "@taquito/core": "^18.0.0-RC.0", + "@taquito/rpc": "^18.0.0-RC.0", + "@taquito/utils": "^18.0.0-RC.0", + "bignumber.js": "^9.1.0", + "fast-json-stable-stringify": "^2.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@taqueria/plugin-taquito/node_modules/@taquito/taquito/node_modules/@taquito/rpc": { + "version": "18.0.0-RC.0", + "resolved": "https://registry.npmjs.org/@taquito/rpc/-/rpc-18.0.0-RC.0.tgz", + "integrity": "sha512-JNvWlYHH5/kKaHoYrBmYqRuPafvars97ena1x9y4cksrapg4ZgvkAWudaVfJ9L8ahfY+IoPdmNP3G2FQ3WkeMg==", + "dev": true, + "dependencies": { + "@taquito/core": "^18.0.0-RC.0", + "@taquito/http-utils": "^18.0.0-RC.0", + "@taquito/utils": "^18.0.0-RC.0", + "bignumber.js": "^9.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@taqueria/plugin-taquito/node_modules/@taquito/taquito/node_modules/@taquito/utils": { + "version": "18.0.0-RC.0", + "resolved": "https://registry.npmjs.org/@taquito/utils/-/utils-18.0.0-RC.0.tgz", + "integrity": "sha512-rn/wQ6BHpfMskOp06C4Re2mqMkM1d4PtQlJT602nWNAnpEoEnIWXrJ4Dxsy3xQZxL+Jx1YTtGag9iibYcu89Zw==", + "dev": true, + "dependencies": { + "@stablelib/blake2b": "^1.0.1", + "@stablelib/ed25519": "^1.0.3", + "@taquito/core": "^18.0.0-RC.0", + "@types/bs58check": "^2.1.0", + "bignumber.js": "^9.1.0", + "blakejs": "^1.2.1", + "bs58check": "^2.1.2", + "buffer": "^6.0.3", + "elliptic": "^6.5.4", + "typedarray-to-buffer": "^4.0.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@taqueria/protocol": { - "version": "0.40.0", - "resolved": "https://registry.npmjs.org/@taqueria/protocol/-/protocol-0.40.0.tgz", - "integrity": "sha512-IDCWf1WkYRTrDN08pg0w0AhRsgDS7yu7szH8H5mZAMESxOnOUPwAnjdpVD1Olgv8xqKgeGOMM5wjlsLglyOjWg==", + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/@taqueria/protocol/-/protocol-0.44.0.tgz", + "integrity": "sha512-0vZf1n4y0sF60uhba0EXCrlZoUFxj/Ktv0pGTuAtmMPh3iBKLPkvos5bicfbm5e2zMFDnBXsU9kPh3+6I3ru/w==", "dev": true, "dependencies": { "@peculiar/webcrypto": "^1.4.3", @@ -347,21 +834,21 @@ } }, "node_modules/@taquito/core": { - "version": "17.3.1", - "resolved": "https://registry.npmjs.org/@taquito/core/-/core-17.3.1.tgz", - "integrity": "sha512-dk8ZXuZktfoIV5ESUx3mPkJxLBVL7o3CXLsaVoiPIu2nyd4I/VNkPh+aIOrhxHSVPBib3sVYN0Pc2a8ZO+0gsg==", + "version": "17.3.2", + "resolved": "https://registry.npmjs.org/@taquito/core/-/core-17.3.2.tgz", + "integrity": "sha512-prdrB+Cjty+XY2uXbgJXTS5+4+XLLurkchwHFojGLT4y56sHOkSDBSsELwiXvQPf2R6nH24q1v79uSBj+OdvsA==", "dev": true, "engines": { "node": ">=16" } }, "node_modules/@taquito/http-utils": { - "version": "17.3.1", - "resolved": "https://registry.npmjs.org/@taquito/http-utils/-/http-utils-17.3.1.tgz", - "integrity": "sha512-lHwkNM1sx1HmzJHuxQy3lxaksZH/Jqxq5cYEPJAeCzEj68TD4T4X2rcVPpa0b6Qka5mQgLITCbyFE+RUg+orgA==", + "version": "17.3.2", + "resolved": "https://registry.npmjs.org/@taquito/http-utils/-/http-utils-17.3.2.tgz", + "integrity": "sha512-jAc1ZRE0zpM7XpIMM90sDWWLq/T3MpB/urxtu9aVnyEM+1n6ERUdK5W3ZA2moMKiHBgLBVHF3EFRdgyoWVEwDg==", "dev": true, "dependencies": { - "@taquito/core": "^17.3.1", + "@taquito/core": "^17.3.2", "axios": "0.26.0" }, "engines": { @@ -369,13 +856,13 @@ } }, "node_modules/@taquito/local-forging": { - "version": "17.3.1", - "resolved": "https://registry.npmjs.org/@taquito/local-forging/-/local-forging-17.3.1.tgz", - "integrity": "sha512-1pBbN8g2Jhq7bxWW3diRC+llL5TA1/m9W2za75IAl2sv5oo8ttXVla4XY8yapLu/yeOWrb5ta7yvfEZ5x2ZeOw==", + "version": "17.3.2", + "resolved": "https://registry.npmjs.org/@taquito/local-forging/-/local-forging-17.3.2.tgz", + "integrity": "sha512-xhTXI/Md2kDykGvHE9ktcxsUt0WymCoLuRpfIm0UfWOkINtmUaXvg9Lwka2e92P6HFMq+nHyzIRMfRsu1psI3g==", "dev": true, "dependencies": { - "@taquito/core": "^17.3.1", - "@taquito/utils": "^17.3.1", + "@taquito/core": "^17.3.2", + "@taquito/utils": "^17.3.2", "bignumber.js": "^9.1.0" }, "engines": { @@ -383,25 +870,26 @@ } }, "node_modules/@taquito/michel-codec": { - "version": "17.3.1", - "resolved": "https://registry.npmjs.org/@taquito/michel-codec/-/michel-codec-17.3.1.tgz", - "integrity": "sha512-dxhrqn+/VBzkhL+bhLO9bVb1r/NKdvSYEoXRWgszNT+enVlokkFBM4kXcvGlFUZklaSiw0dQYd1Zk0sDMkr1sw==", + "version": "17.3.2", + "resolved": "https://registry.npmjs.org/@taquito/michel-codec/-/michel-codec-17.3.2.tgz", + "integrity": "sha512-js/yWXINW5w1WIDgLnTtlXcabtF/N8mt3E+laaHvHSjzxc5BMQXiWcj+vCk0JyHc9Hm/RnVK6AW326R/Bpwj3Q==", "dev": true, "dependencies": { - "@taquito/core": "^17.3.1" + "@taquito/core": "^17.3.2" }, "engines": { "node": ">=16" } }, "node_modules/@taquito/michelson-encoder": { - "version": "17.3.1", - "resolved": "https://registry.npmjs.org/@taquito/michelson-encoder/-/michelson-encoder-17.3.1.tgz", - "integrity": "sha512-PaJYlZnjqDwjvK2qC4j9icqcGLoHHQygnGLs9lPV1quAV1v9yWPe6ABK+miuq2vv4WDMFH6amaXBtLimgPDcuQ==", + "version": "17.3.2", + "resolved": "https://registry.npmjs.org/@taquito/michelson-encoder/-/michelson-encoder-17.3.2.tgz", + "integrity": "sha512-EnZ3kKI/SbYyowGuP9JPDr8tRBshMyfaznNXO/LLCagObMfKlmFEr6cOt0dFi5EHcyJB5vEYC5o5dCn056PnGA==", "dev": true, "dependencies": { - "@taquito/rpc": "^17.3.1", - "@taquito/utils": "^17.3.1", + "@taquito/core": "^17.3.2", + "@taquito/rpc": "^17.3.2", + "@taquito/utils": "^17.3.2", "bignumber.js": "^9.1.0", "fast-json-stable-stringify": "^2.1.0" }, @@ -410,14 +898,14 @@ } }, "node_modules/@taquito/rpc": { - "version": "17.3.1", - "resolved": "https://registry.npmjs.org/@taquito/rpc/-/rpc-17.3.1.tgz", - "integrity": "sha512-VHY5qgUVT4RDXDv7H8DQrrMk1tfnJ6m+BFHmJxYCe7WQCCr8u2TlN0X2N95QhM+LG0LXL/LFlzhQLRBOxLnxsw==", + "version": "17.3.2", + "resolved": "https://registry.npmjs.org/@taquito/rpc/-/rpc-17.3.2.tgz", + "integrity": "sha512-R2rAvRTA/yiGdP4tl268KbY77WBKIWzNHoOaaLJ8SfWWNdgu+UM6/bemNmr3ZTXnabphgF6sBsCsYbkR2hOW4g==", "dev": true, "dependencies": { - "@taquito/core": "^17.3.1", - "@taquito/http-utils": "^17.3.1", - "@taquito/utils": "^17.3.1", + "@taquito/core": "^17.3.2", + "@taquito/http-utils": "^17.3.2", + "@taquito/utils": "^17.3.2", "bignumber.js": "^9.1.0" }, "engines": { @@ -425,9 +913,9 @@ } }, "node_modules/@taquito/signer": { - "version": "17.3.1", - "resolved": "https://registry.npmjs.org/@taquito/signer/-/signer-17.3.1.tgz", - "integrity": "sha512-9fwUuL8vEtzRAc0wX2rYNTkCd4Rhgp2JPrQ7aLz6QWZg0qa43TwjQ1f5o862ryp8PkmJBcA/z9pbD/XHizXfOQ==", + "version": "17.3.2", + "resolved": "https://registry.npmjs.org/@taquito/signer/-/signer-17.3.2.tgz", + "integrity": "sha512-5lFPomhVNgAmNqiTCzCJM6QbPT3XT6HSqcjD3WQx8kM1Xi5P3sLgAMPfucRDVGp2zopZL+MBbt8juIzb7KDy5A==", "dev": true, "dependencies": { "@stablelib/blake2b": "^1.0.1", @@ -436,8 +924,9 @@ "@stablelib/nacl": "^1.0.4", "@stablelib/pbkdf2": "^1.0.1", "@stablelib/sha512": "^1.0.1", - "@taquito/taquito": "^17.3.1", - "@taquito/utils": "^17.3.1", + "@taquito/core": "^17.3.2", + "@taquito/taquito": "^17.3.2", + "@taquito/utils": "^17.3.2", "@types/bn.js": "^5.1.1", "bip39": "3.0.4", "elliptic": "^6.5.4", @@ -449,19 +938,19 @@ } }, "node_modules/@taquito/taquito": { - "version": "17.3.1", - "resolved": "https://registry.npmjs.org/@taquito/taquito/-/taquito-17.3.1.tgz", - "integrity": "sha512-9CnIJv5z6KmYYhycOSTpSh51hH818yzXMjr5iR4Q2raWEtWQHYeXoDcVec1dkI+IITm9cQpLp5Gm++smSBElOA==", + "version": "17.3.2", + "resolved": "https://registry.npmjs.org/@taquito/taquito/-/taquito-17.3.2.tgz", + "integrity": "sha512-q9+9i1LwqGl8MpWKzhJVWolnRY3tmYJL7cLfLA0z73X+/tEyZB+Me5VnzIk0rfssAIyXzdQq3KV0FUuprEolyg==", "dev": true, "hasInstallScript": true, "dependencies": { - "@taquito/core": "^17.3.1", - "@taquito/http-utils": "^17.3.1", - "@taquito/local-forging": "^17.3.1", - "@taquito/michel-codec": "^17.3.1", - "@taquito/michelson-encoder": "^17.3.1", - "@taquito/rpc": "^17.3.1", - "@taquito/utils": "^17.3.1", + "@taquito/core": "^17.3.2", + "@taquito/http-utils": "^17.3.2", + "@taquito/local-forging": "^17.3.2", + "@taquito/michel-codec": "^17.3.2", + "@taquito/michelson-encoder": "^17.3.2", + "@taquito/rpc": "^17.3.2", + "@taquito/utils": "^17.3.2", "bignumber.js": "^9.1.0", "rxjs": "^7.8.1" }, @@ -470,14 +959,14 @@ } }, "node_modules/@taquito/utils": { - "version": "17.3.1", - "resolved": "https://registry.npmjs.org/@taquito/utils/-/utils-17.3.1.tgz", - "integrity": "sha512-NLSFOaZbbs5L7aSV+vgCxj6celHBYLkzwmGvPiCm/mDSj1a1i4bgt8fJ6DbtwDiAmiA9tXPmRileRC4iPEaewg==", + "version": "17.3.2", + "resolved": "https://registry.npmjs.org/@taquito/utils/-/utils-17.3.2.tgz", + "integrity": "sha512-0nT6VmgxUcdWRSH1pE5IfLez7HIXuYBniI4IjrzeX6q5Nu1u+tGuivhi3h77DjNg5/cHAPn0g7G59Gm7Biy5AA==", "dev": true, "dependencies": { "@stablelib/blake2b": "^1.0.1", "@stablelib/ed25519": "^1.0.3", - "@taquito/core": "^17.3.1", + "@taquito/core": "^17.3.2", "@types/bs58check": "^2.1.0", "bignumber.js": "^9.1.0", "blakejs": "^1.2.1", @@ -491,28 +980,31 @@ } }, "node_modules/@types/bn.js": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.2.tgz", - "integrity": "sha512-dkpZu0szUtn9UXTmw+e0AJFd4D2XAxDnsCLdc05SfqpqzPEBft8eQr8uaFitfo/dUUOZERaLec2hHMG87A4Dxg==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.5.tgz", + "integrity": "sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/bs58check": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/bs58check/-/bs58check-2.1.0.tgz", - "integrity": "sha512-OxsysnJQh82vy9DRbOcw9m2j/WiyqZLn0YBhKxdQ+aCwoHj+tWzyCgpwAkr79IfDXZKxc6h7k89T9pwS78CqTQ==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@types/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-xpXaQlOIY1KoXlA/ytHGHpEIU87PJt+g9SH7nC6HdCgaBwT2IEZIwBMHbjuX6BpnfbiUMlmwqurdLDwXpcdmSA==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/node": { - "version": "20.8.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.2.tgz", - "integrity": "sha512-Vvycsc9FQdwhxE3y3DzeIxuEJbWGDsnrxvMADzTDF/lcdR9/K+AQIeAghTQsHtotg/q0j3WEOYS/jQgSdWue3w==", - "dev": true + "version": "20.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz", + "integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/ansi-regex": { "version": "5.0.1", @@ -818,9 +1310,9 @@ } }, "node_modules/fast-glob": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", - "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -985,9 +1477,9 @@ } }, "node_modules/i18next": { - "version": "23.5.1", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.5.1.tgz", - "integrity": "sha512-JelYzcaCoFDaa+Ysbfz2JsGAKkrHiMG6S61+HLBUEIPaF40WMwW9hCPymlQGrP+wWawKxKPuSuD71WZscCsWHg==", + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.6.0.tgz", + "integrity": "sha512-z0Cxr0MGkt+kli306WS4nNNM++9cgt2b2VCMprY92j+AIab/oclgPxdwtTZVLP1zn5t5uo8M6uLsZmYrcjr3HA==", "dev": true, "funding": [ { @@ -1367,6 +1859,15 @@ "sha.js": "bin.js" } }, + "node_modules/shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/siginfo": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", @@ -1503,6 +2004,12 @@ } ] }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/solution/package.json b/solution/package.json index d133157..33da5b4 100644 --- a/solution/package.json +++ b/solution/package.json @@ -1,5 +1,5 @@ { - "name": "training", + "name": "nft-marketplace", "version": "1.0.0", "description": "", "main": "index.js", @@ -10,8 +10,9 @@ "author": "", "license": "ISC", "devDependencies": { - "@taqueria/plugin-contract-types": "^0.40.0", - "@taqueria/plugin-ligo": "^0.40.0", - "@taqueria/plugin-taquito": "^0.40.0" + "@taqueria/plugin-contract-types": "^0.44.0", + "@taqueria/plugin-ligo": "^0.44.0", + "@taqueria/plugin-octez-client": "^0.42.0", + "@taqueria/plugin-taquito": "^0.42.0" } }