diff --git a/packages/demo/components/common/mintMeshToken.ts b/packages/demo/components/common/mintMeshToken.ts
new file mode 100644
index 00000000..db6ee722
--- /dev/null
+++ b/packages/demo/components/common/mintMeshToken.ts
@@ -0,0 +1,57 @@
+import {
+ ForgeScript,
+ Transaction,
+ AppWallet,
+ BlockfrostProvider,
+} from '@meshsdk/core';
+import type { AssetMetadata, Mint } from '@meshsdk/core';
+import { demoMnemonic } from '../../configs/demo';
+
+export default async function mintMeshToken({setLoading, setResponse, wallet}) {
+ setLoading(true);
+ try {
+ const blockchainProvider = new BlockfrostProvider(
+ process.env.NEXT_PUBLIC_BLOCKFROST_API_KEY_PREPROD!
+ );
+
+ const mintingWallet = new AppWallet({
+ networkId: 0,
+ fetcher: blockchainProvider,
+ submitter: blockchainProvider,
+ key: {
+ type: 'mnemonic',
+ words: demoMnemonic,
+ },
+ });
+
+ const usedAddress = await wallet.getUsedAddresses();
+ const address = usedAddress[0];
+ const forgingScript = ForgeScript.withOneSignature(
+ mintingWallet.getPaymentAddress()
+ );
+
+ const tx = new Transaction({ initiator: wallet });
+
+ const assetMetadata: AssetMetadata = {
+ name: 'Mesh Token',
+ image: 'ipfs://QmRzicpReutwCkM6aotuKjErFCUD213DpwPq6ByuzMJaua',
+ mediaType: 'image/jpg',
+ description: 'This NFT is minted by Mesh (https://meshjs.dev/).',
+ };
+ const asset: Mint = {
+ assetName: 'MeshToken',
+ assetQuantity: '1',
+ metadata: assetMetadata,
+ label: '721',
+ recipient: address,
+ };
+ tx.mintAsset(forgingScript, asset);
+
+ const unsignedTx = await tx.build();
+ const signedTx = await wallet.signTx(unsignedTx, true);
+ const signedTx2 = await mintingWallet.signTx(signedTx, true);
+ const txHash = await wallet.submitTx(signedTx2);
+ setResponse(txHash);
+ } catch (error) {}
+ setLoading(false);
+}
\ No newline at end of file
diff --git a/packages/demo/components/common/showMoreDetails.tsx b/packages/demo/components/common/showMoreDetails.tsx
new file mode 100644
index 00000000..1f9775d8
--- /dev/null
+++ b/packages/demo/components/common/showMoreDetails.tsx
@@ -0,0 +1,33 @@
+import { ChevronUpIcon, ChevronDownIcon } from '@heroicons/react/24/solid';
+import { useState } from 'react';
+
+export default function ShowMoreDetails({ children, label = 'Show details' }) {
+ const [show, setShow] = useState(false);
+ return (
+ <>
+
+ setShow(!show)}
+ >
+ {label}
+ {show ? (
+
+ ) : (
+
+ )}
+
+
+
+ >
+ );
+}
diff --git a/packages/demo/components/pages/contracts/marketplaceV1/hero.tsx b/packages/demo/components/pages/contracts/marketplaceV1/hero.tsx
index 5f6dda05..e50cfa10 100644
--- a/packages/demo/components/pages/contracts/marketplaceV1/hero.tsx
+++ b/packages/demo/components/pages/contracts/marketplaceV1/hero.tsx
@@ -1,19 +1,12 @@
import { ShoppingCartIcon } from '@heroicons/react/24/solid';
-import {
- ForgeScript,
- Transaction,
- AppWallet,
- BlockfrostProvider,
-} from '@meshsdk/core';
-import type { AssetMetadata, Mint } from '@meshsdk/core';
import { CardanoWallet, useWallet } from '@meshsdk/react';
import Button from '../../../ui/button';
import Card from '../../../ui/card';
import { useState } from 'react';
-import { demoMnemonic } from '../../../../configs/demo';
import RunDemoResult from '../../../common/runDemoResult';
import Codeblock from '../../../ui/codeblock';
import Link from 'next/link';
+import mintMeshToken from '../../../common/mintMeshToken';
export default function Hero() {
let codeInit = ``;
@@ -134,55 +127,6 @@ function Demo() {
const [loading, setLoading] = useState(false);
const [response, setResponse] = useState(null);
- async function mintMeshToken() {
- setLoading(true);
- try {
- const blockchainProvider = new BlockfrostProvider(
- process.env.NEXT_PUBLIC_BLOCKFROST_API_KEY_PREPROD!
- );
-
- const mintingWallet = new AppWallet({
- networkId: 0,
- fetcher: blockchainProvider,
- submitter: blockchainProvider,
- key: {
- type: 'mnemonic',
- words: demoMnemonic,
- },
- });
-
- const usedAddress = await wallet.getUsedAddresses();
- const address = usedAddress[0];
- const forgingScript = ForgeScript.withOneSignature(
- mintingWallet.getPaymentAddress()
- );
-
- const tx = new Transaction({ initiator: wallet });
-
- const assetMetadata: AssetMetadata = {
- name: 'Mesh Token',
- image: 'ipfs://QmRzicpReutwCkM6aotuKjErFCUD213DpwPq6ByuzMJaua',
- mediaType: 'image/jpg',
- description: 'This NFT is minted by Mesh (https://meshjs.dev/).',
- };
- const asset: Mint = {
- assetName: 'MeshToken',
- assetQuantity: '1',
- metadata: assetMetadata,
- label: '721',
- recipient: address,
- };
- tx.mintAsset(forgingScript, asset);
-
- const unsignedTx = await tx.build();
- const signedTx = await wallet.signTx(unsignedTx, true);
- const signedTx2 = await mintingWallet.signTx(signedTx, true);
- const txHash = await wallet.submitTx(signedTx2);
- setResponse(txHash);
- } catch (error) {}
- setLoading(false);
- }
-
return (
Try the demo
@@ -196,7 +140,7 @@ function Demo() {
<>
Next, mint a Mesh Token. We will use list this NFT for sale.
mintMeshToken()}
+ onClick={() => mintMeshToken({ setLoading, setResponse, wallet })}
style={
loading ? 'warning' : response !== null ? 'success' : 'light'
}
diff --git a/packages/demo/components/pages/contracts/marketplaceV1/listAsset.tsx b/packages/demo/components/pages/contracts/marketplaceV1/listAsset.tsx
index 66ed258d..e5c91e5f 100644
--- a/packages/demo/components/pages/contracts/marketplaceV1/listAsset.tsx
+++ b/packages/demo/components/pages/contracts/marketplaceV1/listAsset.tsx
@@ -9,15 +9,6 @@ import useLocalStorage from '../../../../hooks/useLocalStorage';
import { getMarketplace, asset, price } from './config';
import Input from '../../../ui/input';
-import {
- BlockfrostProvider,
- AppWallet,
- largestFirstMultiAsset,
- largestFirst,
- Transaction,
- keepRelevant,
-} from '@meshsdk/core';
-
export default function MarketplaceListAsset() {
return (
<>
diff --git a/packages/demo/pages/guides/smart-contract-transactions.tsx b/packages/demo/pages/guides/smart-contract-transactions.tsx
index fab7eb0a..6a4e7108 100644
--- a/packages/demo/pages/guides/smart-contract-transactions.tsx
+++ b/packages/demo/pages/guides/smart-contract-transactions.tsx
@@ -4,13 +4,33 @@ import GuidesLayout from '../../components/pages/guides/layout';
import Codeblock from '../../components/ui/codeblock';
import { Element } from 'react-scroll';
import Metatags from '../../components/site/metatags';
-import Image from '../../components/ui/image';
+import { CardanoWallet, useWallet } from '@meshsdk/react';
+import { BlockfrostProvider } from '@meshsdk/core';
+import {
+ Transaction,
+ resolvePaymentKeyHash,
+ resolvePlutusScriptAddress,
+ resolveDataHash,
+} from '@meshsdk/core';
+import type { Data, PlutusScript } from '@meshsdk/core';
+import { useEffect, useState } from 'react';
+import Button from '../../components/ui/button';
+import RunDemoResult from '../../components/common/runDemoResult';
+import mintMeshToken from '../../components/common/mintMeshToken';
+import ShowMoreDetails from '../../components/common/showMoreDetails';
+import useLocalStorage from '../../hooks/useLocalStorage';
+
+const policyId = 'd9312da562da182b02322fd8acb536f37eb9d29fba7c49dc17255527';
+const assetId = '4d657368546f6b656e';
+const price = 10000000;
const GuideSmartContractTransactionsPage: NextPage = () => {
const sidebarItems = [
- { label: 'List asset', to: 'listasset' },
- { label: 'Setup MongoDB', to: 'mongodb' },
- { label: 'Customize Marketplace', to: 'marketplace' },
+ { label: 'Initialize the Marketplace', to: 'init' },
+ { label: 'Listing asset for sale', to: 'listasset' },
+ { label: 'Cancel the listing', to: 'cancelListing' },
+ { label: 'Purchase the listed asset', to: 'purchaseAsset' },
+ { label: 'Update listing', to: 'updateListing' },
];
return (
@@ -29,15 +49,66 @@ const GuideSmartContractTransactionsPage: NextPage = () => {
+
+
+
>
);
};
function IntroSection() {
+ const { wallet, connected } = useWallet();
+ const [loading, setLoading] = useState(false);
+ const [response, setResponse] = useState(null);
+
return (
<>
-
+
+ In this guide, we will build a marketplace where users can list their
+ assets for sale and purchase the listed assets. The contract is written
+ in Haskell, and it is open-source and available on{' '}
+
+ GitHub
+
+ .
+
+
+ If you would like to try out the demo in this guide, you can mint a Mesh
+ token which we will be using it for listing on this marketplace demo.
+ Connect your wallet and mint the token on preprod/preview testnet.
+
+
+ {connected ? (
+ <>
+ mintMeshToken({ setLoading, setResponse, wallet })}
+ style={
+ loading ? 'warning' : response !== null ? 'success' : 'light'
+ }
+ disabled={loading}
+ >
+ Mint Mesh Token
+
+ {response !== null && (
+ <>
+ Mesh token minted successful.
+
+ >
+ )}
+ >
+ ) : (
+
+ )}
+
+
+ If you have the Mesh token, we can list it on the marketplace in this
+ demo.
+
>
);
}
@@ -61,27 +132,24 @@ function Init() {
isJson={false}
/>
- Let's say we are testing our marketplace implmentation on{' '}
- proprod
, we can resolve the Plutus script address with
- the following code:
+ Let's say we are testing our marketplace implementation on{' '}
+ proprod
, we can resolve the Plutus script address with{' '}
+ resolvePlutusScriptAddress
where we input the{' '}
+ PlutusScript
and define the network
(in our
+ demo we use 0
for testnet):
-
- Create reference UTxO
-
-
>
);
}
function ListAsset() {
-
let code = ``;
- code += `const addr = (await wallet.getUsedAddresses())[0];\n`;
+ code += `const addr = (await wallet.getUsedAddresses())[0];\n\n`;
code += `const datumConstr: Data = {\n`;
code += ` alternative: 0,\n`;
code += ` fields: [\n`;
@@ -90,7 +158,7 @@ function ListAsset() {
code += ` policyId,\n`;
code += ` assetId,\n`;
code += ` ],\n`;
- code += `};\n`;
+ code += `};\n\n`;
code += `const tx = new Transaction({ initiator: wallet })\n`;
code += `.sendAssets(\n`;
code += ` {\n`;
@@ -102,10 +170,10 @@ function ListAsset() {
code += ` [\n`;
code += ` {\n`;
code += ` unit: policyId + assetId,\n`;
- code += ` quantity: quantity.toString(),\n`;
+ code += ` quantity: '1',\n`;
code += ` },\n`;
code += ` ]\n`;
- code += `);\n`;
+ code += `);\n\n`;
code += `const unsignedTx = await tx.build();\n`;
code += `const signedTx = await wallet.signTx(unsignedTx);\n`;
code += `const txHash = await wallet.submitTx(signedTx);\n`;
@@ -114,27 +182,86 @@ function ListAsset() {
codeDatum += `const datumConstr: Data = {\n`;
codeDatum += ` alternative: 0,\n`;
codeDatum += ` fields: [\n`;
- codeDatum += ` 'seller address as pubkeyhash',\n`;
- codeDatum += ` 'price',\n`;
- codeDatum += ` 'policy ID of token for sale',\n`;
- codeDatum += ` 'asset name of token for sale in hex',\n`;
+ codeDatum += ` resolvePaymentKeyHash(addr), // seller address as pubkeyhash\n`;
+ codeDatum += ` listPriceInLovelace, // price\n`;
+ codeDatum += ` policyId, // policy ID of token\n`;
+ codeDatum += ` assetId, // asset name of token in hex\n`;
codeDatum += ` ],\n`;
codeDatum += `};\n`;
let codeAddress = `const addr = (await wallet.getUsedAddresses())[0];`;
+ let codeTransaction = ``;
+ codeTransaction += `const tx = new Transaction({ initiator: wallet })\n`;
+ codeTransaction += `.sendAssets(\n`;
+ codeTransaction += ` {\n`;
+ codeTransaction += ` address: scriptAddress,\n`;
+ codeTransaction += ` datum: {\n`;
+ codeTransaction += ` value: datumConstr,\n`;
+ codeTransaction += ` },\n`;
+ codeTransaction += ` },\n`;
+ codeTransaction += ` [\n`;
+ codeTransaction += ` {\n`;
+ codeTransaction += ` unit: policyId + assetId,\n`;
+ codeTransaction += ` quantity: '1',\n`;
+ codeTransaction += ` },\n`;
+ codeTransaction += ` ]\n`;
+ codeTransaction += `);\n\n`;
+ codeTransaction += `const unsignedTx = await tx.build();\n`;
+ codeTransaction += `const signedTx = await wallet.signTx(unsignedTx);\n`;
+ codeTransaction += `const txHash = await wallet.submitTx(signedTx);\n`;
+
+ //
+
+ const { wallet, connected } = useWallet();
+ const [loading, setLoading] = useState(false);
+ const [response, setResponse] = useState(null);
+ const [userLocalStorage, setUserlocalStorage] = useLocalStorage(
+ 'meshMarketplaceDemo',
+ {}
+ );
+
+ const blockchainProvider = new BlockfrostProvider(
+ process.env.NEXT_PUBLIC_BLOCKFROST_API_KEY_PREPROD!
+ );
+
+ const { listAsset } = useMarketplacePlutus({
+ blockchainFetcher: blockchainProvider,
+ network: 0,
+ });
+
+ async function doListAsset() {
+ setLoading(true);
+ setResponse(null);
+
+ try {
+ const address = (await wallet.getUsedAddresses())[0];
+
+ const txHash = await listAsset({
+ policyId: policyId,
+ assetId: assetId,
+ listPriceInLovelace: price,
+ quantity: '1',
+ });
+ setUserlocalStorage({
+ sellerAddress: address,
+ listPrice: price,
+ });
+ setResponse(txHash);
+ } catch (error) {
+ setResponse(`${error}`);
+ }
+ setLoading(false);
+ }
+
return (
- Listing asset for sale
+ Listing Asset for Sale
-
- Listing assets for sale is straightforward. The following code shows how
- to list an asset for sale:
-
-
Firstly, we get the user's wallet address, this address is the seller's
- address. We acquired the first address from the connected wallet with:
+ address. We can acquired the first wallet's address from the connected
+ wallet with getUsedAddresses()
:
Then, we create the datum that has the following schema:
@@ -146,23 +273,624 @@ function ListAsset() {
build the transaction, the seller sign the transaction and submit the
transaction to the blockchain.
+
+
+ Give the demo a try!
+
+ {connected ? (
+ <>
+ doListAsset()}
+ style={
+ loading ? 'warning' : response !== null ? 'success' : 'light'
+ }
+ disabled={loading}
+ >
+ List Mesh Token
+
+ {response !== null && (
+ <>
+ Asset listed successful.
+
+ >
+ )}
+ >
+ ) : (
+
+ )}
+
+
+ Now implement your own marketplace. Note: you may need a database to
+ store the listing information.
+
+
+
+
+
);
}
-export default GuideSmartContractTransactionsPage;
+function CancelListing() {
+ let codeDatum = ``;
+ codeDatum += `const datumConstr: Data = {\n`;
+ codeDatum += ` alternative: 0,\n`;
+ codeDatum += ` fields: [\n`;
+ codeDatum += ` resolvePaymentKeyHash(addr), // seller address as pubkeyhash\n`;
+ codeDatum += ` listPriceInLovelace, // price\n`;
+ codeDatum += ` policyId, // policy ID of token\n`;
+ codeDatum += ` assetId, // asset name of token in hex\n`;
+ codeDatum += ` ],\n`;
+ codeDatum += `};\n`;
+ let codegetAssetUtxo = ``;
+ codegetAssetUtxo += `async function _getAssetUtxo({ scriptAddress, asset, datum }) {\n`;
+ codegetAssetUtxo += ` const utxos = await blockchainFetcher.fetchAddressUTxOs(\n`;
+ codegetAssetUtxo += ` scriptAddress,\n`;
+ codegetAssetUtxo += ` asset\n`;
+ codegetAssetUtxo += ` );\n`;
+ codegetAssetUtxo += ` if (utxos.length == 0) {\n`;
+ codegetAssetUtxo += ` throw 'No listing found.';\n`;
+ codegetAssetUtxo += ` }\n`;
+ codegetAssetUtxo += ` const dataHash = resolveDataHash(datum);\n`;
+ codegetAssetUtxo += ` let utxo = utxos.find((utxo: any) => {\n`;
+ codegetAssetUtxo += ` return utxo.output.dataHash == dataHash;\n`;
+ codegetAssetUtxo += ` });\n`;
+ codegetAssetUtxo += ` return utxo;\n`;
+ codegetAssetUtxo += `}\n`;
+ let codeRedeemer = `const redeemer = { data: { alternative: 1, fields: [] } };\n`;
-import { useEffect, useState } from 'react';
-import { useWallet } from '@meshsdk/react';
-import {
- Transaction,
- resolvePaymentKeyHash,
- resolvePlutusScriptAddress,
- resolveDataHash,
-} from '@meshsdk/core';
-import type { Data, PlutusScript } from '@meshsdk/core';
+ let codeTransaction = '';
+ codeTransaction += `const tx = new Transaction({ initiator: wallet })\n`;
+ codeTransaction += ` .redeemValue({\n`;
+ codeTransaction += ` value: assetUtxo,\n`;
+ codeTransaction += ` script: script,\n`;
+ codeTransaction += ` datum: datumConstr,\n`;
+ codeTransaction += ` redeemer: redeemer,\n`;
+ codeTransaction += ` })\n`;
+ codeTransaction += ` .sendValue(addr, assetUtxo)\n`;
+ codeTransaction += ` .setRequiredSigners([addr]);\n`;
+ codeTransaction += `\n`;
+ codeTransaction += `const unsignedTx = await tx.build();\n`;
+ codeTransaction += `const signedTx = await wallet.signTx(unsignedTx, true);\n`;
+ codeTransaction += `const txHash = await wallet.submitTx(signedTx);\n`;
+
+ let code = ``;
+ code += `const addr = (await wallet.getUsedAddresses())[0];\n`;
+ code += `\n`;
+ code += `const datumConstr: Data = {\n`;
+ code += ` alternative: 0,\n`;
+ code += ` fields: [\n`;
+ code += ` resolvePaymentKeyHash(addr),\n`;
+ code += ` listPriceInLovelace,\n`;
+ code += ` policyId,\n`;
+ code += ` assetId,\n`;
+ code += ` ],\n`;
+ code += `};\n`;
+ code += `\n`;
+ code += `const assetUtxo = await _getAssetUtxo({\n`;
+ code += ` scriptAddress: scriptAddress,\n`;
+ code += ` asset: '${policyId}${assetId}',\n`;
+ code += ` datum: datumConstr,\n`;
+ code += `});\n`;
+ code += `\n`;
+ code += `if (assetUtxo === undefined) {\n`;
+ code += ` throw 'No listing found.';\n`;
+ code += `}\n`;
+ code += `\n`;
+ code += `const redeemer = { data: { alternative: 1, fields: [] } };\n`;
+ code += `\n`;
+ code += `const tx = new Transaction({ initiator: wallet })\n`;
+ code += ` .redeemValue({\n`;
+ code += ` value: assetUtxo,\n`;
+ code += ` script: script,\n`;
+ code += ` datum: datumConstr,\n`;
+ code += ` redeemer: redeemer,\n`;
+ code += ` })\n`;
+ code += ` .sendValue(addr, assetUtxo)\n`;
+ code += ` .setRequiredSigners([addr]);\n`;
+ code += `\n`;
+ code += `const unsignedTx = await tx.build();\n`;
+ code += `const signedTx = await wallet.signTx(unsignedTx, true);\n`;
+ code += `const txHash = await wallet.submitTx(signedTx);\n`;
+
+ //
+
+ const { connected } = useWallet();
+ const [loading, setLoading] = useState(false);
+ const [response, setResponse] = useState(null);
+
+ const blockchainProvider = new BlockfrostProvider(
+ process.env.NEXT_PUBLIC_BLOCKFROST_API_KEY_PREPROD!
+ );
+
+ const { cancelListing } = useMarketplacePlutus({
+ blockchainFetcher: blockchainProvider,
+ network: 0,
+ });
+
+ async function doCancelListing() {
+ setLoading(true);
+ setResponse(null);
+
+ try {
+ const txHash = await cancelListing({
+ policyId: policyId,
+ assetId: assetId,
+ listPriceInLovelace: price,
+ });
+ setResponse(txHash);
+ } catch (error) {
+ setResponse(`${error}`);
+ }
+ setLoading(false);
+ }
+
+ return (
+
+ Cancel the Listing
+
+
+ Next, we will learn how to cancel the listing. Only the seller of the
+ NFT can cancel the listing, thus we will define the datum with the
+ following information:
+
+
+
+
+
+ For cancel, update and purchase endpoints, we need the UTxO in the
+ script address as inputs to create the transaction. We use{' '}
+ fetchAddressUTxOs()
from one of the{' '}
+ providers to query for UTxO that contain
+ the asset of our interest. Next, we filter the UTxO list by the datum
+ hash, which we can get from the datum with{' '}
+ resolveDataHash()
(see{' '}
+ resolvers). Here is the
+ implementation for _getAssetUtxo()
, to get the UTxO in the
+ script address, asset, and use the datum hash to filter the correct UTxO
+ for the transaction:
+
+
+
+
+ Next, we define the redeemer for cancel listing:
+
+
+
+
+ Finally, we can build the transaction with the following code. We use
+ the redeemValue()
method to redeem the UTxO in the script
+ address, and send the value back to the seller's address. We also need
+ to set the required signers to the seller's address.
+
+
+
+
+
+ Give the demo a try! Try cancelling the listing and get your NFT back if
+ you have listed one.
+
+
+ {connected ? (
+ <>
+ doCancelListing()}
+ style={
+ loading ? 'warning' : response !== null ? 'success' : 'light'
+ }
+ disabled={loading}
+ >
+ Cancel listing
+
+ {response !== null && (
+ <>
+ Listing cancelled successful.
+
+ >
+ )}
+ >
+ ) : (
+
+ )}
+
+
+
+
+
+ );
+}
+
+function PurchaseListing() {
+ let codeAddress =
+ "const addr = (await wallet.getUsedAddresses())[0]; // buyer's address";
+
+ let codeDatum = ``;
+ codeDatum += `const datumConstr: Data = {\n`;
+ codeDatum += ` alternative: 0,\n`;
+ codeDatum += ` fields: [\n`;
+ codeDatum += ` resolvePaymentKeyHash(sellerAddr), // seller address as pubkeyhash\n`;
+ codeDatum += ` listPriceInLovelace, // price\n`;
+ codeDatum += ` policyId, // policy ID of token\n`;
+ codeDatum += ` assetId, // asset name of token in hex\n`;
+ codeDatum += ` ],\n`;
+ codeDatum += `};\n`;
+
+ let codeRedeemer = `const redeemer = { data: { alternative: 0, fields: [] } };\n`;
+
+ let codeTransaction = '';
+ codeTransaction += `const tx = new Transaction({ initiator: wallet })\n`;
+ codeTransaction += ` .redeemValue({\n`;
+ codeTransaction += ` value: assetUtxo,\n`;
+ codeTransaction += ` script: script,\n`;
+ codeTransaction += ` datum: datumConstr,\n`;
+ codeTransaction += ` redeemer: redeemer,\n`;
+ codeTransaction += ` })\n`;
+ codeTransaction += ` .sendValue(addr, assetUtxo)\n`;
+ codeTransaction += ` .sendLovelace(sellerAddr, listPriceInLovelace.toString())\n`;
+ codeTransaction += ` .setRequiredSigners([addr]);\n`;
+ codeTransaction += `\n`;
+ codeTransaction += `const unsignedTx = await tx.build();\n`;
+ codeTransaction += `const signedTx = await wallet.signTx(unsignedTx, true);\n`;
+ codeTransaction += `const txHash = await wallet.submitTx(signedTx);\n`;
+
+ let code = ``;
+ code += `const addr = (await wallet.getUsedAddresses())[0]; // buyer's address\n`;
+ code += `\n`;
+ code += `const datumConstr: Data = {\n`;
+ code += ` alternative: 0,\n`;
+ code += ` fields: [\n`;
+ code += ` resolvePaymentKeyHash(sellerAddr),\n`;
+ code += ` listPriceInLovelace,\n`;
+ code += ` policyId,\n`;
+ code += ` assetId,\n`;
+ code += ` ],\n`;
+ code += `};\n`;
+ code += `\n`;
+ code += `const assetUtxo = await _getAssetUtxo({\n`;
+ code += ` scriptAddress: scriptAddress,\n`;
+ code += ` asset: '${policyId}${assetId}',\n`;
+ code += ` datum: datumConstr,\n`;
+ code += `});\n`;
+ code += `\n`;
+ code += `const redeemer = { data: { alternative: 0, fields: [] } };\n`;
+ code += `\n`;
+ code += `const tx = new Transaction({ initiator: wallet })\n`;
+ code += ` .redeemValue({\n`;
+ code += ` value: assetUtxo,\n`;
+ code += ` script: script,\n`;
+ code += ` datum: datumConstr,\n`;
+ code += ` redeemer: redeemer,\n`;
+ code += ` })\n`;
+ code += ` .sendValue(addr, assetUtxo)\n`;
+ code += ` .sendLovelace(sellerAddr, listPriceInLovelace.toString())\n`;
+ code += ` .setRequiredSigners([addr]);\n`;
+ code += `\n`;
+ code += `const unsignedTx = await tx.build();\n`;
+ code += `const signedTx = await wallet.signTx(unsignedTx, true);\n`;
+ code += `const txHash = await wallet.submitTx(signedTx);\n`;
+
+ //
+
+ const { connected } = useWallet();
+ const [loading, setLoading] = useState(false);
+ const [response, setResponse] = useState(null);
+ const [userLocalStorage, setUserlocalStorage] = useLocalStorage(
+ 'meshMarketplaceDemo',
+ {}
+ );
+
+ const blockchainProvider = new BlockfrostProvider(
+ process.env.NEXT_PUBLIC_BLOCKFROST_API_KEY_PREPROD!
+ );
+
+ const { buyAsset } = useMarketplacePlutus({
+ blockchainFetcher: blockchainProvider,
+ network: 0,
+ });
+
+ async function doPurchase() {
+ setLoading(true);
+ setResponse(null);
+
+ try {
+ const txHash = await buyAsset({
+ policyId: policyId,
+ assetId: assetId,
+ listPriceInLovelace: price,
+ sellerAddr: userLocalStorage.sellerAddress,
+ });
+ setResponse(txHash);
+ } catch (error) {
+ setResponse(`${error}`);
+ }
+ setLoading(false);
+ }
+
+ return (
+
+ Purchase the Listed Asset
+
+
+ A key feature of a marketplace is the ability to purchase the listed
+ asset from the seller. The purchase endpoint will take the asset, the
+ price and the seller address as parameters. These parameters will be
+ used to create the datum for the validator. With a successful purchase
+ will transfer the asset to the buyer and the listed price to the seller.
+
+
+ First, we need the buyer's address to send the asset to:
+
+
+
+
+ Like the cancel endpoint, we need to create the datum for the validator:
+
+
+
+
+ Then, we will define the redeemer:
+
+
+
+
+ Finally, we can build the transaction and submit it to the blockchain.
+ We will use the redeemValue()
method to redeem the asset
+ from the validator, use sendValue()
to send the asset to
+ the buyer and sendLovelace()
to send the payment price to
+ the seller:
+
+
+
+
+
+ Give the demo a try! Try purchasing the NFT by connecting another
+ wallet.
+
+
+ {connected ? (
+ <>
+ doPurchase()}
+ style={
+ loading ? 'warning' : response !== null ? 'success' : 'light'
+ }
+ disabled={loading}
+ >
+ Purchase NFT
+
+ {response !== null && (
+ <>
+ Purchase successful.
+
+ >
+ )}
+ >
+ ) : (
+
+ )}
+
+
+
+
+
+ );
+}
+
+function UpdateListing() {
+ let codeDatum = ``;
+ codeDatum += `const datumConstr: Data = {\n`;
+ codeDatum += ` alternative: 0,\n`;
+ codeDatum += ` fields: [\n`;
+ codeDatum += ` resolvePaymentKeyHash(addr), // seller address as pubkeyhash\n`;
+ codeDatum += ` listPriceInLovelace, // listed price\n`;
+ codeDatum += ` policyId, // policy ID of token\n`;
+ codeDatum += ` assetId, // asset name of token in hex\n`;
+ codeDatum += ` ],\n`;
+ codeDatum += `};\n`;
+
+ let codeDatumNew = '';
+ codeDatumNew += `const datumConstrNew: Data = {\n`;
+ codeDatumNew += ` alternative: 0,\n`;
+ codeDatumNew += ` fields: [\n`;
+ codeDatumNew += ` resolvePaymentKeyHash(addr), // seller address as pubkeyhash\n`;
+ codeDatumNew += ` updatedPriceInLovelace, // updated price\n`;
+ codeDatumNew += ` policyId, // policy ID of token\n`;
+ codeDatumNew += ` assetId, // asset name of token in hex\n`;
+ codeDatumNew += ` ],\n`;
+ codeDatumNew += `};\n`;
+
+ let codeRedeemer = `const redeemer = { data: { alternative: 1, fields: [] } };\n`;
+
+ let codeTransaction = '';
+ codeTransaction += `const tx = new Transaction({ initiator: wallet })\n`;
+ codeTransaction += ` .redeemValue({\n`;
+ codeTransaction += ` value: assetUtxo,\n`;
+ codeTransaction += ` script: script,\n`;
+ codeTransaction += ` datum: datumConstr,\n`;
+ codeTransaction += ` redeemer: redeemer,\n`;
+ codeTransaction += ` })\n`;
+ codeTransaction += ` .setRequiredSigners([addr])\n`;
+ codeTransaction += ` .sendAssets(\n`;
+ codeTransaction += ` {\n`;
+ codeTransaction += ` address: scriptAddress,\n`;
+ codeTransaction += ` datum: {\n`;
+ codeTransaction += ` value: datumConstrNew,\n`;
+ codeTransaction += ` },\n`;
+ codeTransaction += ` },\n`;
+ codeTransaction += ` [\n`;
+ codeTransaction += ` {\n`;
+ codeTransaction += ` unit: '${policyId}${assetId}',\n`;
+ codeTransaction += ` quantity: '1',\n`;
+ codeTransaction += ` },\n`;
+ codeTransaction += ` ]\n`;
+ codeTransaction += ` );\n`;
+ codeTransaction += `\n`;
+ codeTransaction += `const unsignedTx = await tx.build();\n`;
+ codeTransaction += `const signedTx = await wallet.signTx(unsignedTx, true);\n`;
+ codeTransaction += `const txHash = await wallet.submitTx(signedTx);\n`;
+
+ let code = ``;
+ code += `const addr = (await wallet.getUsedAddresses())[0];\n`;
+ code += `\n`;
+ code += `const datumConstr: Data = {\n`;
+ code += ` alternative: 0,\n`;
+ code += ` fields: [\n`;
+ code += ` resolvePaymentKeyHash(addr),\n`;
+ code += ` listPriceInLovelace,\n`;
+ code += ` policyId,\n`;
+ code += ` assetId,\n`;
+ code += ` ],\n`;
+ code += `};\n`;
+ code += `\n`;
+ code += `const datumConstrNew: Data = {\n`;
+ code += ` alternative: 0,\n`;
+ code += ` fields: [\n`;
+ code += ` resolvePaymentKeyHash(addr),\n`;
+ code += ` updatedPriceInLovelace,\n`;
+ code += ` policyId,\n`;
+ code += ` assetId,\n`;
+ code += ` ],\n`;
+ code += `};\n`;
+ code += `\n`;
+ code += `const assetUtxo = await _getAssetUtxo({\n`;
+ code += ` scriptAddress: scriptAddress,\n`;
+ code += ` asset: '${policyId}${assetId}',\n`;
+ code += ` datum: datumConstr,\n`;
+ code += `});\n`;
+ code += `\n`;
+ code += `const redeemer = { data: { alternative: 1, fields: [] } };\n`;
+ code += `\n`;
+ code += `const tx = new Transaction({ initiator: wallet })\n`;
+ code += ` .redeemValue({\n`;
+ code += ` value: assetUtxo,\n`;
+ code += ` script: script,\n`;
+ code += ` datum: datumConstr,\n`;
+ code += ` redeemer: redeemer,\n`;
+ code += ` })\n`;
+ code += ` .setRequiredSigners([addr])\n`;
+ code += ` .sendAssets(\n`;
+ code += ` {\n`;
+ code += ` address: scriptAddress,\n`;
+ code += ` datum: {\n`;
+ code += ` value: datumConstrNew,\n`;
+ code += ` },\n`;
+ code += ` },\n`;
+ code += ` [\n`;
+ code += ` {\n`;
+ code += ` unit: '${policyId}${assetId}',\n`;
+ code += ` quantity: '1',\n`;
+ code += ` },\n`;
+ code += ` ]\n`;
+ code += ` );\n`;
+ code += `\n`;
+ code += `const unsignedTx = await tx.build();\n`;
+ code += `const signedTx = await wallet.signTx(unsignedTx, true);\n`;
+ code += `const txHash = await wallet.submitTx(signedTx);\n`;
+
+ //
+
+ const { connected } = useWallet();
+ const [loading, setLoading] = useState(false);
+ const [response, setResponse] = useState(null);
+
+ const blockchainProvider = new BlockfrostProvider(
+ process.env.NEXT_PUBLIC_BLOCKFROST_API_KEY_PREPROD!
+ );
+
+ const { updateListing } = useMarketplacePlutus({
+ blockchainFetcher: blockchainProvider,
+ network: 0,
+ });
+
+ async function doUpdatelListing() {
+ setLoading(true);
+ setResponse(null);
+
+ try {
+ const txHash = await updateListing({
+ policyId: policyId,
+ assetId: assetId,
+ listPriceInLovelace: price,
+ quantity: '1',
+ updatedPriceInLovelace: price,
+ });
+ setResponse(txHash);
+ } catch (error) {
+ setResponse(`${error}`);
+ }
+ setLoading(false);
+ }
+
+ return (
+
+ Update the Listing
+
+
+ Finally, we will learn how to update the listing. Only the seller of the
+ NFT can update the listing, thus we will define the datum with the
+ following information:
+
+
+
+
+ We will also need to create the updated datum with the new price:
+
+
+
+ Next, we define the redeemer for updating the listing:
+
+
+
+
+ Finally, we can build the transaction to update the listing. We use the{' '}
+ redeemValue()
method to redeem the UTxO in the script
+ address with the original datum, and then we use the{' '}
+ sendAssets()
method to send the NFT to the same script
+ address, with the new datum.
+
+
+
+
+
+ Give the demo a try! Try updating the listing. Just simplicity for this
+ demo, we will update the price to the same price, but in a real
+ application, you would update the price to a new price.
+
+
+ {connected ? (
+ <>
+ doUpdatelListing()}
+ style={
+ loading ? 'warning' : response !== null ? 'success' : 'light'
+ }
+ disabled={loading}
+ >
+ Update listing
+
+ {response !== null && (
+ <>
+ Listing updated successful.
+
+ >
+ )}
+ >
+ ) : (
+
+ )}
+
+
+
+
+
+ );
+}
+
+/////////
+
+export default GuideSmartContractTransactionsPage;
export const scriptCbor =
'590d74590d7101000033232323232323232323232323233223232323232223232323223223232533533300b3333573466e1cd55cea804a400046666444424666600200a0080060046eb8d5d0a8049bad35742a0106eb8d5d0a8039bae357426ae89401c8c98c8078cd5ce00f80f00e1999ab9a3370ea0089001109100091999ab9a3370ea00a9000109100111931900f99ab9c02001f01d01c3333573466e1cd55cea80124000466442466002006004646464646464646464646464646666ae68cdc39aab9d500c480008cccccccccccc88888888888848cccccccccccc00403403002c02802402001c01801401000c008cd406c070d5d0a80619a80d80e1aba1500b33501b01d35742a014666aa03eeb94078d5d0a804999aa80fbae501e35742a01066a03604c6ae85401cccd5407c09dd69aba150063232323333573466e1cd55cea801240004664424660020060046464646666ae68cdc39aab9d5002480008cc8848cc00400c008cd40c5d69aba150023032357426ae8940088c98c80d0cd5ce01a81a01909aab9e5001137540026ae854008c8c8c8cccd5cd19b8735573aa004900011991091980080180119a818bad35742a00460646ae84d5d1280111931901a19ab9c035034032135573ca00226ea8004d5d09aba2500223263203033573806206005c26aae7940044dd50009aba1500533501b75c6ae854010ccd5407c08c8004d5d0a801999aa80fbae200135742a004604a6ae84d5d1280111931901619ab9c02d02c02a135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d55cf280089baa00135742a004602a6ae84d5d1280111931900f19ab9c01f01e01c101d13263201d335738921035054350001d135573ca00226ea80044d55ce9baa001135744a00226ae8940044d55cf280089baa0011232230023758002640026aa028446666aae7c004940288cd4024c010d5d080118019aba2002014232323333573466e1cd55cea8012400046644246600200600460186ae854008c014d5d09aba2500223263201433573802a02802426aae7940044dd50009191919191999ab9a3370e6aae75401120002333322221233330010050040030023232323333573466e1cd55cea80124000466442466002006004602a6ae854008cd403c050d5d09aba2500223263201933573803403202e26aae7940044dd50009aba150043335500875ca00e6ae85400cc8c8c8cccd5cd19b875001480108c84888c008010d5d09aab9e500323333573466e1d4009200223212223001004375c6ae84d55cf280211999ab9a3370ea00690001091100191931900d99ab9c01c01b019018017135573aa00226ea8004d5d0a80119a805bae357426ae8940088c98c8054cd5ce00b00a80989aba25001135744a00226aae7940044dd5000899aa800bae75a224464460046eac004c8004d5404488c8cccd55cf80112804119a8039991091980080180118031aab9d5002300535573ca00460086ae8800c0484d5d080088910010910911980080200189119191999ab9a3370ea0029000119091180100198029aba135573ca00646666ae68cdc3a801240044244002464c6402066ae700440400380344d55cea80089baa001232323333573466e1d400520062321222230040053007357426aae79400c8cccd5cd19b875002480108c848888c008014c024d5d09aab9e500423333573466e1d400d20022321222230010053007357426aae7940148cccd5cd19b875004480008c848888c00c014dd71aba135573ca00c464c6402066ae7004404003803403002c4d55cea80089baa001232323333573466e1cd55cea80124000466442466002006004600a6ae854008dd69aba135744a004464c6401866ae700340300284d55cf280089baa0012323333573466e1cd55cea800a400046eb8d5d09aab9e500223263200a33573801601401026ea80048c8c8c8c8c8cccd5cd19b8750014803084888888800c8cccd5cd19b875002480288488888880108cccd5cd19b875003480208cc8848888888cc004024020dd71aba15005375a6ae84d5d1280291999ab9a3370ea00890031199109111111198010048041bae35742a00e6eb8d5d09aba2500723333573466e1d40152004233221222222233006009008300c35742a0126eb8d5d09aba2500923333573466e1d40192002232122222223007008300d357426aae79402c8cccd5cd19b875007480008c848888888c014020c038d5d09aab9e500c23263201333573802802602202001e01c01a01801626aae7540104d55cf280189aab9e5002135573ca00226ea80048c8c8c8c8cccd5cd19b875001480088ccc888488ccc00401401000cdd69aba15004375a6ae85400cdd69aba135744a00646666ae68cdc3a80124000464244600400660106ae84d55cf280311931900619ab9c00d00c00a009135573aa00626ae8940044d55cf280089baa001232323333573466e1d400520022321223001003375c6ae84d55cf280191999ab9a3370ea004900011909118010019bae357426aae7940108c98c8024cd5ce00500480380309aab9d50011375400224464646666ae68cdc3a800a40084244400246666ae68cdc3a8012400446424446006008600c6ae84d55cf280211999ab9a3370ea00690001091100111931900519ab9c00b00a008007006135573aa00226ea80048c8cccd5cd19b8750014800884880088cccd5cd19b8750024800084880048c98c8018cd5ce00380300200189aab9d37540029309000a48103505431001123230010012233003300200200132323232332232323233223232332232323232323232323232332232323222232533500313301c3301249010131003301a33300f3300550015335323500122222222222200450011622153350011002221635004222200235004222200148008cc070cc0492410132003232333573466e2000800407c080d4014888800cccc03ccc0154004c05801006c06ccc0492410133003301a32333355300c12001323350132233350110030010023500e00133501222230033002001200122337000029001000a4000664464600266aa60342400246a00244002646a002444444444444018a008640026aa04844a66a002200644264a66a64646a004446a006446466a00a466a0084a66a666ae68cdc78010008170168a80188169016919a80210169299a999ab9a3371e00400205c05a2a006205a2a66a00642a66a0044266a004466a004466a004466a0044660420040024060466a004406046604200400244406044466a0084060444a66a666ae68cdc38030018198190a99a999ab9a3370e00a00406606426605e0080022064206420562a66a002420562056664424660020060046424460020066aa66a6a014446a0044444444444446666a01a4a0564a0564a0564666aa604424002a04e46a00244a66aa66a666ae68cdc79a801110011a8021100101c01b8999ab9a3370e6a004440026a0084400207006e206e26a05e0062a05c01a426a002446a00244446a0084466a0044606493119aa8198008028981424c44004a0386a006444400826600e00600220026008002a030a03290010998092481013400323235002222222222222533533355301912001501e25335333573466e3c0380040b40b04d40900045408c010840b440acc05c014c04c0084c04800488ccd54c0104800488cd54c024480048d400488cd5408c008cd54c030480048d400488cd54098008ccd40048cc0952000001223302600200123302500148000004cd54c024480048d400488cd5408c008ccd40048cd54c034480048d400488cd5409c008d5403c00400488ccd5540280480080048cd54c034480048d400488cd5409c008d54038004004ccd5540140340080054058d4008888888888888ccd54c0404800488d40088888d401088cd400894cd4ccd5cd19b8f01600103002f133502a00600810082008502200a111222333553004120015015335530071200123500122335502100235500900133355300412001223500222533533355300c120013233501322333500322002002001350012200112330012253350021022100101f235001223300a002005006100313350190040035016001335530071200123500122323355022003300100532001355022225335001135500a003221350022253353300c002008112223300200a004130060030023200135501b2211222533500110022213300500233355300712001005004001112122230030041121222300100432001355018221122533500115013221335014300400233553006120010040013200135501722112225335001135006003221333500900530040023335530071200100500400112350012200112350012200222333573466e3c008004048044888c8c8c004014c8004d5405c88cd400520002235002225335333573466e3c0080240640604c01c0044c01800cc8004d5405888cd400520002235002225335333573466e3c00801c06005c40044c01800c4cd4004894cd40088400c4005401048848cc00400c008894cd400440384cd5ce00100691a80091001090911801001889100091a8009111002190009aa805910891299a8008a80311099a803980200119aa98030900080200088910010910911980080200191199ab9a3370e00400200c00a910100225335002100110031220021220012233700004002464c649319ab9c4901024c67001200111221233001003002112323001001223300330020020011';
@@ -259,7 +987,6 @@ export function useMarketplacePlutus({ blockchainFetcher, network = 0 }) {
assetId: string;
listPriceInLovelace: number;
}) {
- console.log('cancelListing', policyId, assetId, listPriceInLovelace);
const addr = (await wallet.getUsedAddresses())[0];
const datumConstr: Data = {
alternative: 0,
@@ -270,7 +997,7 @@ export function useMarketplacePlutus({ blockchainFetcher, network = 0 }) {
assetId,
],
};
- const redeemer = { data: { alternative: 1, fields: [] } };
+
if (wallet) {
const assetUtxo = await _getAssetUtxo({
scriptAddress: scriptAddress,
@@ -282,8 +1009,7 @@ export function useMarketplacePlutus({ blockchainFetcher, network = 0 }) {
throw 'No listing found.';
}
- // const parameters = await blockchainFetcher.fetchProtocolParameters(118);
- // console.log('parameters', parameters);
+ const redeemer = { data: { alternative: 1, fields: [] } };
const tx = new Transaction({ initiator: wallet })
.redeemValue({
@@ -299,8 +1025,6 @@ export function useMarketplacePlutus({ blockchainFetcher, network = 0 }) {
const signedTx = await wallet.signTx(unsignedTx, true);
const txHash = await wallet.submitTx(signedTx);
- // const txHash = await blockchainFetcher.submitTx(signedTx);
-
return txHash;
}
}
@@ -326,13 +1050,16 @@ export function useMarketplacePlutus({ blockchainFetcher, network = 0 }) {
assetId,
],
};
- const redeemer = { data: { alternative: 0, fields: [] } };
+
if (wallet) {
const assetUtxo = await _getAssetUtxo({
scriptAddress: scriptAddress,
asset: `${policyId}${assetId}`,
datum: datumConstr,
});
+
+ const redeemer = { data: { alternative: 0, fields: [] } };
+
const tx = new Transaction({ initiator: wallet })
.redeemValue({
value: assetUtxo,
@@ -364,7 +1091,6 @@ export function useMarketplacePlutus({ blockchainFetcher, network = 0 }) {
quantity: number | string;
updatedPriceInLovelace: number;
}) {
- console.log('updateListing', policyId, assetId, listPriceInLovelace);
const addr = (await wallet.getUsedAddresses())[0];
const datumConstr: Data = {
alternative: 0,
@@ -385,13 +1111,14 @@ export function useMarketplacePlutus({ blockchainFetcher, network = 0 }) {
],
};
if (wallet) {
- const redeemer = { data: { alternative: 1, fields: [] } };
const assetUtxo = await _getAssetUtxo({
scriptAddress: scriptAddress,
asset: `${policyId}${assetId}`,
datum: datumConstr,
});
+ const redeemer = { data: { alternative: 1, fields: [] } };
+
const tx = new Transaction({ initiator: wallet })
.redeemValue({
value: assetUtxo,