Skip to content

Commit

Permalink
Merge pull request #305 from MeshJS/metadata
Browse files Browse the repository at this point in the history
Mask Metadata
  • Loading branch information
jinglescode authored Oct 4, 2024
2 parents 39c980b + c1c1da1 commit 733e52d
Show file tree
Hide file tree
Showing 5 changed files with 351 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const ReactPage: NextPage = () => {
{ label: "Mint with Plutus Script", to: "mintingPlutusScript" },
{ label: "Mint with CIP-68", to: "mintingCip68" },
{ label: "Mint Royalty Token", to: "mintingRoyaltyToken" },
{ label: "Mask metadata", to: "maskMetadata" },
];

return (
Expand Down
186 changes: 186 additions & 0 deletions apps/playground/src/pages/apis/transaction/minting/mask-metadata.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
import {
AssetMetadata,
deserializeAddress,
ForgeScript,
Mint,
NativeScript,
Transaction,
} from "@meshsdk/core";
import { useWallet } from "@meshsdk/react";

import Link from "~/components/link";
import LiveCodeDemo from "~/components/sections/live-code-demo";
import TwoColumnsScroll from "~/components/sections/two-columns-scroll";
import Codeblock from "~/components/text/codeblock";
import { demoAddresses, demoAssetMetadata } from "~/data/cardano";

export default function MintingNativeScript() {
return (
<TwoColumnsScroll
sidebarTo="maskMetadata"
title="Mask asset metadata"
leftSection={Left()}
rightSection={Right()}
/>
);
}

function Left() {
let codeSnippetNative = ``;
codeSnippetNative += `import type { NativeScript } from '@meshsdk/core';\n`;
codeSnippetNative += `\n`;

codeSnippetNative += `const usedAddress = await wallet.getUsedAddresses();\n`;
codeSnippetNative += `const address = usedAddress[0];\n`;
codeSnippetNative += `\n`;
codeSnippetNative += `const { pubKeyHash: keyHash } = deserializeAddress(address);\n\n`;

codeSnippetNative += `const nativeScript: NativeScript = {\n`;
codeSnippetNative += ` type: "all",\n`;
codeSnippetNative += ` scripts: [\n`;
codeSnippetNative += ` {\n`;
codeSnippetNative += ` type: "before",\n`;
codeSnippetNative += ` slot: "99999999",\n`;
codeSnippetNative += ` },\n`;
codeSnippetNative += ` {\n`;
codeSnippetNative += ` type: "sig",\n`;
codeSnippetNative += ` keyHash: keyHash,\n`;
codeSnippetNative += ` },\n`;
codeSnippetNative += ` ],\n`;
codeSnippetNative += `};\n`;

let codeSnippet1 = `const forgingScript = ForgeScript.fromNativeScript(nativeScript);\n`;

let codeSnippet2 = `const assetMetadata: AssetMetadata = ${JSON.stringify(
demoAssetMetadata,
null,
2,
)};\n\n`;
codeSnippet2 += `const asset: Mint = {\n`;
codeSnippet2 += ` assetName: 'MeshToken',\n`;
codeSnippet2 += ` assetQuantity: '1',\n`;
codeSnippet2 += ` metadata: assetMetadata,\n`;
codeSnippet2 += ` label: '721',\n`;
codeSnippet2 += ` recipient: '${demoAddresses.testnet}' \n`;
codeSnippet2 += `};\n`;

let codeSnippet3 = `const tx = new Transaction({ initiator: wallet });\n`;
codeSnippet3 += `tx.mintAsset(\n`;
codeSnippet3 += ` forgingScript,\n`;
codeSnippet3 += ` asset,\n`;
codeSnippet3 += `);\n\n`;
codeSnippet3 += `const unsignedTx = await tx.build();\n`;
codeSnippet3 += `const signedTx = await wallet.signTx(unsignedTx);\n`;
codeSnippet3 += `const txHash = await wallet.submitTx(signedTx);\n`;

return (
<>
<p>
Additionally, you can define the forging script with{" "}
<code>NativeScript</code>. For example if you want to have a policy
locking script, you can create a new <code>ForgeScript</code> from{" "}
<code>NativeScript</code>:
</p>
<Codeblock data={codeSnippetNative} />
<Codeblock data={codeSnippet1} />
<p>
To get the <code>keyHash</code>, use the{" "}
<code>deserializeAddress()</code>. To get the slot, use the{" "}
<code>resolveSlotNo()</code>. Check out{" "}
<Link href="/apis/resolvers">Resolvers</Link> on how to use these
functions.
</p>
<p>
Important: if you are using a policy locking script, you must define{" "}
<code>setTimeToExpire</code> before the expiry; otherwise, you will
catch the <code>ScriptWitnessNotValidatingUTXOW</code> error. See{" "}
<Link href="/apis/transaction#setTime">Transaction - set time</Link>.
</p>

<p>
Next, we define the metadata for the asset and create the asset object:
</p>

<Codeblock data={codeSnippet2} />

<p>
Finally, we create a transaction and mint the asset with the{" "}
<code>mintAsset</code> method:
</p>
<Codeblock data={codeSnippet3} />

<p>
You can get the policy ID for this Native Script with{" "}
<code>resolveNativeScriptHash</code>:
</p>
<Codeblock
data={`const policyId = resolveNativeScriptHash(nativeScript);`}
/>
</>
);
}

function Right() {
const { wallet, connected } = useWallet();

async function runDemo() {
const usedAddress = await wallet.getUsedAddresses();
const address = usedAddress[0];

if (address === undefined) {
throw "Address not found";
}

const { pubKeyHash: keyHash } = deserializeAddress(address);

const nativeScript: NativeScript = {
type: "all",
scripts: [
{
type: "before",
slot: "99999999",
},
{
type: "sig",
keyHash: keyHash,
},
],
};

const forgingScript = ForgeScript.fromNativeScript(nativeScript);

// define asset metadata
const assetMetadata1: AssetMetadata = demoAssetMetadata;

const asset1: Mint = {
assetName: "MeshToken",
assetQuantity: "1",
metadata: assetMetadata1,
label: "721",
recipient: address,
};

const tx = new Transaction({ initiator: wallet }).setNetwork("preprod");
tx.mintAsset(forgingScript, asset1);
tx.setTimeToExpire("99999999");

const unsignedTx = await tx.build();
const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);

return txHash;
}

return (
<LiveCodeDemo
title="Mint Assets with Native Script"
subtitle="Mint native assets with Native Script"
runCodeFunction={runDemo}
disabled={!connected}
runDemoButtonTooltip={
!connected ? "Connect wallet to run this demo" : undefined
}
runDemoShowBrowseWalletConnect={true}
></LiveCodeDemo>
);
}
48 changes: 34 additions & 14 deletions apps/playground/src/pages/guides/multisig-minting/demo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,19 @@ import { demoAddresses, demoAssetMetadata, demoMnemonic } from "~/data/cardano";
const walletSystemMnemonic = demoMnemonic;
const walletAccountAddress = demoAddresses.testnetPayment;
const mintingFee = "10000000";
let originalMetadata = "";

const blockchainProvider = getProvider();

const systemWallet = new MeshWallet({
networkId: 0,
fetcher: blockchainProvider,
submitter: blockchainProvider,
key: {
type: "mnemonic",
words: walletSystemMnemonic,
},
});

export default function Demo() {
const { wallet, connected } = useWallet();
Expand All @@ -46,26 +59,20 @@ export default function Demo() {

// backend to build tx
const unsignedTx = await backendBuildTx(selectedUtxos, recipientAddress);

// user sign
const signedTx = await wallet.signTx(unsignedTx, true);
const txHash = await wallet.submitTx(signedTx);

// backend sign and submit
const backendSignedTx = await backendSignTx(signedTx);

const txHash = await wallet.submitTx(backendSignedTx);

setResponse(txHash);
setLoading(false);
}

async function backendBuildTx(userUtxos: UTxO[], recipientAddress: string) {
const blockchainProvider = getProvider();

const systemWallet = new MeshWallet({
networkId: 0,
fetcher: blockchainProvider,
submitter: blockchainProvider,
key: {
type: "mnemonic",
words: walletSystemMnemonic,
},
});

const systemWalletAddress = systemWallet.getChangeAddress();
const forgingScript = ForgeScript.withOneSignature(systemWalletAddress);
const assetName = "MeshToken";
Expand All @@ -85,8 +92,21 @@ export default function Demo() {
tx.setChangeAddress(recipientAddress);
const unsignedTx = await tx.build();

const meshWalletSignedTx = await systemWallet.signTx(unsignedTx, true);
originalMetadata = Transaction.readMetadata(unsignedTx);

const maskedTx = Transaction.maskMetadata(unsignedTx);
return maskedTx;
}

async function backendSignTx(unsignedTx: string) {
const signedOriginalTx = Transaction.writeMetadata(
unsignedTx,
originalMetadata,
);
const meshWalletSignedTx = await systemWallet.signTx(
signedOriginalTx,
true,
);
return meshWalletSignedTx;
}

Expand Down
42 changes: 36 additions & 6 deletions apps/playground/src/pages/guides/multisig-minting/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ In this guide, we will build a multi-sig transaction for minting. There are 2 wa
1. client wallet belonging to the user who wishes to buy a native asset
2. application wallet that holds the forging script

## See it in action

In this guide, we will connect to user's CIP30 wallet (`BrowserWallet`) to request for a minting transaction. Then, the backend application wallet (`MeshWallet`) will build the transaction, and we will sign it with our wallet.
In this guide, we will connect to user's CIP30 wallet (`BrowserWallet`) to request for a minting transaction. Then, the backend application wallet (`MeshWallet`) will build the transaction, and we will sign it with our wallet. [Check out the code here](https://github.com/MeshJS/mesh/blob/main/apps/playground/src/pages/guides/multisig-minting/demo.tsx).

## Connect wallet (client)

Expand Down Expand Up @@ -140,21 +138,53 @@ tx.setChangeAddress(recipientAddress);
const unsignedTx = await tx.build();
```

The backend will sign the transaction with the application wallet:
Optionally, we can mask the metadata so users do not see the NFT metadata:

```
const meshWalletSignedTx = await systemWallet.signTx(unsignedTx, true);
originalMetadata = Transaction.readMetadata(unsignedTx);
```

And send the signed transaction to the client for their signature.
This `originalMetadata` can be stored in a database to merge with the original transaction after user signature.

Send the transaction to the client for their signature.

## Sign transaction (client)

In this section, we need the client's signature to send the payment to the `bankWalletAddress`. The client's wallet will open and prompts for payment password. Note that the partial sign is set to `true`.

```
const signedTx = await wallet.signTx(unsignedTx, true);
```

## Sign transaction (application)

The backend will sign the transaction with the application wallet:

```
const meshWalletSignedTx = await systemWallet.signTx(unsignedTx, true);
```

If you have masked the metadata, you can merge the metadata with the signed transaction:

```
const signedOriginalTx = Transaction.writeMetadata(
unsignedTx,
originalMetadata,
);
const meshWalletSignedTx = await systemWallet.signTx(
signedOriginalTx,
true,
);
```

## Submit transaction (application)

Finally, we submit the transaction to the blockchain:

```
const txHash = await wallet.submitTx(signedTx);
```

Voila! You can build any multi-sig transactions!

[Check out the code here](https://github.com/MeshJS/mesh/blob/main/apps/playground/src/pages/guides/multisig-minting/demo.tsx).
Loading

0 comments on commit 733e52d

Please sign in to comment.