Bläddra i källkod

Merge branch 'main' into tony/bubblegum-cost-fix

Tony Boyle 1 år sedan
förälder
incheckning
c16a6a22e6
53 ändrade filer med 3690 tillägg och 143 borttagningar
  1. 2 0
      .vscode/ltex.dictionary.en-US.txt
  2. 8 0
      .vscode/settings.json
  3. 12 1
      markdoc/tags.js
  4. 7 9
      postcss.config.js
  5. BIN
      public/images/metaplex-next-js-template.png
  6. 3 4
      src/components/Fence.jsx
  7. 1 1
      src/components/Hero.jsx
  8. 9 0
      src/components/Image.jsx
  9. 0 2
      src/components/helperComponents/packagesUsed.jsx
  10. 1 2
      src/components/products/Grid.jsx
  11. 0 1
      src/components/products/MobileAppGrid.jsx
  12. 0 1
      src/components/products/SwitcherPopover.jsx
  13. 2 5
      src/components/products/amman/index.js
  14. 6 4
      src/components/products/bubblegum/index.js
  15. 66 15
      src/components/products/coreCandyMachine/index.js
  16. 43 4
      src/components/products/guides/index.js
  17. 19 9
      src/components/products/mpl-hybrid/Hero.jsx
  18. 57 6
      src/components/products/mpl-hybrid/index.js
  19. 0 1
      src/pages/_app.jsx
  20. 2 0
      src/pages/bubblegum/guides/index.md
  21. 9 9
      src/pages/bubblegum/guides/javascript/how-to-create-1000000-nfts-on-solana.md
  22. 150 0
      src/pages/bubblegum/guides/javascript/how-to-interact-with-cnfts-on-other-svms.md
  23. 2 2
      src/pages/candy-machine/sugar/commands/freeze.md
  24. 1 1
      src/pages/core-candy-machine/create.md
  25. 17 0
      src/pages/core-candy-machine/guards/allocation.md
  26. 27 1
      src/pages/core-candy-machine/guards/allow-list.md
  27. 21 0
      src/pages/core-candy-machine/guards/asset-mint-limit.md
  28. 21 1
      src/pages/core-candy-machine/guards/mint-limit.md
  29. 21 0
      src/pages/core-candy-machine/guards/nft-mint-limit.md
  30. 789 0
      src/pages/core-candy-machine/guides/create-a-core-candy-machine-ui.md
  31. 8 0
      src/pages/core-candy-machine/guides/index.md
  32. 274 0
      src/pages/guides/general/create-deterministic-metadata-with-turbo.md
  33. 247 0
      src/pages/guides/javascript/how-to-add-metadata-to-spl-tokens.md
  34. 1 1
      src/pages/guides/javascript/how-to-create-a-solana-token.md
  35. 236 0
      src/pages/guides/setup-a-local-validator.md
  36. 266 0
      src/pages/guides/templates/metaplex-nextjs-tailwind-template.md
  37. 221 0
      src/pages/mpl-hybrid/create-escrow.md
  38. 51 0
      src/pages/mpl-hybrid/fetch-escrow.md
  39. 46 0
      src/pages/mpl-hybrid/funding-escrow.md
  40. 0 13
      src/pages/mpl-hybrid/getting-started/index.md
  41. 0 35
      src/pages/mpl-hybrid/getting-started/js.md
  42. 592 0
      src/pages/mpl-hybrid/guides/create-your-first-hybrid-collection.md
  43. 13 0
      src/pages/mpl-hybrid/guides/index.md
  44. 13 0
      src/pages/mpl-hybrid/sdk/index.md
  45. 42 0
      src/pages/mpl-hybrid/sdk/javascript.md
  46. 28 0
      src/pages/mpl-hybrid/swapping-nfts-to-tokens.md
  47. 28 0
      src/pages/mpl-hybrid/swapping-tokens-to-nfts.md
  48. 153 0
      src/pages/mpl-hybrid/update-escrow.md
  49. 3 2
      src/pages/stability-index.md
  50. 111 0
      src/pages/token-metadata/faq.md
  51. 54 12
      src/pages/token-metadata/guides/account-size-reduction.md
  52. 6 0
      src/pages/umi/transactions.md
  53. 1 1
      src/styles/scrollbar.css

+ 2 - 0
.vscode/ltex.dictionary.en-US.txt

@@ -0,0 +1,2 @@
+Umi
+Solana

+ 8 - 0
.vscode/settings.json

@@ -3,6 +3,7 @@
       "Arweave",
       "bincode",
       "blockhash",
+      "Blocktime",
       "Bundlr",
       "callout",
       "composability",
@@ -10,15 +11,21 @@
       "Devnet",
       "disciminant",
       "eddsa",
+      "gamification",
       "hashlist",
       "Hookable",
       "Irys",
       "Keypair",
+      "Kinobi",
       "lamports",
       "Localnet",
+      "Memecoin",
+      "memecoins",
       "Merkle",
       "metadata",
       "metaplex",
+      "Meteora",
+      "mintable",
       "msgpack",
       "nonblocking",
       "offchain",
@@ -27,6 +34,7 @@
       "println",
       "Pubkey",
       "publickey",
+      "Raydium",
       "rpcs",
       "seperator",
       "serde",

+ 12 - 1
markdoc/tags.js

@@ -16,6 +16,8 @@ import {
 import { PackagesUsed } from '@/components/helperComponents/packagesUsed'
 import { MarkdocGrid as ProductGrid } from '@/components/products/Grid'
 import { MarkdocGrid as AllProductsGrid } from '@/components/products/GridAllProducts'
+import Image from '@/components/Image'
+
 
 const tags = {
   callout: {
@@ -151,7 +153,16 @@ const tags = {
       packages: { type: Array },
       type: { type: String },
     },
-  }
+  },
+  image: {
+    render: Image,
+    attributes: {
+      src: { type: String },
+      alt: { type: String },
+      classes: { type: String },
+    },
+    selfClosing: true,
+  },
 }
 
 export default tags

+ 7 - 9
postcss.config.js

@@ -1,10 +1,8 @@
 module.exports = {
-  plugins: {
-    'postcss-import': {},
-    tailwindcss: {},
-    'postcss-focus-visible': {
-      replaceWith: '[data-focus-visible-added]',
-    },
-    autoprefixer: {},
-  },
-}
+  plugins: [
+      'postcss-import',
+      'tailwindcss/nesting',
+      'tailwindcss',
+      'autoprefixer',
+  ]
+}

BIN
public/images/metaplex-next-js-template.png


+ 3 - 4
src/components/Fence.jsx

@@ -1,6 +1,5 @@
-import { Fragment } from 'react'
 import Highlight, { defaultProps } from 'prism-react-renderer'
-import { ClipboardIcon } from '@heroicons/react/24/outline'
+import { Fragment } from 'react'
 import CopyToClipboardButton from './products/CopyToClipboard'
 
 export function Fence({ children, language }) {
@@ -12,9 +11,9 @@ export function Fence({ children, language }) {
       theme={undefined}
     >
       {({ className, style, tokens, getTokenProps }) => (
-        <pre className={className + ' relative'} style={style}>
+        <pre className={className + ' relative scrollbar '} style={style}>
           <CopyToClipboardButton text={children} />
-          <code>
+          <code className='w-[calc(100%-25px)] block overflow-auto '>
             {tokens.map((line, lineIndex) => (
               <Fragment key={lineIndex}>
                 {line

+ 1 - 1
src/components/Hero.jsx

@@ -66,7 +66,7 @@ export function Hero({
                   <Button href={primaryCta.href}>{primaryCta.title}</Button>
                 }
                 {secondaryCta &&
-                  <Button href={secondaryCta.href} variant="secondary">
+                  <Button href={secondaryCta.href} target="_blank" variant="secondary">
                     {secondaryCta.title}
                   </Button>
                 }

+ 9 - 0
src/components/Image.jsx

@@ -0,0 +1,9 @@
+const Image = ({ src, alt, classes }) => {
+  return (
+    <div className="image">
+      <img className={classes} src={src} />
+    </div>
+  )
+}
+
+export default Image

+ 0 - 2
src/components/helperComponents/packagesUsed.jsx

@@ -2,7 +2,6 @@ import Link from 'next/link.js'
 import { installationPackages } from './packages.js'
 
 export const PackagesUsed = ({ packages, type }) => {
-  console.log({ installationPackages })
 
   const url = (installationPackage) => {
     if (type === 'npm') {
@@ -10,7 +9,6 @@ export const PackagesUsed = ({ packages, type }) => {
         installationPackages[installationPackage] &&
         installationPackages[installationPackage].npm
       ) {
-        console.log(installationPackage)
         return `https://www.npmjs.com/package/${installationPackages[installationPackage].npm}`
       }
       return `https://www.npmjs.com/package/${installationPackage}`

+ 1 - 2
src/components/products/Grid.jsx

@@ -9,13 +9,12 @@ export function Grid({
   numCols = 'grid-cols-3',
   ...props
 }) {
-  console.log('menuItem', menuItem)
+  
   const products = allProducts.filter(
     (product) => menuItem === product.navigationMenuCatergory
   )
 
   let className = `relative grid ${numCols} gap-3`
-  console.log(className)
 
   return (
     <ul className={className} {...props}>

+ 0 - 1
src/components/products/MobileAppGrid.jsx

@@ -11,7 +11,6 @@ export function MobileAppGrid({
   menuItem,
   ...props
 }) {
-  console.log('menuItem', menuItem)
   const products = allProducts
 
   const hub = products.find((product) => product.name === 'Metaplex')

+ 0 - 1
src/components/products/SwitcherPopover.jsx

@@ -2,7 +2,6 @@ import { Grid } from '@/components/products/Grid'
 import { Popover, Transition } from '@headlessui/react'
 
 export function SwitcherPopover({ children, menuItem, ...props }) {
-  console.log('menuItem', menuItem)
   return (
     <Popover {...props}>
       {children}

+ 2 - 5
src/components/products/amman/index.js

@@ -1,11 +1,8 @@
 import {
-  changelogSection,
-  documentationSection,
-  recipesSection,
-  referencesSection,
+  documentationSection
 } from '@/shared/sections'
-import { Hero } from './Hero'
 import { ServerIcon } from '@heroicons/react/24/solid'
+import { Hero } from './Hero'
 
 export const amman = {
   name: 'Amman',

+ 6 - 4
src/components/products/bubblegum/index.js

@@ -1,9 +1,7 @@
 import {
-  changelogSection,
   documentationSection,
   guidesSection,
-  recipesSection,
-  referencesSection,
+  referencesSection
 } from '@/shared/sections'
 import { ArchiveBoxIcon } from '@heroicons/react/24/solid'
 import { Hero } from './Hero'
@@ -87,7 +85,11 @@ export const bubblegum = {
           links: [
             {
               title: 'How to Create a 1,000,000 NFT Collection on Solana',
-              href: 'guides/javascript/how-to-create-1000000-nfts-on-solana',
+              href: '/bubblegum/guides/javascript/how-to-create-1000000-nfts-on-solana',
+            },
+            {
+              title: 'How to Interact with cNFTs on Other SVMs',
+              href: '/bubblegum/guides/javascript/how-to-interact-with-cnfts-on-other-svms',
             },
           ],
         },

+ 66 - 15
src/components/products/coreCandyMachine/index.js

@@ -1,6 +1,10 @@
-import { documentationSection, referencesSection } from '@/shared/sections'
-import { Hero } from './Hero'
-import { Square3Stack3DIcon } from '@heroicons/react/24/solid'
+import {
+  documentationSection,
+  guidesSection,
+  referencesSection,
+} from '@/shared/sections';
+import { Square3Stack3DIcon } from '@heroicons/react/24/solid';
+import { Hero } from './Hero';
 
 export const coreCandyMachine = {
   name: 'Core Candy Machine',
@@ -44,7 +48,10 @@ export const coreCandyMachine = {
               title: 'Creating a Candy Machine',
               href: '/core-candy-machine/create',
             },
-            { title: 'Inserting Items', href: '/core-candy-machine/insert-items' },
+            {
+              title: 'Inserting Items',
+              href: '/core-candy-machine/insert-items',
+            },
             {
               title: 'Updating a Candy Machine and Guards',
               href: '/core-candy-machine/update',
@@ -78,8 +85,14 @@ export const coreCandyMachine = {
               title: 'Address Gate',
               href: '/core-candy-machine/guards/address-gate',
             },
-            { title: 'Allocation', href: '/core-candy-machine/guards/allocation' },
-            { title: 'Allow List', href: '/core-candy-machine/guards/allow-list' },
+            {
+              title: 'Allocation',
+              href: '/core-candy-machine/guards/allocation',
+            },
+            {
+              title: 'Allow List',
+              href: '/core-candy-machine/guards/allow-list',
+            },
             {
               title: 'Asset Burn',
               href: '/core-candy-machine/guards/asset-burn',
@@ -100,7 +113,10 @@ export const coreCandyMachine = {
               title: 'Asset Payment Multi',
               href: '/core-candy-machine/guards/asset-payment-multi',
             },
-            { title: 'Asset Mint Limit', href: '/core-candy-machine/guards/asset-mint-limit' },
+            {
+              title: 'Asset Mint Limit',
+              href: '/core-candy-machine/guards/asset-mint-limit',
+            },
             { title: 'Bot Tax', href: '/core-candy-machine/guards/bot-tax' },
             { title: 'End Date', href: '/core-candy-machine/guards/end-date' },
             { title: 'Edition', href: '/core-candy-machine/guards/edition' },
@@ -112,11 +128,20 @@ export const coreCandyMachine = {
               title: 'Freeze Token Payment',
               href: '/core-candy-machine/guards/freeze-token-payment',
             },
-            { title: 'Gatekeeper', href: '/core-candy-machine/guards/gatekeeper' },
-            { title: 'Mint Limit', href: '/core-candy-machine/guards/mint-limit' },
+            {
+              title: 'Gatekeeper',
+              href: '/core-candy-machine/guards/gatekeeper',
+            },
+            {
+              title: 'Mint Limit',
+              href: '/core-candy-machine/guards/mint-limit',
+            },
             { title: 'NFT Burn', href: '/core-candy-machine/guards/nft-burn' },
             { title: 'NFT Gate', href: '/core-candy-machine/guards/nft-gate' },
-            { title: 'NFT Mint Limit', href: '/core-candy-machine/guards/nft-mint-limit' },
+            {
+              title: 'NFT Mint Limit',
+              href: '/core-candy-machine/guards/nft-mint-limit',
+            },
             {
               title: 'NFT Payment',
               href: '/core-candy-machine/guards/nft-payment',
@@ -137,13 +162,22 @@ export const coreCandyMachine = {
               title: 'Sol Payment',
               href: '/core-candy-machine/guards/sol-payment',
             },
-            { title: 'Start Date', href: '/core-candy-machine/guards/start-date' },
+            {
+              title: 'Start Date',
+              href: '/core-candy-machine/guards/start-date',
+            },
             {
               title: 'Third Party Signer',
               href: '/core-candy-machine/guards/third-party-signer',
             },
-            { title: 'Token Burn', href: '/core-candy-machine/guards/token-burn' },
-            { title: 'Token Gate', href: '/core-candy-machine/guards/token-gate' },
+            {
+              title: 'Token Burn',
+              href: '/core-candy-machine/guards/token-burn',
+            },
+            {
+              title: 'Token Gate',
+              href: '/core-candy-machine/guards/token-gate',
+            },
             {
               title: 'Token Payment',
               href: '/core-candy-machine/guards/token-payment',
@@ -169,6 +203,24 @@ export const coreCandyMachine = {
         },
       ],
     },
+    {
+      ...guidesSection('core-candy-machine'),
+      navigation: [
+        {
+          title: 'General',
+          links: [
+            { 
+              title: 'Overview', 
+              href: '/core-candy-machine/guides' 
+            },
+            {
+              title: 'Create a Website for minting Assets from your Core Candy Machine',
+              href: '/core-candy-machine/guides/create-a-core-candy-machine-ui',
+            },
+          ],
+        },
+      ],
+    },
     // {
     //   id: 'sugar',
     //   title: 'Sugar',
@@ -249,12 +301,11 @@ export const coreCandyMachine = {
     //   ],
     // },
 
-
     {
       ...referencesSection('core-candy-machine'),
       href: `https://mpl-core-candy-machine.typedoc.metaplex.com/`,
       title: 'Javascript API References',
-      icon: "JavaScript",
+      icon: 'JavaScript',
       target: '_blank',
     },
     {

+ 43 - 4
src/components/products/guides/index.js

@@ -1,5 +1,5 @@
-import { documentationSection } from '@/shared/sections'
-import { BookOpenIcon } from '@heroicons/react/24/outline'
+import { documentationSection } from '@/shared/sections';
+import { BookOpenIcon } from '@heroicons/react/24/outline';
 
 export const guides = {
   name: 'Guides',
@@ -48,6 +48,12 @@ export const guides = {
               created: '2021-10-01',
               updated: null, // null means it's never been updated
             },
+            {
+              title: 'Setup a Local Validator',
+              href: '/guides/setup-a-local-validator',
+              created: '2021-11-6',
+              updated: null, // null means it's never been updated
+            },
             {
               title: 'How to Diagnose Transaction Errors on Solana',
               href: '/guides/general/how-to-diagnose-solana-transaction-errors',
@@ -56,11 +62,16 @@ export const guides = {
             },
           ],
         },
-        {title: 'General',
+        {
+          title: 'General',
           links: [
             {
               title: 'Creating an NFT Collection With Candy Machine',
               href: '/candy-machine/guides/create-an-nft-collection-on-solana-with-candy-machine',
+            },
+            {
+              title: 'Create deterministic Metadata with Turbo',
+              href: '/guides/general/create-deterministic-metadata-with-turbo',
             }
           ]
         },
@@ -79,6 +90,12 @@ export const guides = {
               created: '2024-06-16',
               updated: null, // null means it's never been updated
             },
+            {
+              title: 'How to Add Metadata to a Solana Token',
+              href: '/guides/javascript/how-to-add-metadata-to-spl-tokens',
+              created: '2024-10-01',
+              updated: null, // null means it's never been updated
+            },
             {
               title: 'Transferring Tokens',
               href: '/guides/javascript/how-to-transfer-spl-tokens-on-solana',
@@ -116,9 +133,26 @@ export const guides = {
             },
           ],
         },
+        {
+          title: 'Templates',
+          links: [
+            {
+              title: 'NextJs Template',
+              href: '/guides/templates/metaplex-nextjs-tailwind-template',
+              created: '2024-09-04',
+              updated: null, // null means it's never been updated
+            },
+          ],
+        },
         {
           title: 'Metaplex Program Guides',
           links: [
+            {
+              title: 'Bubblegum',
+              href: '/bubblegum/guides/',
+              created: '2024-11-13',
+              updated: null, // null means it's never been updated
+            },
             {
               title: 'Core',
               href: '/core/guides/',
@@ -131,7 +165,12 @@ export const guides = {
               created: '2021-10-01',
               updated: null, // null means it's never been updated
             },
-
+            {
+              title: 'Core Candy Machine',
+              href: '/core-candy-machine/guides/',
+              created: '2024-09-20',
+              updated: null, // null means it's never been updated
+            },
           ],
         },
         {

+ 19 - 9
src/components/products/mpl-hybrid/Hero.jsx

@@ -1,16 +1,26 @@
 import { Hero as BaseHero } from '@/components/Hero'
-import Link from 'next/link'
+import { HeroCode } from '@/components/HeroCode'
+
+const codeProps = {
+  tabs: [
+    { name: 'captureV1.js', isActive: true },
+  ],
+  language: 'javascript',
+  code: `const captureTx = await captureV1(umi, {
+    owner: umi.identity,
+    escrow: escrowAddress,
+    asset: escrowAssets[0].publicKey,
+    collection: COLLECTION,
+    feeProjectAccount: FEE_WALLET,
+    token: TOKEN,
+  }).sendAndConfirm(umi);`,
+}
 
 export function Hero({ page }) {
   return (
-    <BaseHero page={page} light2Off light3Off primaryCta={''}>
-      <Link href="/mpl-hybrid">
-        <img
-          src="https://pbs.twimg.com/media/GOsXQI-bQAAecgo?format=png&name=large"
-          alt="Introducing MPL-404"
-          className="no-lightense rounded rounded-xl"
-        />
-      </Link>
+    <BaseHero page={page}>
+      <HeroCode {...codeProps}></HeroCode>
     </BaseHero>
   )
 }
+

+ 57 - 6
src/components/products/mpl-hybrid/index.js

@@ -1,4 +1,8 @@
-import { documentationSection } from '@/shared/sections'
+import {
+  documentationSection,
+  guidesSection,
+  referencesSection,
+} from '@/shared/sections'
 import { ArrowsRightLeftIcon } from '@heroicons/react/24/solid'
 import { Hero } from './Hero'
 
@@ -9,8 +13,8 @@ export const mplHybrid = {
   navigationMenuCatergory: 'Create',
   path: 'mpl-hybrid',
   icon: <ArrowsRightLeftIcon />,
-  github: '',
-  className: 'accent-amber',
+  github: 'https://github.com/metaplex-foundation/mpl-hybrid',
+  className: 'accent-green',
   heroes: [{ path: '/mpl-hybrid', component: Hero }],
   sections: [
     {
@@ -20,16 +24,63 @@ export const mplHybrid = {
           title: 'Introduction',
           links: [
             { title: 'Overview', href: '/mpl-hybrid' },
-            { title: 'Getting Started', href: '/mpl-hybrid/getting-started' },
             { title: 'Preparation', href: '/mpl-hybrid/preparation' },
             { title: 'FAQ', href: '/mpl-hybrid/faq' },
           ],
         },
+        {
+          title: 'SDK',
+          links: [
+            { title: 'Javascript SDK', href: '/mpl-hybrid/sdk/javascript' },
+          ],
+        },
         {
           title: 'Features',
           links: [
-            { title: 'Escrow', href: '/mpl-hybrid/escrow' },
-            { title: 'Swapping', href: '/mpl-hybrid/swapping' },
+            {
+              title: 'Create Escrow Configuration',
+              href: '/mpl-hybrid/create-escrow',
+            },
+            {
+              title: 'Fetch Escrow Configuration',
+              href: '/mpl-hybrid/fetch-escrow',
+            },
+            { title: 'Funding Escrow', href: '/mpl-hybrid/funding-escrow' },
+            {
+              title: 'Updating Escrow Configuration',
+              href: '/mpl-hybrid/update-escrow',
+            },
+            {
+              title: 'Swapping NFTs to Tokens',
+              href: '/mpl-hybrid/swapping-nfts-to-tokens',
+            },
+            {
+              title: 'Swapping Tokens to NFTs',
+              href: '/mpl-hybrid/swapping-tokens-to-nfts',
+            },
+          ],
+        },
+      ],
+    },
+    {
+      ...referencesSection('mpl-hybrid'),
+      href: 'https://mpl-hybrid.typedoc.metaplex.com/',
+      target: '_blank',
+    },
+    {
+      ...guidesSection('mpl-hybrid'),
+      navigation: [
+        {
+          title: 'General',
+          links: [
+            {
+              title: 'Overview',
+              href: '/mpl-hybrid/guides',
+            },
+            {
+              title: 'Create your first Hybrid Collection',
+              href: '/mpl-hybrid/guides/create-your-first-hybrid-collection',
+            },
           ],
         },
       ],

+ 0 - 1
src/pages/_app.jsx

@@ -17,7 +17,6 @@ require('prismjs/components/prism-rust')
 
 export default function App({ Component, pageProps }) {
   const page = usePage(pageProps)
-  console.log({ page })
 
   return (
     <>

+ 2 - 0
src/pages/bubblegum/guides/index.md

@@ -10,4 +10,6 @@ The following Guides for MPL Bubblegum are currently available:
 
 {% quick-link title="How to Mint 1 Million cNFTs on Solana" icon="CodeBracketSquare" href="/bubblegum/guides/javascript/how-to-create-1000000-nfts-on-solana" description="Learn how to create and mint cNFTs from Bubblegum trees" /%}
 
+{% quick-link title="How to Interact with cNFTs on Other SVMs" icon="CodeBracketSquare" href="/bubblegum/guides/javascript/how-to-interact-with-cnfts-on-other-svms" description="How to Interact with compressed NFTs, using the Metaplex Bubblegum program, on Solana Virtual Machine (SVM) environments other than Solana devnet and mainnet-beta." /%}
+
 {% /quick-links %}

+ 9 - 9
src/pages/bubblegum/guides/javascript/how-to-create-1000000-nfts-on-solana.md

@@ -12,13 +12,13 @@ description: How to Create a Compressed NFT Collection of 1 Million cNFTs on Sol
 
 ## Initial Setup
 
-This guide will run through create of an NFT Core Asset with Javascript based on a single file script. You may need to modify and move functions around to suit your needs.
+This guide will run through creation of a compressed NFT (cNFT) Asset with Javascript based on a single file script. You may need to modify and move functions around to suit your needs.
 
 ### Initializing
 
 Start by initializing a new project (optional) with the package manager of your choice (npm, yarn, pnpm, bun) and fill in required details when prompted.
 
-```js
+```bash
 npm init
 ```
 
@@ -28,24 +28,24 @@ Install the required packages for this guide.
 
 {% packagesUsed packages=["umi", "umiDefaults", "bubblegum", "tokenMetadata", "@metaplex-foundation/umi-uploader-irys"] type="npm" /%}
 
-```js
+```bash
 npm i @metaplex-foundation/umi
 ```
 
-```js
+```bash
 npm i @metaplex-foundation/umi-bundle-defaults
 ```
 
-```js
+```bash
 npm i @metaplex-foundation/mpl-bubblegum
 ```
 
-```js
+```bash
 npm i @metaplex-foundation/mpl-token-metadata
 ```
 
-```js
-npm i @metaplex-foundation/umi-uploader-irys;
+```bash
+npm i @metaplex-foundation/umi-uploader-irys
 ```
 
 ### Imports and Wrapper Function
@@ -173,7 +173,7 @@ const umi = createUmi('https://rpcAddress.com')
 ### Creating a Tree
 
 {% callout title="Tree Cost" type="warning" %}
-We are creating a Merkle Tree that holds 1,000,000 cNFTs in this guide which requires the cost of roughly 7.7 SOL. Please try this example on devnet only until you are ready as Merkle Trees can not be closed or refunded. You will need at least 7.7 devnet SOL in order to run this code, this may require multiple airdrops.
+We are creating a Merkle Tree that holds 1,000,000 cNFTs in this guide which requires the cost of roughly 7.7 SOL. Until you are ready, please try this example on devnet only, as Merkle Trees can not be closed or refunded. You will need at least 7.7 devnet SOL in order to run this code. This may require multiple airdrops.
 {% /callout %}
 
 To store Compressed NFTs (cNFTs) on the Solana blockchain you need to create a **Merkle Tree** in which to store the data. The size and cost of the merkle tree is determined by the merkle tree creator and all cNFTs storage on chain is paid for in advanced which differs from Token Metadata's approach of **lazy minting** where normally the payer would pay for the necessary storage space and account creation on the solana blockchain at the time of minting the NFT itself, with bubblegum all data space needed is determined and paid for at tree creation by the tree creator.

+ 150 - 0
src/pages/bubblegum/guides/javascript/how-to-interact-with-cnfts-on-other-svms.md

@@ -0,0 +1,150 @@
+---
+title: How to Interact with cNFTs on Other SVMs
+metaTitle: How to Interact with cNFTs on Other SVMs | Bubblegum
+description: How to Interact with compressed NFTs, using the Metaplex Bubblegum program, on Solana Virtual Machine (SVM) environments other than Solana devnet and mainnet-beta.
+---
+
+## Overview
+
+This guide details the specific requirements for interacting with compressed NFT (cNFT) assets using JavaScript on Solana Virtual Machine (SVM) environments other than Solana's devnet and mainnet-beta. For a more comprehensive overview of creating cNFTs, see the [Create 1,000,000 NFTs on Solana with Bubblegum](/bubblegum/guides/javascript/how-to-create-1000000-nfts-on-solana) guide.
+
+### Required Package
+
+This guide makes use of a specific beta npm package for `@metaplex-foundation/mpl-bubblegum`.  Install using:
+
+```bash
+npm -i @metaplex-foundation/mpl-bubblegum@4.3.1-beta.0
+```
+
+### Connecting to the SVM
+
+Note you will need to create your umi instance using the endpoint for the SVM.
+
+```ts
+import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";
+
+const umi = createUmi('<RPC endpoint for the SVM>')
+  .use(mplBubblegum())
+  .use(mplTokenMetadata())
+  ...
+```
+
+### Creating a Tree
+
+{% callout title="Tree Cost" type="warning" %}
+We are creating a Merkle Tree that with a real up-front SOL cost that will vary depending on the tree size and the specific SVM you are using. Until you are ready, please try this example on devnet only, as Merkle Trees can not be closed or refunded.
+{% /callout %}
+
+Creating a tree can be done using the same `createTree` function that is used on Solana devnet/mainnet-beta. However, we must override the default `logWrapper` and `compressionProgram` values. This could be accomplished as simply as:
+
+```ts
+import {
+  createTree,
+  MPL_ACCOUNT_COMPRESSION_PROGRAM_ID,
+  MPL_NOOP_PROGRAM_ID,
+} from '@metaplex-foundation/mpl-bubblegum'
+import {
+  generateSigner,
+  publicKey,
+} from '@metaplex-foundation/umi';
+
+// Create a Merkle tree specifying the correct `logWrapper` and
+// `compressionProgram` for the SVM.
+const merkleTree = generateSigner(umi);
+const createTreeTx = await createTree(umi, {
+  merkleTree,
+  maxDepth: 3,
+  maxBufferSize: 8,
+  canopyDepth: 0,
+  logWrapper: MPL_NOOP_PROGRAM_ID,
+  compressionProgram: MPL_ACCOUNT_COMPRESSION_PROGRAM_ID,
+});
+
+await createTreeTx.sendAndConfirm(umi);
+```
+
+However, a helper function has been provided to automatically resolve these program IDs, and this is the recommended approach as it will work on Solana devnet/mainnet-beta as well as other SVMs to which Bubblegum has been deployed:
+
+```ts
+import {
+  getCompressionPrograms,
+  createTree,
+} from '@metaplex-foundation/mpl-bubblegum'
+import {
+  generateSigner,
+  publicKey,
+} from '@metaplex-foundation/umi';
+
+// Create a Merkle tree using the `getCompressionPrograms` helper function.
+const merkleTree = generateSigner(umi);
+const createTreeTx = await createTree(umi, {
+  merkleTree,
+  maxDepth: 3,
+  maxBufferSize: 8,
+  canopyDepth: 0,
+  ...(await getCompressionPrograms(umi)),
+});
+
+await createTreeTx.sendAndConfirm(umi);
+```
+
+### Mint and Transfer a cNFT
+
+Similarly to creating the Merkle tree on another SVM, other SDK functions such as `mintV1` and `transfer` will also require specifying the compression programs.  Again we use the `getCompressionPrograms` helper.
+
+```ts
+import {
+  fetchMerkleTree,
+  getCurrentRoot,
+  hashMetadataCreators,
+  hashMetadataData,
+  transfer,
+  getCompressionPrograms,
+  createTree,
+  MetadataArgsArgs,
+  mintV1,
+} from '@metaplex-foundation/mpl-bubblegum'
+import {
+  generateSigner,
+  none,
+} from '@metaplex-foundation/umi';
+
+// Get leaf index before minting.
+const leafIndex = Number(
+  (await fetchMerkleTree(umi, merkleTree.publicKey)).tree.activeIndex
+);
+
+// Define Metadata.
+const metadata: MetadataArgsArgs = {
+  name: 'My NFT',
+  uri: 'https://example.com/my-nft.json',
+  sellerFeeBasisPoints: 500, // 5%
+  collection: none(),
+  creators: [],
+};
+
+// Mint a cNFT.
+const originalOwner = generateSigner(umi);
+const mintTxn = await mintV1(umi, {
+  leafOwner: originalOwner.publicKey,
+  merkleTree: merkleTree.publicKey,
+  metadata,
+  ...(await getCompressionPrograms(umi)),
+}).sendAndConfirm(umi);
+
+// Transfer the cNFT to a new owner.
+const newOwner = generateSigner(umi);
+const merkleTreeAccount = await fetchMerkleTree(umi, merkleTree.publicKey);
+const transferTxn = await transfer(umi, {
+  leafOwner: originalOwner,
+  newLeafOwner: newOwner.publicKey,
+  merkleTree: merkleTree.publicKey,
+  root: getCurrentRoot(merkleTreeAccount.tree),
+  dataHash: hashMetadataData(metadata),
+  creatorHash: hashMetadataCreators(metadata.creators),
+  nonce: leafIndex,
+  index: leafIndex,
+  proof: [],
+  ...(await getCompressionPrograms(umi)),
+}).sendAndConfirm(umi);
+```

+ 2 - 2
src/pages/candy-machine/sugar/commands/freeze.md

@@ -4,9 +4,9 @@ metaTitle: freeze | Sugar
 description: freeze command.
 ---
 
-When the Candy Machine has the freeze guard enabled, the `freeze` command can be used to manege its different stages.
+When the Candy Machine has the freeze guard enabled, the `freeze` command can be used to manage its different stages.
 
-After enabling the freeze guard on the default guards or an individual group, it needs to be initialized before minting can start. To initialize the freeza guard, use the `initialize` sub-command:
+After enabling the freeze guard on the default guards or an individual group, it needs to be initialized before minting can start. To initialize the freeze guard, use the `initialize` sub-command:
 
 ```
 sugar freeze initialize --period <SECONDS>

+ 1 - 1
src/pages/core-candy-machine/create.md

@@ -387,7 +387,7 @@ const createIx = await create(umi, {
   itemsAvailable: 5000,
   hiddenSettings = {
     name: "Hidden Asset",
-    uri: "https://example.com/hidden-asset.json,
+    uri: "https://example.com/hidden-asset.json",
     hash,
   }
 })

+ 17 - 0
src/pages/core-candy-machine/guards/allocation.md

@@ -256,3 +256,20 @@ _Sugar currently does not support route instructions._
 {% /totem %}
 {% /dialect %}
 {% /dialect-switcher %}
+
+## Allocation Accounts
+When the `Allocation` Guard is used a `allocationTracker` Account is created after the route instruction was run. For validation purposes it can be fetched like this:
+
+```js
+import {
+  safeFetchAllocationTrackerFromSeeds,
+} from "@metaplex-foundation/mpl-core-candy-machine";
+
+const allocationTracker = await safeFetchAllocationTrackerFromSeeds(umi, {
+  id: 1, // The allocation id you set in your guard config
+  candyMachine: candyMachine.publicKey,
+  // or candyMachine: publicKey("Address") with your CM Address
+  candyGuard: candyMachine.mintAuthority,
+  // or candyGuard: publicKey("Address") with your candyGuard Address
+});
+```

+ 27 - 1
src/pages/core-candy-machine/guards/allow-list.md

@@ -381,4 +381,30 @@ _Sugar can not be used to call the "Proof" Route._
 
 {% /totem %}
 {% /dialect %}
-{% /dialect-switcher %}
+{% /dialect-switcher %}
+
+## Allowlist Accounts
+When the `Allowlist` Guard is used a `AllowListProof` Account is created after the route instruction was run. When it can be fetched the user is on the allowlist and the route was run already. For validation purposes it can be fetched like this:
+
+```js
+import {
+  safeFetchAllowListProofFromSeeds,
+  getMerkleRoot,
+} from "@metaplex-foundation/mpl-core-candy-machine";
+
+const allowlist = [
+  "Tes1zkZkXhgTaMFqVgbgvMsVkRJpq4Y6g54SbDBeKVV",
+  "GjwcWFQYzemBtpUoN5fMAP2FZviTtMRWCmrppGuTthJS",
+  "AT8nPwujHAD14cLojTcB1qdBzA1VXnT6LVGuUd6Y73Cy"
+];
+
+const allowListProof = await safeFetchAllowListProofFromSeeds(umi, {
+  candyMachine: candyMachine.publicKey,
+  // or candyMachine: publicKey("Address") with your CM Address
+  candyGuard: candyMachine.mintAuthority,
+  // or candyGuard: publicKey("Address") with your candyGuard Address
+  merkleRoot: getMerkleRoot(allowlist),
+  user: umi.identity.publicKey,
+  // or publicKey of the "minting" account
+});
+```

+ 21 - 0
src/pages/core-candy-machine/guards/asset-mint-limit.md

@@ -137,3 +137,24 @@ mintV1(umi, {
 ## Route Instruction
 
 _The Asset Mint Limit guard does not support the route instruction._
+
+## AssetMintLimit Accounts
+When the `AssetMintLimit` Guard is used a `AssetMintCounter` Account is created for each Core NFT Asset, CandyMachine and `id` combination. For validation purposes it can be fetched like this:
+
+```js
+import { 
+  findAssetMintCounterPda,
+  fetchNftMintCounter
+ } from "@metaplex-foundation/mpl-core-candy-machine";
+
+const pda = findAssetMintCounterPda(umi, {
+  id: 1, // The nftMintLimit id you set in your guard config
+  mint: asset.publicKey, // The address of the nft your user owns
+  candyMachine: candyMachine.publicKey,
+  // or candyMachine: publicKey("Address") with your CM Address
+  candyGuard: candyMachine.mintAuthority
+  // or candyGuard: publicKey("Address") with your candyGuard Address
+});
+      
+const nftMintCounter = fetchAssetMintCounter(umi, pda)
+```

+ 21 - 1
src/pages/core-candy-machine/guards/mint-limit.md

@@ -1,5 +1,5 @@
 ---
-title: Mint Limit Guard"
+title: Mint Limit Guard
 metaTitle: "Mint Limit Guard | Core Candy Machine"
 description: "The Core Candy Machine 'Mint Limit' guard allows specifying a limit on the number of Assets each wallet can mint."
 ---
@@ -134,3 +134,23 @@ mintV1(umi, {
 ## Route Instruction
 
 _The Mint Limit guard does not support the route instruction._
+
+## MintLimit Accounts
+When the `MintLimit` Guard is used a `MintCounter` Account is created for each Wallet, CandyMachine and `id` combination. For validation purposes it can be fetched like this:
+
+```js
+import { safeFetchMintCounterFromSeeds } from "@metaplex-foundation/mpl-core-candy-machine";
+import { umi } from "@metaplex-foundation/mpl-core-candy-machine";
+
+const mintCounter = await safeFetchMintCounterFromSeeds(umi, {
+  id: 1, // The mintLimit id you set in your guard config
+  user: umi.identity.publicKey,
+  candyMachine: candyMachine.publicKey, 
+  // or candyMachine: publicKey("Address") with your CM Address
+  candyGuard: candyMachine.mintAuthority, 
+  // or candyGuard: publicKey("Address") with your candyGuard Address
+});
+
+// Amount already minted
+console.log(mintCounter.count)
+```

+ 21 - 0
src/pages/core-candy-machine/guards/nft-mint-limit.md

@@ -137,3 +137,24 @@ mintV1(umi, {
 ## Route Instruction
 
 _The NFT Mint Limit guard does not support the route instruction._
+
+## NftMintLimit Accounts
+When the `NftMintLimit` Guard is used a `NftMintCounter` Account is created for each NFT, CandyMachine and `id` combination. For validation purposes it can be fetched like this:
+
+```js
+import { 
+  findNftMintCounterPda,
+  fetchNftMintCounter
+ } from "@metaplex-foundation/mpl-core-candy-machine";
+
+const pda = findNftMintCounterPda(umi, {
+  id: 1, // The nftMintLimit id you set in your guard config
+  mint: asset.publicKey, // The address of the nft your user owns
+  candyMachine: candyMachine.publicKey,
+  // or candyMachine: publicKey("Address") with your CM Address
+  candyGuard: candyMachine.mintAuthority
+  // or candyGuard: publicKey("Address") with your candyGuard Address
+});
+      
+const nftMintCounter = fetchNftMintCounter(umi, pda)
+```

+ 789 - 0
src/pages/core-candy-machine/guides/create-a-core-candy-machine-ui.md

@@ -0,0 +1,789 @@
+---
+title: Create a Website for minting Assets from your Core Candy Machine
+metaTitle: Create a Website for minting Assets from your Core Candy Machine | Core Candy Machine
+description: How to create an NFT collection on the Solana blockchain using Candy Machine.
+---
+
+If you are looking to launch a Core NFT Collection on Solana, you would typically use a Candy Machine where your users can come and buy your Assets. To provide a user-friendly experience, it is recommended to have a website for it. This guide will focus on how to build your own mint function. It will also show you how to fetch data from the Candy Machine to, for example, display the remaining amount that can be minted.
+
+This guide focuses on the core Candy Machine functionality and interactions, rather than providing a complete website implementation. It will not cover aspects like adding buttons to a website or integrating with a wallet adapter. Instead, it provides essential information on working with the Core Candy Machine.
+
+For a full website implementation, including UI elements and wallet integration, you may want to start with a template like the [`metaplex-nextjs-tailwind-template`](https://github.com/metaplex-foundation/metaplex-nextjs-tailwind-template). This template includes many setup steps for components like the Wallet Adapter.
+
+If you're looking for guidance on general web development practices or how to use specific frameworks, tools like Visual Studio Code offer extensive documentation and community resources.
+
+## Prerequisites
+
+- An already created Candy Machine. Find more info on how to create one [here](https://developers.metaplex.com/core-candy-machine/create).
+- Basic familiarity with web development and your chosen framework. We recommend Next JS for easiest compatability to umi.
+
+## Required Packages
+
+Regardless of your chosen template or implementation, you'll need to install the following packages for interacting with the Core Candy Machine:
+
+{% packagesUsed packages=["umi", "umiDefaults", "core", "candyMachineCore"] type="npm" /%}
+
+```ts
+npm i @metaplex-foundation/umi @metaplex-foundation/umi-bundle-defaults @metaplex-foundation/mpl-core-candy-machine
+```
+
+## Fetch On-chain Data
+
+After setting up your environment, we can start focusing on the Candy Machine. Mint UIs often want to show data such as:
+
+- Number of already minted Assets
+- Number of Assets in the Candy Machine
+- Time until the mint starts
+- Price of Assets
+- etc.
+
+It can also make sense to fetch additional data that is not shown to the user but used in background calculations. For example, when using the [Redeemed Amount](core-candy-machine/guards/redeemed-amount) Guard, you would want to fetch the already redeemed amount to see if the user is allowed to mint more.
+
+### Fetch Candy Machine Data
+In the Candy Machine Account, data such as the number of Available and Redeemed assets is stored. It also stores the `mintAuthority`, which is usually the address of your Candy Guard.  
+
+To fetch the Candy Machine, the `fetchCandyMachine` function can be used as shown below:
+
+```ts
+import {
+  mplCandyMachine,
+  fetchCandyMachine,
+} from "@metaplex-foundation/mpl-core-candy-machine";
+import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";
+
+// The next two lines are only required if you did not set up umi before
+const umi = createUmi("https://api.devnet.solana.com")
+            .use(mplCandyMachine());
+
+const candyMachineId = "Ct5CWicvmjETYXarcUVJenfz3CCh2hcrCM3CMiB8x3k9";
+const candyMachine = await fetchCandyMachine(umi, publicKey(candyMachineId));
+console.log(candyMachine)
+```
+
+This would return the Candy Machine Data like this:
+
+{% dialect-switcher title="JSON Result" %}
+{% dialect title="JSON" id="json-cm" %}
+
+{% totem-accordion title="Candy Machine Data" %}
+```tson
+{
+    "publicKey": "Ct5CWicvmjETYXarcUVJenfz3CCh2hcrCM3CMiB8x3k9",
+    "header": {
+        "executable": false,
+        "owner": "CMACYFENjoBMHzapRXyo1JZkVS6EtaDDzkjMrmQLvr4J",
+        "lamports": {
+            "basisPoints": "91814160",
+            "identifier": "SOL",
+            "decimals": 9
+        },
+        "rentEpoch": "18446744073709551616",
+        "exists": true
+    },
+    "discriminator": [
+        51,
+        173,
+        177,
+        113,
+        25,
+        241,
+        109,
+        189
+    ],
+    "authority": "Tes1zkZkXhgTaMFqVgbgvMsVkRJpq4Y6g54SbDBeKVV",
+    "mintAuthority": "ACJCHhsWCKw9Euu9nLdyxajqitvmwrXQMRWe2mrmva8u",
+    "collectionMint": "GPHD33NBaM8TgvbfgcxrusD6nyfhNLbeyKjxMRLAr9LM",
+    "itemsRedeemed": "13",
+    "data": {
+        "itemsAvailable": "16",
+        "maxEditionSupply": "0",
+        "isMutable": true,
+        "configLineSettings": {
+            "__option": "Some",
+            "value": {
+                "prefixName": "",
+                "nameLength": 32,
+                "prefixUri": "",
+                "uriLength": 200,
+                "isSequential": false
+            }
+        },
+        "hiddenSettings": {
+            "__option": "None"
+        }
+    },
+    "items": [
+        {
+            "index": 0,
+            "minted": true,
+            "name": "0.json",
+            "uri": ""
+        },
+        [...]
+    ],
+    "itemsLoaded": 16
+}
+```
+{% /totem-accordion  %}
+
+{% /dialect %}
+{% /dialect-switcher %}
+
+From a UI perspective the most important field in here are `itemsRedeemed`, `itemsAvailable` and `mintAuthority`. In some cases it might also be interesting to show some of the `items` on your website as teaser pictures.
+
+#### Show remaining Asset Amount
+To display a section like `13 / 16 Assets minted` one would use something like:
+
+```ts
+const mintedString = `${candyMachine.itemsRedeemed} / ${candyMachine.itemsAvailable} Assets minted`
+```
+
+If you want to get the remaining mintable Assets like `3 available` you would instead run a calculation like:
+
+```ts
+const availableString = `${candyMachine.itemsAvailable - candyMachine.itemsRedeemed} available`;
+```
+
+### Fetch Candy Guard Data
+The Candy Guard contains the conditions that have to be met to allow minting. This can for example be a Sol or Token Payment happening, limiting the amount of Assets one Wallet is allowed to mint and way more. You can find more information about Candy Guards on the [Candy Guard Page](/core-candy-machine/guards).
+
+Similar to the Candy Machine Data it is not a necessity to fetch the guard account. Doing so can allow more flexibility like just updating the SOL price in the Candy Guard and automatically updating the numbers on the website, too. 
+
+If you want to build a more flexible UI that can be used for multiple Candy Machines fetching the Candy Guard then allows you to both build your mint function and check eligibility dynamically.
+
+The following snippet assumes that the `candyMachine` account was fetched before. Alternatively to `candyMachine.mintAuthority` the publicKey of the Candy Guard could be hardcoded.
+
+```ts
+import { safeFetchCandyGuard } from "@metaplex-foundation/mpl-core-candy-machine";
+
+const candyGuard = await safeFetchCandyGuard(umi, candyMachine.mintAuthority);
+```
+
+{% dialect-switcher title="JSON Result" %}
+{% dialect title="JSON" id="json-cg" %}
+
+{% totem-accordion title="Candy Guard Data" %}
+
+{% totem-prose %}
+In this Object the most important field for the UI is the `guards` object. It contains the `default` guards that are always applied. `guards.groups` contains the different [Guard Groups](/core-candy-machine/guard-groups).
+
+{% /totem-prose %}
+
+```json
+{
+    "publicKey": "ACJCHhsWCKw9Euu9nLdyxajqitvmwrXQMRWe2mrmva8u",
+    "header": {
+        "executable": false,
+        "owner": "CMAGAKJ67e9hRZgfC5SFTbZH8MgEmtqazKXjmkaJjWTJ",
+        "lamports": {
+            "basisPoints": "2561280",
+            "identifier": "SOL",
+            "decimals": 9
+        },
+        "rentEpoch": "18446744073709551616",
+        "exists": true
+    },
+    "discriminator": [
+        44,
+        207,
+        199,
+        184,
+        112,
+        103,
+        34,
+        181
+    ],
+    "base": "Ct5CWicvmjETYXarcUVJenfz3CCh2hcrCM3CMiB8x3k9",
+    "bump": 255,
+    "authority": "Tes1zkZkXhgTaMFqVgbgvMsVkRJpq4Y6g54SbDBeKVV",
+    "guards": {
+        "botTax": {
+            "__option": "None"
+        },
+        "solPayment": {
+            "__option": "None"
+        },
+        "tokenPayment": {
+            "__option": "None"
+        },
+        "startDate": {
+            "__option": "None"
+        },
+        "thirdPartySigner": {
+            "__option": "None"
+        },
+        "tokenGate": {
+            "__option": "None"
+        },
+        "gatekeeper": {
+            "__option": "None"
+        },
+        "endDate": {
+            "__option": "None"
+        },
+        "allowList": {
+            "__option": "None"
+        },
+        "mintLimit": {
+            "__option": "None"
+        },
+        "nftPayment": {
+            "__option": "None"
+        },
+        "redeemedAmount": {
+            "__option": "None"
+        },
+        "addressGate": {
+            "__option": "None"
+        },
+        "nftGate": {
+            "__option": "None"
+        },
+        "nftBurn": {
+            "__option": "None"
+        },
+        "tokenBurn": {
+            "__option": "None"
+        },
+        "freezeSolPayment": {
+            "__option": "None"
+        },
+        "freezeTokenPayment": {
+            "__option": "None"
+        },
+        "programGate": {
+            "__option": "None"
+        },
+        "allocation": {
+            "__option": "None"
+        },
+        "token2022Payment": {
+            "__option": "None"
+        },
+        "solFixedFee": {
+            "__option": "None"
+        },
+        "nftMintLimit": {
+            "__option": "None"
+        },
+        "edition": {
+            "__option": "None"
+        },
+        "assetPayment": {
+            "__option": "None"
+        },
+        "assetBurn": {
+            "__option": "None"
+        },
+        "assetMintLimit": {
+            "__option": "None"
+        },
+        "assetBurnMulti": {
+            "__option": "None"
+        },
+        "assetPaymentMulti": {
+            "__option": "None"
+        },
+        "assetGate": {
+            "__option": "None"
+        },
+        "vanityMint": {
+            "__option": "None"
+        }
+    },
+    "groups": [
+        {
+            "label": "group1",
+            "guards": {
+                "botTax": {
+                    "__option": "Some",
+                    "value": {
+                        "lamports": {
+                            "basisPoints": "10000000",
+                            "identifier": "SOL",
+                            "decimals": 9
+                        },
+                        "lastInstruction": false
+                    }
+                },
+                "solPayment": {
+                    "__option": "Some",
+                    "value": {
+                        "lamports": {
+                            "basisPoints": "100000000",
+                            "identifier": "SOL",
+                            "decimals": 9
+                        },
+                        "destination": "Tes1zkZkXhgTaMFqVgbgvMsVkRJpq4Y6g54SbDBeKVV"
+                    }
+                },
+                "tokenPayment": {
+                    "__option": "None"
+                },
+                "startDate": {
+                    "__option": "Some",
+                    "value": {
+                        "date": "1723996800"
+                    }
+                },
+                "thirdPartySigner": {
+                    "__option": "None"
+                },
+                "tokenGate": {
+                    "__option": "None"
+                },
+                "gatekeeper": {
+                    "__option": "None"
+                },
+                "endDate": {
+                    "__option": "Some",
+                    "value": {
+                        "date": "1729270800"
+                    }
+                },
+                "allowList": {
+                    "__option": "None"
+                },
+                "mintLimit": {
+                    "__option": "Some",
+                    "value": {
+                        "id": 1,
+                        "limit": 5
+                    }
+                },
+                "nftPayment": {
+                    "__option": "None"
+                },
+                "redeemedAmount": {
+                    "__option": "None"
+                },
+                "addressGate": {
+                    "__option": "None"
+                },
+                "nftGate": {
+                    "__option": "None"
+                },
+                "nftBurn": {
+                    "__option": "None"
+                },
+                "tokenBurn": {
+                    "__option": "None"
+                },
+                "freezeSolPayment": {
+                    "__option": "None"
+                },
+                "freezeTokenPayment": {
+                    "__option": "None"
+                },
+                "programGate": {
+                    "__option": "None"
+                },
+                "allocation": {
+                    "__option": "None"
+                },
+                "token2022Payment": {
+                    "__option": "None"
+                },
+                "solFixedFee": {
+                    "__option": "None"
+                },
+                "nftMintLimit": {
+                    "__option": "None"
+                },
+                "edition": {
+                    "__option": "None"
+                },
+                "assetPayment": {
+                    "__option": "None"
+                },
+                "assetBurn": {
+                    "__option": "None"
+                },
+                "assetMintLimit": {
+                    "__option": "None"
+                },
+                "assetBurnMulti": {
+                    "__option": "None"
+                },
+                "assetPaymentMulti": {
+                    "__option": "None"
+                },
+                "assetGate": {
+                    "__option": "None"
+                },
+                "vanityMint": {
+                    "__option": "None"
+                }
+            }
+        },
+    ]
+}
+```
+{% /totem-accordion  %}
+
+{% /dialect %}
+{% /dialect-switcher %}
+
+### Fetch additional Candy Machine related Accounts
+The choice of Guards you implement may necessitate fetching additional accounts. For instance, if you plan to verify a wallet's minting eligibility and are utilizing the `mintLimit` Guard, you would need to retrieve the `mintCounter` account. This account maintains a record of how many NFTs a particular wallet has already minted under that specific guard.
+
+#### `MintLimit` Accounts
+When the [`MintLimit`](/core-candy-machine/guards/mint-limit) guard is active, it's advisable to retrieve the `MintCounter` account for the user's wallet. This allows you to check whether the user has reached their minting limit or if they're still eligible to mint additional items.
+
+The following code snippet demonstrates how to fetch the `MintCounter`. Note that this example assumes you've already obtained the Candy Machine and Candy Guard data:
+
+```ts
+import { safeFetchMintCounterFromSeeds } from "@metaplex-foundation/mpl-core-candy-machine";
+
+const mintCounter = await safeFetchMintCounterFromSeeds(umi, {
+  id: 1, // The mintLimit id you set in your guard config
+  user: umi.identity.publicKey,
+  candyMachine: candyMachine.publicKey,
+  candyGuard: candyMachine.mintAuthority,
+});
+```
+
+#### `NftMintLimit` Accounts
+Similar to the `MintLimit` guard it can make sense to fetch the `NftMintCounter` account of the [`NftMintLimit`](/core-candy-machine/guards/nft-mint-limit) guard to verify eligibility.
+
+The following code snippet demonstrates how to fetch the `NftMintCounter` account. Note that this example assumes you've already obtained the Candy Machine and Candy Guard data:
+
+```ts
+import { 
+  findNftMintCounterPda,
+  fetchNftMintCounter
+ } from "@metaplex-foundation/mpl-core-candy-machine";
+
+const pda = findNftMintCounterPda(umi, {
+  id: 1, // The nftMintLimit id you set in your guard config
+  mint: asset.publicKey, // The address of the nft your user owns
+  candyGuard: candyMachine.mintAuthority,
+  candyMachine: candyMachine.publicKey,
+});
+      
+const nftMintCounter = fetchNftMintCounter(umi, pda)
+```
+
+#### `AssetMintLimit` Accounts
+Similar to the `NftMintCounter` guard it can make sense to fetch the `AssetMintCounter` account of the [`AssetMintLimit`](/core-candy-machine/guards/asset-mint-limit) guard to verify eligibility.
+
+The following code snippet demonstrates how to fetch the `AssetMintCounter` account. Note that this example assumes you've already obtained the Candy Machine data:
+
+```ts
+import { 
+  findAssetMintCounterPda,
+  fetchAssetMintCounter
+ } from "@metaplex-foundation/mpl-core-candy-machine";
+
+const pda = findAssetMintCounterPda(umi, {
+  id: 1, // The assetMintLimit id you set in your guard config
+  asset: asset.publicKey, // The address of the core nft your user owns
+  candyGuard: candyMachine.mintAuthority,
+  candyMachine: candyMachine.publicKey,
+});
+
+const assetMintCounter = fetchAssetMintCounter(umi, pda);
+```
+
+#### `Allocation` Accounts
+For the `Allocation` guard it can make sense to fetch the `AllocationTracker` account to verify that additional NFTs can be minted from a given group.
+
+The following code snippet demonstrates how to fetch the `AllocationTracker` account. Note that this example assumes you've already obtained the Candy Machine data:
+
+```ts
+import {
+  safeFetchAllocationTrackerFromSeeds,
+} from "@metaplex-foundation/mpl-core-candy-machine";
+
+const allocationTracker = await safeFetchAllocationTrackerFromSeeds(umi, {
+  id: 1, // The allocation id you set in your guard config
+  candyMachine: candyMachine.publicKey,
+  candyGuard: candyMachine.mintAuthority,
+});
+```
+
+#### `Allowlist` Accounts
+When implementing the Allowlist guard, it's crucial to execute a `route` instruction beforehand. This instruction generates a unique account for each wallet and Candy Machine combination, effectively marking the wallet as authorized to mint.
+
+From a UI perspective, it's beneficial to query this account. This allows you to determine whether the `route` instruction needs to be executed or if the user can proceed directly to the mint instruction.
+
+The following code snippet demonstrates how to fetch this account. It assumes that you've already retrieved the Candy Machine data. However, if you prefer, you can hardcode the `candyGuard` and `candyMachine` public keys instead.
+
+```ts
+import {
+  safeFetchAllowListProofFromSeeds,
+  getMerkleRoot,
+} from "@metaplex-foundation/mpl-core-candy-machine";
+
+const allowlist = [
+  "Tes1zkZkXhgTaMFqVgbgvMsVkRJpq4Y6g54SbDBeKVV",
+  "GjwcWFQYzemBtpUoN5fMAP2FZviTtMRWCmrppGuTthJS",
+  "AT8nPwujHAD14cLojTcB1qdBzA1VXnT6LVGuUd6Y73Cy"
+];
+
+const allowListProof = await safeFetchAllowListProofFromSeeds(umi, {
+  candyGuard: candyMachine.mintAuthority,
+  candyMachine: candyMachine.publicKey,
+  merkleRoot: getMerkleRoot(allowlist),
+  user: umi.identity.publicKey,
+});
+```
+
+### Fetch Wallet Data
+To validate the legibility you may also want to fetch information about the connected wallet. Depending on the Guards you are using you may want to know how much SOL is in the wallet and which Tokens and NFTs the wallet owns.
+
+To fetch the SOL balance the built in `getAccount` umi function can be used to fetch the wallet account:
+```ts
+const account = await umi.rpc.getAccount(umi.identity.publicKey);
+const solBalance = account.lamports;
+```
+
+If you are using one of the guards that require tokens or NFTs you may want to fetch those, too. We recommend to use [DAS API](/das-api/methods/get-asset-by-owner) for this. DAS is an index of Tokens maintained by your RPC Provider. Using this allows to fetch all the required information with one call. In the UI you can then use the returned object to verify if the connected wallet owns the required tokens or NFTs.
+
+```ts
+import { publicKey } from '@metaplex-foundation/umi';
+import { dasApi } from '@metaplex-foundation/digital-asset-standard-api';
+
+// When defining the umi instance somewhere before you can already
+// add `.use(dasApi());` so there is no need to define umi again.
+const umi = createUmi('<ENDPOINT>').use(dasApi());
+
+const assets = await umi.rpc.getAssetsByOwner({
+    umi.identity.publicKey
+});
+
+```
+
+## Verify legibility
+After fetching all the required data you can then verify if the connected wallet is allowed to mint or not.
+
+It's important to note that when groups are attached to a Candy Machine, the `default` guards apply universally across all created groups.  Also, when groups are enabled the ability to mint from the `default` group becomes disabled and you must use the created groups for minting.
+
+Therefore, if there are no groups defined you need to check if all the mint conditions of the `default` group are met. If there are groups defined the combination of both the `default` guards and the current minting group guards both need to be validated.
+
+Given a Candy Machine with the `startDate`, `SolPayment` and `mintLimit` guards attached that is not leveraging groups the following validations should be done before allowing the user to call the mint function. It is assumed that the `candyGuard` was fetched before and one Core NFT Asset should be minted.
+
+1. Validate the `startDate` is in the past. Note that we are not using the users device time here but instead fetching the current internal Solana Blocktime since this is the time the Candy Machine will use for the validation on mint: 
+```ts
+import { unwrapOption } from '@metaplex-foundation/umi';
+
+let allowed = true;
+
+// fetch the current slot and read the blocktime
+const slot = await umi.rpc.getSlot();
+let solanaTime = await umi.rpc.getBlockTime(slot);
+
+// Check if a `default` startDate guard is attached
+const startDate = unwrapOption(candyGuard.guards.startDate);
+if (startDate) {
+  // validate the startTime is in the future
+  if (solanaTime < startDate) {
+        console.info(`StartDate not reached!`);
+        allowed = false;
+  }
+}
+```
+
+2. Check if the Wallet has enough SOL to pay for the mint. Note that we are not including transaction fees here and assume that the `SolBalance` was fetched as described above.
+```ts
+import { unwrapOption } from '@metaplex-foundation/umi';
+
+const solPayment = unwrapOption(candyGuard.guards.solPayment);
+if (solPayment){
+  if (solPayment.lamports.basisPoints > solBalance){
+    console.info(`Not enough SOL!`);
+    allowed = false;
+  }
+}
+```
+
+3. Make sure the `mintLimit` was not reached yet:
+```ts
+import { unwrapOption } from '@metaplex-foundation/umi';
+import { 
+  safeFetchMintCounterFromSeeds,
+} from "@metaplex-foundation/mpl-core-candy-machine";
+
+const mintLimit = unwrapOption(candyGuard.guards.mintLimit);
+if (mintLimit){
+      const mintCounter = await safeFetchMintCounterFromSeeds(umi, {
+      id: mintLimit.id,
+      user: umi.identity.publicKey,
+      candyMachine: candyMachine.publicKey,
+      candyGuard: candyMachine.mintAuthority,
+    });
+
+    // mintCounter PDA exists (not the first mint)
+    if (mintCounter && mintLimit.limit >= mintCounter.count
+    ) {
+      allowed = false;
+    }
+}
+```
+
+When a wallet is not eligible to mint it is helpful to disable the mint button and show the user the reason for not being eligible to mint. E.g. a `Not enough SOL!` message.
+
+## Guard Routes
+Certain Guards require specific instructions to be executed before minting can occur. These instructions create accounts that store data or provide proof of a wallet's eligibility to mint. The execution frequency of these instructions varies depending on the Guard type. 
+
+{% callout type="note" title="Target Audience of this section" %}
+In case you are not using the `Allocation`, `FreezeSolPayment`, `FreezeTokenPayment` or `Allowlist` guard it is safe to skip this section.
+{% callout type="note" title="Configuration" %}
+
+Some Guards need their routes executed only once for the entire Candy Machine. For these, it's not necessary to include a function in the UI but can be run upfront once through a script:
+- [Allocation](/core-candy-machine/guards/allocation)
+- [FreezeSolPayment](/core-candy-machine/guards/freeze-sol-payment)
+- [FreezeTokenPayment](/core-candy-machine/guards/freeze-token-payment)
+
+Other Guards require their routes to be executed for each individual wallet. In these cases, the route instruction should be run prior to the mint transaction:
+- [Allowlist](/core-candy-machine/guards/allow-list)
+
+For an example of how to implement Guard routes, consider the case of the **Allowlist** guard. This assumes that the `allowListProof` has been fetched as described earlier, and that `allowlist` represents an array of eligible wallet addresses. The following code demonstrates how to handle this scenario in your implementation.
+
+```ts
+import {
+  getMerkleRoot,
+  getMerkleProof,
+  route
+} from "@metaplex-foundation/mpl-core-candy-machine";
+import {
+  publicKey,
+} from "@metaplex-foundation/umi";
+
+// assuming you fetched the AllowListProof as described above
+if (allowListProof === null) { 
+  route(umi, {
+    guard: "allowList",
+    candyMachine: candyMachine.publicKey,
+    candyGuard: candyMachine.mintAuthority,
+    group: "default", // Add your guard label here
+    routeArgs: {
+      path: "proof",
+      merkleRoot: getMerkleRoot(allowlist),
+      merkleProof: getMerkleProof(allowlist, publicKey(umi.identity)),
+    },
+  })
+}
+```
+
+## Create a Mint Function
+It is recommended to implement legibility checks for all the guards that are attached. Keep in mind that if there are any groups attached the `default` guards will apply to all additional groups, while simultaneously disabling the `default` group.
+
+After those checks are done and, if required, the route instructions were run the mint transaction can be built. Depending on the guards, `mintArgs` may have to be passed in. These are arguments that help build the mint transaction by passing in the correct accounts and data. For example the `mintLimit` guard requires the `mintCounter` account. The Umi SDK abstracts these details away but still requires some information to build the transaction correctly.
+
+Assuming again a Candy Machine with `startDate`, `SolPayment` and `mintLimit` Guards attached let's see how to build the `mintArgs`.
+
+```ts
+import { some, unwrapOption } from '@metaplex-foundation/umi';
+import {
+  DefaultGuardSetMintArgs
+} from "@metaplex-foundation/mpl-core-candy-machine";
+
+let mintArgs: Partial<DefaultGuardSetMintArgs> = {};
+
+// add solPayment mintArgs
+const solPayment = unwrapOption(candyGuard.guards.solPayment)
+if (solPayment) {
+  mintArgs.solPayment = some({
+    destination: solPayment.destination,
+  });
+}
+
+// add mintLimit mintArgs
+const mintLimit = unwrapOption(candyGuard.guards.mintLimit)
+if (mintLimit) {
+  mintArgs.mintLimit = some({ id: mintLimit.id });
+}
+```
+
+Not all Guards require additional `mintArgs` to be passed in. This is the reason `startDate` is not in the above code snippet. To understand if the guards you are using require `mintArgs` to be passed in it is recommended to check the [Developer Hub](/core-candy-machine) Guard pages. If there are "Mint Settings" described you need to pass in `mintArgs` for this guard.
+
+Now that the `mintArgs` are built let's see how to call the mint function itself. The following snippet assumes that the `candyMachine` and `candyGuard` were fetched as described above. Technically the publicKeys of `candyMachine`, `collection`, `candyGuard` and all the `mintArgs` can also be passed in manually in case you do not want to fetch them.
+
+```ts
+// Generate the NFT address
+const nftMint = generateSigner(umi);
+
+await mintV1(umi, {
+  candyMachine: candyMachine.publicKey,
+  collection: candyMachine.collectionMint,
+  asset: nftMint,
+  candyGuard: candyGuard.publicKey,
+  mintArgs,
+}).sendAndConfirm(umi)
+
+console.log(`NFT ${nftMint.publicKey} minted!`)
+```
+
+
+## Advanced Minting Techniques
+
+While the basic minting function we've discussed works well for most cases, there are some advanced techniques you can use to enhance your minting process. Let's explore a couple of these:
+
+### Minting Multiple NFTs in One Transaction
+
+For efficiency, you might want to allow users to mint multiple NFTs in a single transaction. Here's how you can achieve this:
+
+Depending on the specific setup it can be helpful to allow minting multiple NFTs in one Transaction by combining the [Transaction Builders](/umi/transactions#transaction-builders).
+
+```ts
+let builder = transactionBuilder()
+  .add(mintV1(...))
+  .add(mintV1(...))
+```
+
+If you add to many `mintV1` instructions into a transaction you will recieve a `Transaction too large` error. The function [`builder.fitsInOneTransaction(umi)`](/umi/transactions#transaction-builders) allows to check for this before sending the transaction so that the transaction can be split before being sent. In case splitting is needed using [`signAllTransactions`](/umi/transactions#building-and-signing-transactions) is recommended so that only one popup has to be approved in the Wallet Adapter.    
+
+### Guard Groups
+
+Guard groups are a powerful feature of Core Candy Machine that allow you to define multiple sets of guards with different configurations. They can be particularly useful in scenarios such as:
+
+1. Tiered minting: Different groups for VIP, early access, and public sale.
+2. Multiple payment options: Groups for SOL payment, SPL token payment, etc.
+3. Time-based minting: Groups with different start and end dates.
+4. Allowlist-based minting: Groups for allowlisted users and public sale.
+
+To implement guard groups in your UI, you have two main approaches:
+
+1. Multiple buttons approach:
+   Create a separate button for each group, allowing users to choose their preferred minting option.
+
+2. Automatic group selection:
+   Implement a function that determines the best group for a user based on their eligibility and current conditions.
+
+Regardless of which scenario or approach you choose, here's how to adjust the `mintV1` instruction to work with your specific group. The key modification is to include a `group` parameter that specifies the desired label.
+
+```ts
+// Generate the NFT address
+const nftMint = generateSigner(umi);
+
+await mintV1(umi, {
+  candyMachine: candyMachine.publicKey,
+  collection: candyMachine.collectionMint,
+  asset: nftMint,
+  candyGuard: candyGuard.publicKey,
+  mintArgs,
+  group: "group1",
+}).sendAndConfirm(umi)
+
+console.log(`NFT ${nftMint.publicKey} minted!`)
+```
+
+
+## Next Steps
+
+Now that you've mastered the essentials of interacting with the Candy Machine in your frontend, you might want to consider the following steps to further enhance and distribute your project:
+
+1. Hosting: Make your frontend accessible to users by deploying it to a hosting platform. Popular options among developers include:
+   - Vercel
+   - Cloudflare Pages
+   - Netlify
+   - GitHub Pages
+
+2. Testing: Thoroughly test your UI on various devices and browsers to ensure a smooth user experience.
+
+3. Optimization: Fine-tune your frontend for performance, especially if you're expecting high traffic during minting events.
+
+8. Monitoring: Set up monitoring tools to keep track of your Candy Machine UIs status and quickly address any issues that may arise.
+
+By focusing on these areas, you'll be well-prepared to launch and maintain a successful NFT minting project using Core Candy Machine.

+ 8 - 0
src/pages/core-candy-machine/guides/index.md

@@ -0,0 +1,8 @@
+---
+title: Guides for Metaplex Core Candy Machine
+metaTitle: Guides for Metaplex Core Candy Machine | Core Candy Machine
+description: A list of guides for the Metaplex Core Candy Machine for creating Core based NFT collections on Solana.
+---
+
+
+{% quick-link title="Create a Website for minting Assets from your Core Candy Machine" icon="CodeBracketSquare" href="/core-candy-machine/guides/create-a-core-candy-machine-ui" description="Learn how to build your own Core Candy Machine UI" /%}

+ 274 - 0
src/pages/guides/general/create-deterministic-metadata-with-turbo.md

@@ -0,0 +1,274 @@
+---
+title: Create deterministic metadata with Turbo
+metaTitle: Create deterministic metadata with Turbo | General Guides
+description: Learn how to create deterministic metadata leveraging the Turbo SDK for Arweave-based uploads.
+# remember to update dates also in /components/guides/index.js
+created: '10-19-2024'
+updated: '10-19-2024'
+---
+
+To utilize the metadata randomization feature in the MPL-Hybrid program, the off-chain metadata URIs need to follow a consistent, incremental structure. To achieve this, we will use the [path manifest](https://cookbook.arweave.dev/concepts/manifests.html) feature from Arweave and the Turbo SDK. **This guide will demonstrate how to set this up!**
+
+{% callout title="What is Turbo" %}
+
+Turbo is a ultrahigh-throughput Permaweb service that streamlines the funding, indexing, and transmission of data to and from Arweave. It provides graphical and programmatic interfaces for payment options in fiat currency with credit or debit cards as well as cryptocurrencies such as ETH, SOL, and AR.
+
+{% /callout %}
+
+## Prerequisite
+
+### Required Packages
+
+{% packagesUsed packages=[ "@ardrive/turbo-sdk" ] type="npm" /%}
+
+Install the required packages for this guide.
+
+```js
+npm i @ardrive/turbo-sdk
+```
+
+### Metadata Folder
+
+In this example, we will show you how to upload metadata in a deterministic way. To do so, you'll need to prepare all the assets before starting. 
+
+To generate the metadata, you can use [one of these methods](/candy-machine/guides/create-an-nft-collection-on-solana-with-candy-machine#image-and-metadata-generators) and save the metadata follow an incremental naming convention starting from 0 like this:
+
+```
+metadata/
+├─ 0.json
+├─ 1.json
+├─ 2.json
+├─ ...
+```
+
+**Note**: When creating the metadata, make sure to follow the proper [JSON schema for NFTs](/token-metadata/token-standard#the-non-fungible-standard)!
+
+## Setting up Turbo 
+
+Since Turbo is compatible with multiple tokens and chains, we'll need to configure our Turbo instance to use Solana as the token for this guide. We do this by calling the `TurboFactory.authenticated()` method and passing in Solana-specific configuration options.
+
+```javascript
+import { TurboFactory } from '@ardrive/turbo-sdk';
+
+// Import here the keypair.json file that you're going
+// to use to pay for the upload
+import secretKey from "/path/to/your/kepypair.json";
+
+const turbo = TurboFactory.authenticated({
+  privateKey: bs58.encode(Uint8Array.from(secretKey)),
+  token: 'solana',
+  gatewayUrl: `https://api.devnet.solana.com`,
+  paymentServiceConfig: { url: "https://payment.ardrive.dev" },
+  uploadServiceConfig: { url: "https://upload.ardrive.dev" },
+});
+```
+
+**Note**: In this example, we explicitly provide the `gatewayUrl`, `paymentServiceConfig`, and `uploadServiceConfig` because we want to configure the environment to work on devnet. For mainnet usage, you can leave these fields empty, and Turbo will default to the mainnet endpoints.
+
+## Upload the Metadata
+
+Turbo simplifies the process of uploading entire folders of metadata using the `TurboAuthenticatedClient.uploadFolder()` function. This function supports Manifests by default, returning a Manifest ID via `result.manifestResponse?.id`, which can be used for metadata creation and escrow setup.
+
+To simplify the process, this guide provides helper function called `uploadAssetsAndMetadata()` that handles the entire workflow.
+
+```javascript
+const metadataUploadResponse = await uploadMetadata(turbo);
+```
+
+**Steps of the `uploadAssetsAndMetadata()` helper**
+
+1. Determines how many lamports are needed for the upload by calling `calculateRequiredLamportsForUpload()`, which calculates the upload cost in Winc (Turbo’s token) and converts it to lamports using `TurboAuthenticatedClient.getWincForToken()`.
+
+2. If the wallet lacks sufficient Winc, the function uses `TurboAuthenticatedClient.topUpWithTokens()` to top up the required amount by converting lamports to Winc.
+
+3. Once the wallet has enough Winc, upload the metadata folder using `TurboAuthenticatedClient.uploadFolder()`, which returns a Manifest ID for the metadata.
+
+### Calculating Required Lamports
+
+```javascript
+const requiredLamportsForMetadata = await calculateRequiredLamportsForUpload(
+  turbo,
+  calculateFolderSize(metadataFolderPath)
+);
+```
+
+We begin by calculating the total size of the folder in bytes. The following function recursively traverses the folder structure to sum the sizes of all files:
+
+```javascript
+function calculateFolderSize(folderPath: string): number {
+  return fs.readdirSync(folderPath).reduce((totalSize, item) => {
+    const fullPath = path.join(folderPath, item);
+    
+    const stats = fs.statSync(fullPath);
+
+    return stats.isFile() 
+        ? totalSize + stats.size 
+        : totalSize + calculateFolderSize(fullPath);
+  }, 0);
+}
+```
+
+Once the folder size is determined, the next step is to calculate how many lamports are needed for the upload. This is done using the `calculateRequiredLamportsForUpload()` function, which determines the Winc cost and converts it into lamports:
+
+```javascript
+async function calculateRequiredLamportsForUpload(turbo: TurboAuthenticatedClient, fileSize: number): Promise<number> {
+    /// If the file size is less than 105 KiB, then we don't need to pay for it
+    if (fileSize < 107_520) { return 0; }
+
+    /// Check how many winc does it cost to upload the file
+    const uploadPrice = new BigNumber((await turbo.getUploadCosts({ bytes: [fileSize]}))[0].winc);
+
+    /// Check the current Winc balance
+    const currentBalance = new BigNumber((await turbo.getBalance()).winc);
+
+    /// Calculate how much Winc is required to upload the file
+    const requiredWinc = uploadPrice.isGreaterThan(currentBalance)
+        ? uploadPrice.minus(currentBalance)
+        : new BigNumber(0); // If balance is enough, no Winc is required
+
+    /// If the required Winc is 0, we already have enough to upload the file
+    if (requiredWinc.isEqualTo(0)) { return 0; }
+
+    /// Calculate how much Winc 1 SOL is worth (1 SOL = 1_000_000_000 Lamports)
+    const wincForOneSol = new BigNumber((await turbo.getWincForToken({ tokenAmount: 1_000_000_000 })).winc);
+
+    /// Calculate how much SOL is required to upload the file (return in SOL)
+    const requiredSol = requiredWinc.dividedBy(wincForOneSol).toNumber();
+
+    /// Return the amount of SOL required in Lamports
+    return Math.floor(requiredSol * 1_000_000_000)
+}
+```
+
+### Top Up the Wallet and Upload Metadata
+
+To top up the wallet, we use the `TurboAuthenticatedClient.topUpWithTokens()` method, specifying the amount of lamports calculated in the previous step. This amount is converted into Winc (Turbo’s token), which is required for the upload process.
+
+**Note**: The top-up process is conditional. If we already have enough Winc in the wallet, the `calculateRequiredLamportsForUpload()` function will return 0, and no top-up will be necessary.
+
+```javascript
+// Top up wallet if required
+await turbo.topUpWithTokens({tokenAmount: lamportToTokenAmount(requiredLamportsForMetadata)});
+```
+
+After ensuring the wallet has enough Winc, we can proceed with uploading the image folder. This is done using the `TurboAuthenticatedClient.uploadFolder()` method. The upload will return a manifest ID that allows access to the uploaded files, formatted like this: `https://arweave.net/${manifestID}/${nameOfTheFile.extension}.`
+
+**Note**: It’s important to set the correct [MIME type](https://developer.mozilla.org/en-US/docs/Web/HTTP/MIME_types) for each file during the upload. If the MIME type is not set correctly, the file might not be displayed properly when accessed via the URI.
+
+
+```javascript 
+// Upload image folder
+const metadataUploadResponse = await turbo.uploadFolder({
+    folderPath: metadataFolderPath,
+    dataItemOpts: { tags: [{ name: 'Content-Type', value: 'application/json' }] },
+});
+```
+
+## Full code Example
+
+Here's the full code example that you can copy and paste for easy use
+
+{% totem %}
+
+{% totem-accordion title="Full Code Example" %}
+
+```javascript
+import { 
+    TurboFactory, 
+    TurboAuthenticatedClient, 
+    lamportToTokenAmount, 
+    TurboUploadFolderResponse 
+} from '@ardrive/turbo-sdk';
+
+import bs58 from 'bs58';
+import path from 'path';
+import fs from 'fs';
+import BigNumber from 'bignumber.js';
+
+import secretKey from "/path/to/your/kepypair.json";
+
+const imageFolderPath = path.join(__dirname, './assets');
+const metadataFolderPath = path.join(__dirname, './metadata');
+
+(async () => {
+    try {
+        /// Step 1: Setup Turbo
+        const turbo = TurboFactory.authenticated({
+            privateKey: bs58.encode(Uint8Array.from(secretKey)),
+            token: 'solana',
+            gatewayUrl: `https://api.devnet.solana.com`,
+            paymentServiceConfig: { url: "https://payment.ardrive.dev" },
+            uploadServiceConfig: { url: "https://upload.ardrive.dev" },
+        });
+
+        /// Step 2: Upload Metadata
+        const metadataUploadResponse = await uploadMetadata(turbo);
+    } catch (error) {
+        console.error("Error during execution:", error);
+    }
+})();
+
+async function uploadMetadata(turbo: TurboAuthenticatedClient): Promise<TurboUploadFolderResponse> {
+    // Calculate and upload metadata folder
+    const requiredLamportsForMetadata = await calculateRequiredLamportsForUpload(
+        turbo,
+        await calculateFolderSize(metadataFolderPath)
+    );
+
+    // Top up wallet if required
+    await turbo.topUpWithTokens({tokenAmount: lamportToTokenAmount(requiredLamportsForMetadata)});
+
+    // Upload metadata folder
+    const metadataUploadResponse = await turbo.uploadFolder({
+        folderPath: metadataFolderPath,
+        dataItemOpts: { tags: [{ name: 'Content-Type', value: 'application/json' }] },
+    });
+
+    console.log('Metadata Manifest ID:', metadataUploadResponse.manifestResponse?.id);
+    return metadataUploadResponse;
+}
+
+function calculateFolderSize(folderPath: string): number {
+  return fs.readdirSync(folderPath).reduce((totalSize, item) => {
+    const fullPath = path.join(folderPath, item);
+    
+    const stats = fs.statSync(fullPath);
+
+    return stats.isFile() 
+        ? totalSize + stats.size 
+        : totalSize + calculateFolderSize(fullPath);
+  }, 0);
+}
+
+async function calculateRequiredLamportsForUpload(turbo: TurboAuthenticatedClient, fileSize: number): Promise<number> {
+    /// If the file size is less than 105 KiB, then we don't need to pay for it
+    if (fileSize < 107_520) { return 0; }
+
+    /// Check how many winc does it cost to upload the file
+    const uploadPrice = new BigNumber((await turbo.getUploadCosts({ bytes: [fileSize]}))[0].winc);
+
+    /// Check the current Winc balance
+    const currentBalance = new BigNumber((await turbo.getBalance()).winc);
+
+    /// Calculate how much Winc is required to upload the file
+    const requiredWinc = uploadPrice.isGreaterThan(currentBalance)
+        ? uploadPrice.minus(currentBalance)
+        : new BigNumber(0); // If balance is enough, no Winc is required
+
+    /// If the required Winc is 0, we already have enough to upload the file
+    if (requiredWinc.isEqualTo(0)) { return 0; }
+
+    /// Calculate how much Winc 1 SOL is worth (1 SOL = 1_000_000_000 Lamports)
+    const wincForOneSol = new BigNumber((await turbo.getWincForToken({ tokenAmount: 1_000_000_000 })).winc);
+
+    /// Calculate how much SOL is required to upload the file (return in SOL)
+    const requiredSol = requiredWinc.dividedBy(wincForOneSol).toNumber();
+
+    /// Return the amount of SOL required in Lamports
+    return Math.floor(requiredSol * 1_000_000_000)
+}
+```
+
+{% /totem %}
+
+{% /totem-accordion %}

+ 247 - 0
src/pages/guides/javascript/how-to-add-metadata-to-spl-tokens.md

@@ -0,0 +1,247 @@
+---
+title: How to Add Metadata to a Solana Token
+metaTitle: How to Add Metadata to a Solana Token | Guides
+description: Learn how to add Metadata to an already existing Solana token.
+created: '10-01-2024'
+updated: '10-01-2024'
+---
+
+This guide will take you through adding metadata to an already initialized Solana Token (SPL Token) using the Metaplex Token Metadata protocol.
+
+{% callout %}
+It is recommended to use the available [create helper](https://developers.metaplex.com/token-metadata/mint#create-helpers) functions to create and initialize your token instead of doing so separately. If you are looking on how to do this, check out this guide instead [`How to create a Solana Token`](https://developers.metaplex.com/guides/javascript/how-to-create-a-solana-token).
+
+{% /callout %}
+
+## Prerequisite
+
+- Code Editor of your choice (recommended Visual Studio Code)
+- Node 18.x.x or above.
+
+## Initial Setup
+
+This guide assumes that you already have an SPL token initialized for which you'd like to add metadata to. You might need to modify and move functions around to suite your needs. 
+
+## Initializing
+
+Start by initializing a new empty project using a JS/TS package manager (npm, yarn, pnpm, bun, deno) of your choice.
+
+```bash
+npm init -y
+```
+
+### Required Packages
+
+Install the required packages for this guide.
+
+{% packagesUsed packages=["umi", "umiDefaults" ,"tokenMetadata"] type="npm" /%}
+
+```bash
+npm i @metaplex-foundation/umi
+```
+
+```bash
+npm i @metaplex-foundation/umi-bundle-defaults
+```
+
+```bash
+npm i @metaplex-foundation/mpl-token-metadata
+```
+
+### Imports and Wrapper Function
+
+We will list the necessary imports and our wrapper function,
+
+1. `addMetadata`
+
+```typescript
+import {
+	createV1,
+	findMetadataPda,
+	mplTokenMetadata,
+} from "@metaplex-foundation/mpl-token-metadata";
+import { generateSigner, signerIdentity, sol } from "@metaplex-foundation/umi";
+import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";
+import { base58 } from "@metaplex-foundation/umi/serializers";
+
+/// 
+/// instantiate umi 
+///
+
+
+// Add metadata to an existing SPL token wrapper function
+async function addMetadata() {
+	///
+	///
+	///  code will go in here
+	///
+	///
+}
+
+// run the function
+addMetadata();
+```
+
+## Setting up Umi
+
+This example is going to run through setting up Umi with a `generatedSigner()`. If you wish to set up a wallet or signer differently you can check out the [**Connecting to Umi**](/umi/connecting-to-umi) guide.
+
+You can place the Umi instantiation code inside or outside the code blocks, but to reduce code duplication, we will keep it outside.
+
+### Generating a New Wallet
+
+```ts
+const umi = createUmi("https://api.devnet.solana.com")
+	.use(mplTokenMetadata())
+	.use(mplToolbox());
+
+// Generate a new keypair signer.
+const signer = generateSigner(umi);
+
+// Tell umi to use the new signer.
+umi.use(signerIdentity(signer));
+
+// Airdrop 2 SOL to the identity
+// if you end up with a 429 too many requests error, you may have to use
+// the a different rpc other than the free default one supplied.
+await umi.rpc.airdrop(umi.identity.publicKey, sol(2));
+```
+
+### Use an Existing Wallet Stored Locally
+
+```ts
+const umi = createUmi("https://api.devnet.solana.com")
+	.use(mplTokenMetadata())
+	.use(mplToolbox());
+
+// You will need to us fs and navigate the filesystem to
+// load the wallet you wish to use via relative pathing.
+const walletFile = const imageFile = fs.readFileSync('./keypair.json')
+
+// Convert your walletFile onto a keypair.
+let keypair = umi.eddsa.createKeypairFromSecretKey(new Uint8Array(walletFile));
+
+// Load the keypair into umi.
+umi.use(keypairIdentity(umiSigner));
+```
+
+## Adding Metadata
+
+Adding metadata is also as simple as creating an SPL token. We will utilize the `createV1` helper method from the `mpl-token-metadata` library.
+
+Also note that this guide assumes that you already had your off-chain token metadata prepared beforehand. We will need the name, off-chain uri address and symbol
+
+```json
+name: "Solana Gold",
+symbol: "GOLDSOL",
+uri: "https://raw.githubusercontent.com/solana-developers/program-examples/new-examples/tokens/tokens/.assets/spl-token.json",
+```
+
+```typescript
+// Sample Metadata for our Token
+const tokenMetadata = {
+	name: "Solana Gold",
+	symbol: "GOLDSOL",
+	uri: "https://raw.githubusercontent.com/solana-developers/program-examples/new-examples/tokens/tokens/.assets/spl-token.json",
+};
+
+// Add metadata to an existing SPL token wrapper function
+async function addMetadata() {
+    const mint = publicKey("YOUR_TOKEN_MINT_ADDRESS");
+
+    // derive the metadata account that will store our metadata data onchain
+	const metadataAccountAddress = await findMetadataPda(umi, {
+		mint: mint,
+	});
+
+   // add metadata to our already initialized token using `createV1` helper 
+	const tx = await createV1(umi, {
+		mint,
+		authority: umi.identity,
+		payer: umi.identity,
+		updateAuthority: umi.identity,
+		name: tokenMetadata.name,
+		symbol: tokenMetadata.symbol,
+		uri: tokenMetadata.uri,
+		sellerFeeBasisPoints: percentAmount(5.5), // 5.5%
+		tokenStandard: TokenStandard.Fungible,
+	}).sendAndConfirm(umi);
+
+	let txSig = base58.deserialize(tx.signature);
+	console.log(`https://explorer.solana.com/tx/${txSig}?cluster=devnet`);
+}
+```
+
+Nullable fields like creators have been left out as they might not be as necessary for SPL tokens compared to Non Fungibles.
+
+Take note of the mint address, If you will call the functions at different instances, make sure to set the address of the `mint` field in the `findMetadataPda` function as `generateSigner` will return a new keypair upon each call. 
+
+## Full Code Example
+
+```typescript
+import {
+	createV1,
+	findMetadataPda,
+	mplTokenMetadata,
+} from "@metaplex-foundation/mpl-token-metadata";
+import { createMint, mplToolbox } from "@metaplex-foundation/mpl-toolbox";
+import { generateSigner, signerIdentity, sol } from "@metaplex-foundation/umi";
+import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";
+import { base58 } from "@metaplex-foundation/umi/serializers";
+
+const umi = createUmi("https://api.devnet.solana.com")
+	.use(mplTokenMetadata())
+	.use(mplToolbox());
+
+// Generate a new keypair signer.
+const signer = generateSigner(umi);
+
+// Tell umi to use the new signer.
+umi.use(signerIdentity(signer));
+
+// Airdrop 2 SOL to the identity
+// if you end up with a 429 too many requests error, you may have to use
+// the a different rpc other than the free default one supplied.
+await umi.rpc.airdrop(umi.identity.publicKey, sol(2));
+
+// your SPL Token mint address
+const mint = publicKey("YOUR_TOKEN_MINT_ADDRESS");
+ 
+
+// Sample Metadata for our Token
+const tokenMetadata = {
+	name: "Solana Gold",
+	symbol: "GOLDSOL",
+	uri: "https://raw.githubusercontent.com/solana-developers/program-examples/new-examples/tokens/tokens/.assets/spl-token.json",
+};
+
+// Add metadata to an existing SPL token wrapper function
+async function addMetadata() {
+    // derive the metadata account that will store our metadata data onchain
+	const metadataAccountAddress = await findMetadataPda(umi, {
+		mint: mint,
+	});
+
+	const tx = await createV1(umi, {
+		mint,
+		authority: umi.identity,
+		payer: umi.identity,
+		updateAuthority: umi.identity,
+		name: tokenMetadata.name,
+		symbol: tokenMetadata.symbol,
+		uri: tokenMetadata.uri,
+		sellerFeeBasisPoints: percentAmount(5.5), // 5.5%
+		tokenStandard: TokenStandard.Fungible,
+	}).sendAndConfirm(umi);
+
+	let txSig = base58.deserialize(tx.signature);
+	console.log(`https://explorer.solana.com/tx/${txSig}?cluster=devnet`);
+}
+
+// run the function
+addMetadata();
+```
+
+## What's Next?
+
+This guide helped you to add metadata to a Solana Token, from here you can head over to the [Token Metadata Program](/token-metadata) and check out helper functions that initialize and add metadata to your token in one step, working with non-fungibles and other various ways to interact with the Token Metadata program.

+ 1 - 1
src/pages/guides/javascript/how-to-create-a-solana-token.md

@@ -44,7 +44,7 @@ npm i @metaplex-foundation/umi-uploader-irys;
 
 ```js
 npm i @metaplex-foundation/mpl-toolbox;
-```
+
 
 ### Imports and Wrapper Function
 

+ 236 - 0
src/pages/guides/setup-a-local-validator.md

@@ -0,0 +1,236 @@
+---
+title: Setup a Local Validator
+metaTitle: Setup a Local Validator
+description: Learn how to setup a local development environment and use a local validator
+# remember to update dates also in /components/guides/index.js
+created: '11-06-2024'
+updated: '11-06-2024'
+---
+
+## Overview
+
+A **Local Validator** acts as your personal node, providing a local sandbox environment for testing applications without the need to connect to a live blockchain network. It operates a **fully customizable local test ledger**, which is a simplified version of the Solana ledger, equipped with all **native programs pre-installed** and various features enabled.
+
+### Setup 
+
+To start using the local validator, you'll need to install the Solana Tools CLI using the appropriate commands for your operating system.
+
+{% dialect-switcher title="Installation Commands" %}
+
+{% dialect title="MacOs & Linux" id="MacOs & Linux" %}
+
+```
+sh -c "$(curl -sSfL https://release.solana.com/v1.18.18/install)"
+```
+
+{% /dialect %}
+
+{% dialect title="Windows" id="Windows" %}
+
+```
+cmd /c "curl https://release.solana.com/v1.18.18/solana-install-init-x86_64-pc-windows-msvc.exe --output C:\solana-install-tmp\solana-install-init.exe --create-dirs"
+```
+
+{% /dialect %}
+
+{% /dialect-switcher %}
+
+**Note**: The installation script references the `1.18.18` version of Solana. To install the latest version or discover different installation methods, refer to the official [Solana documentation](https://docs.solanalabs.com/cli/install).
+
+### Usage
+
+After installing the CLI, you can start your local validator by running a simple command.
+
+```
+solana-test-validator
+```
+
+Upon launch, the validator will be accessible at a local URL(http://127.0.0.1:8899). You'll need to establish a connection by configuring your code with this URL.
+
+```ts
+import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
+
+const umi = createUmi("http://127.0.0.1:8899")
+```
+
+The local validator will generate a directory named `test-ledger` in your user folder. This directory holds all data related to your validator, including accounts and programs. 
+
+To reset your local validator, you can either delete the `test-ledger` folder or use a reset command to restart the validator.
+
+Additionally, the `solana-logs` feature is extremely useful for monitoring program outputs during testing.
+
+## Managing Programs and Accounts
+
+The Local Validator doesn’t include specific programs and accounts found on mainnet. It only comes with Native Programs and the accounts you create during testing. If you need specific programs or accounts from mainnet, the Solana CLI allows you to download and load them onto your local validator.
+
+### Downloading Accounts and Programs:
+
+You can easily download accounts or programs from a source cluster to your local validator for testing purposes. This allows you to replicate the mainnet environment.
+
+**For accounts:**
+```
+solana account -u <source cluster> --output <output format> --output-file <destination file name/path> <address of account to fetch>
+```
+**For Programs:**
+```
+solana program dump -u <source cluster> <address of account to fetch> <destination file name/path>
+```
+
+### Loading Accounts and Programs:
+
+Once downloaded, these accounts and programs can be loaded into your local validator using the CLI. You can run commands to load specific accounts and programs into your local environment, ensuring they are ready for testing.
+
+**For accounts:**
+```
+solana-test-validator --account <address to load the account to> <path to account file> --reset
+```
+**For programs**
+```
+solana-test-validator --bpf-program <address to load the program to> <path to program file> --reset
+```
+
+## Looking at Local transaction on Explorers
+
+Using a local validator doesn't prevent us from using the explorer since many explorers have the capability to connect to our local port and read the local ledger stored in the `test-ledger` folder we mentioned earlier.
+
+There are two ways to do this:
+- Create a link to the transaction signature that points to the local cluster of your favorite explorer.
+- Manually change the cluster on the webpage and then paste the transaction link.
+
+### Creating a link to the transaction signature
+
+When you send a transaction with Umi, you'll receive two key pieces of information: a signature and a result. The signature is in base58 format, so you'll need to deserialize it to make it readable for the blockchain. 
+
+You can do this with the following code:
+```typescript 
+const signature = base58.deserialize(transaction.signature)[0]
+```
+
+Once you have the signature, you can use it with your preferred explorer like this:
+
+{% totem %}
+
+{% totem-accordion title="Solana Explorer" %}
+
+```typescript
+console.log(`Transaction Submitted! https://explorer.solana.com/tx/${signature}?cluster=custom&customUrl=http%3A%2F%2Flocalhost%3A8899`)
+```
+
+{% /totem-accordion %}
+
+{% totem-accordion title="SolanaFM" %}
+
+```typescript
+console.log(`Transaction Submitted! https://solana.fm/tx/${signature}?cluster=localnet-solana`)
+```
+
+{% /totem-accordion %}
+
+{% /totem %}
+
+### Manually changing the Cluster
+
+As mentioned earlier, block explorers allow users to utilize a custom RPC to view transactions. To look at local validator transaction you'll need to look for an input box in the `choose cluster` modal and enter the following address: `http://127.0.0.1:8899`.
+
+Note: The [Solana Explorer](/https://explorer.solana.com/) automatically defaults to the local validator port when you select Custom RPC URL, so you don’t need to make any additional changes. 
+
+## Creating a "Metaplex" Local Validator
+
+{% callout title="Disclaimer" %}
+
+Unfortunately, this part of the guide is available only for users on **Linux** or **MacOS** due to the use of Bash scripts. However, if you're using Windows and still want to follow along to create your own Metaplex validator, you can use the [Windows Subsystem for Linux (WSL)](https://learn.microsoft.com/en-us/windows/wsl/install) or one of the solutions provided in [this thread](https://stackoverflow.com/questions/6413377/is-there-a-way-to-run-bash-scripts-on-windows)!.
+
+{% /callout %}
+
+With the basics of the local validator setup and management, you can create and manage personalized local validators through **bash scripts**. 
+
+For example, you can create a `metaplex-test-validator` that includes the main Metaplex programs: `mpl-token-metadata`, `mpl-bubblegum`, and `mpl-core`.
+
+### Setting Up Directories and Downloading Program Data
+
+First, you'll create a directory within your path to store the necessary programs for your local validator.
+
+```
+mkdir ~/.local/share/metaplex-local-validator
+```
+
+Then, download the program data from specified addresses into this directory.
+
+```
+solana program dump -u m metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s ~/.local/share/metaplex-local-validator/mpl-token-metadata.so
+```
+```
+solana program dump -u m BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY ~/.local/share/metaplex-local-validator/mpl-bubblegum.so
+```
+```
+solana program dump -u m CoREENxT6tW1HoK8ypY1SxRMZTcVPm7R94rH4PZNhX7d ~/.local/share/metaplex-local-validator/mpl-core.so
+```
+
+{% totem %}
+
+{% totem-accordion title="Additional Metaplex Programs" %}
+
+| Name               | Program ID                                   | 
+| ------------------ | -------------------------------------------- | 
+| Auction House      | hausS13jsjafwWwGqZTUQRmWyvyxn9EQpqMwV1PBBmk  | 
+| Auctioneer         | neer8g6yJq2mQM6KbnViEDAD4gr3gRZyMMf4F2p3MEh  | 
+| Bubblegum          | BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY | 
+| Candy Guard        | Guard1JwRhJkVH6XZhzoYxeBVQe872VH6QggF4BWmS9g | 
+| Candy Machine v3   | CndyV3LdqHUfDLmE5naZjVN8rBZz4tqhdefbAnjHG3JR | 
+| Core               | CoREENxT6tW1HoK8ypY1SxRMZTcVPm7R94rH4PZNhX7d | 
+| Core Candy Guard   | CMAGAKJ67e9hRZgfC5SFTbZH8MgEmtqazKXjmkaJjWTJ | 
+| Core Candy Machine | CMACYFENjoBMHzapRXyo1JZkVS6EtaDDzkjMrmQLvr4J | 
+| Gumdrop            | gdrpGjVffourzkdDRrQmySw4aTHr8a3xmQzzxSwFD1a  |
+| Hydra              | hyDQ4Nz1eYyegS6JfenyKwKzYxRsCWCriYSAjtzP4Vg  | 
+| Inscriptions       | 1NSCRfGeyo7wPUazGbaPBUsTM49e1k2aXewHGARfzSo  | 
+| MPL-Hybrid         | MPL4o4wMzndgh8T1NVDxELQCj5UQfYTYEkabX3wNKtb  | 
+| Token Auth Rules   | auth9SigNpDKz4sJJ1DfCTuZrZNSAgh9sFD3rboVmgg  | 
+| Token Metadata     | metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s  | 
+
+{% /totem-accordion %}
+
+{% /totem %}
+
+### Creating a Validator Script
+
+Next, create a validator script that simplifies the process of running your local validator with all the required programs. By scripting the validator setup, you can easily start testing with your personalized environment, including all relevant Metaplex programs.
+
+Start by opening a new script file using:
+
+```
+sudo nano /usr/local/bin/metaplex-local-validator
+```
+
+**Note**: If the /user/local/bin directory doesn’t exist, you can create it using `sudo mkdir -p -m 775 /usr/local/bin.`
+
+Paste in the following code into the editor and save it:
+
+```bash
+#!/bin/bash
+
+# Validator command
+COMMAND="solana-test-validator -r --bpf-program metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s ~/.local/share/metaplex-local-validator/mpl-token-metadata.so --bpf-program BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY ~/.local/share/metaplex-local-validator/mpl-bubblegum.so --bpf-program CoREENxT6tW1HoK8ypY1SxRMZTcVPm7R94rH4PZNhX7d ~/.local/share/metaplex-local-validator/mpl-core.so"
+
+# Append any additional arguments passed to the script
+for arg in "$@"
+do
+    COMMAND+=" $arg"
+done
+
+# Execute the command
+eval $COMMAND
+```
+
+**Note**: To exit and save, use Ctrl + X, then Y to confirm, and Enter to save.
+
+Once your script is ready, modify its permissions so it can be executed:
+
+```
+sudo chmod +x /usr/local/bin/metaplex-test-validator
+```
+
+Finally, test your new validator within your project folder:
+
+```
+metaplex-test-validator
+```

+ 266 - 0
src/pages/guides/templates/metaplex-nextjs-tailwind-template.md

@@ -0,0 +1,266 @@
+---
+title: Metaplex Solana NextJs Tailwind Template
+metaTitle: Metaplex Solana NextJs Tailwind Template | Web UI Templates
+description: A web UI template using Nextjs, Tailwind, Metaplex Umi, Solana WalletAdapter and Zustand.
+---
+
+Downloadable and reusable templates that utilizes Nextjs and Tailwind for the front end framework while also being preinstalled with Metaplex Umi, Solana WalletAdapter, and Zustand global store for ease of use.
+
+{% image src="/images/metaplex-next-js-template.png" classes="m-auto" /%}
+
+## Features
+
+- Nextjs React framework
+- Tailwind
+- Solana WalletAdapter
+- Metaplex Umi
+- Zustand
+- Dark/Light Mode
+- Umi Helpers
+
+## Installation
+
+We currently have a number of templates available for Next JS with slightly different configurations and UI frameworks/component library.
+
+### Tailwind
+
+```shell
+git clone https://github.com/metaplex-foundation/metaplex-nextjs-tailwind-template.git
+```
+
+Github Repo - [https://github.com/metaplex-foundation/metaplex-nextjs-tailwind-template](https://github.com/metaplex-foundation/metaplex-nextjs-tailwind-template)
+
+### Tailwind + Shadcn
+
+```shell
+git clone https://github.com/metaplex-foundation/metaplex-nextjs-tailwind-shadcn-template.git
+```
+
+Github Repo - [https://github.com/metaplex-foundation/metaplex-nextjs-tailwind-shadcn-template](https://github.com/metaplex-foundation/metaplex-nextjs-tailwind-shadcn-template)
+
+_The following sections cover common features shared by all templates listed on this page. Template-specific features are not included here; please refer to the respective GitHub repositories for detailed documentation on individual templates._
+
+## Setup
+
+### Change RPC
+
+You are free to set up the RPC url into your project as you wish either via:
+
+- .env
+- constants.ts file
+- hardcoded into umi directly
+
+In this example the RPC url is hardcoded into the `umiStore` umi state under `src/store/useUmiStore.ts` at line `21`.
+
+```ts
+const useUmiStore = create<UmiState>()((set) => ({
+  // add your own RPC here
+  umi: createUmi('http://api.devnet.solana.com').use(
+    signerIdentity(
+      createNoopSigner(publicKey('11111111111111111111111111111111'))
+    )
+  ),
+  ...
+}))
+```
+
+## Why Zustand?
+
+Zustand is a global store that allows you to access the store state from both hooks and regular state fetching.
+
+By storing the umiInstance in **zustand** we can access it in both `.ts` and `.tsx` files while also having the state update via other providers and hooks such as `walletAdapter`.
+
+While it's normally easier to use the helper methods below to access umi you can also access the state methods manually by calling for the `umiStore` state yourself.
+
+When fetching the `umi` state directly without a helper it will only pickup the umi instance and not the latest signer. By design when the walletAdapter changes state the state of the `signer` in the `umiStore` is updated but **NOT** applied to the `umi` state. So you will need to also pull the latest `signer` state and apply it to `umi`. This behavior can be outlined in the `umiProvider.tsx` file. In contrast, the `umi` [helpers](#helpers) always pull a fresh instance of the `signer` state.
+
+```ts
+// umiProvider.tsx snippet
+useEffect(() => {
+  if (!wallet.publicKey) return
+  // When wallet.publicKey changes, update the signer in umiStore with the new wallet adapter.
+  umiStore.updateSigner(wallet as unknown as WalletAdapter)
+}, [wallet, umiStore])
+```
+
+### Access Umi in .tsx
+
+```ts
+// Pulls the umi state from the umiStore using hook.
+const umi = useUmiStore().umi
+const signer = useUmiStore().signer
+
+umi.use(signerIdentity(signer))
+```
+
+### Access Umi in .ts
+
+```ts
+// Pulls umi state from the umiStore.
+const umi = useUmiStore.getState().umi
+const signer = useUmiStore.getState().signer
+
+umi.use(signerIdentity(signer))
+```
+
+## Helpers
+
+Located in the `/lib/umi` folder there are some pre made helpers you can use to make your development easier.
+
+Umi helpers are split up into several areas which can be called in different scenarios.
+
+### Transaction Helpers
+
+#### sendAndConfirmWithWalletAdapter()
+
+Passing a transaction into `sendAndConfirmWithWalletAdapter()` will send the transaction while pulling the latest walletAdapter state from the zustand `umiStore` and will return the signature as a `string`. This can be accessed in both `.ts` and `.tsx` files.
+
+The function also provides and locks in the commitment level across `blockhash`, `send`, and `confirm` if provided. By default the commitment level of `confirmed` is used if no value is passed.
+
+There is also a `skipPreflight` flag that can be enabled if you need to debug failing transactions on chain. For more information about transactions errors you can view this guide [How to Diagnose Transaction Errors on Solana](/guides/general/how-to-diagnose-solana-transaction-errors).
+
+`sendAndConfirmWithWalletAdapter()` comes ready for priority fees via the `setComputeUnitPrice` instruction. These should reviewed and possibly adjusted or removed depending on your situation.
+
+```ts
+import useUmiStore from '@/store/useUmiStore'
+import { setComputeUnitPrice } from '@metaplex-foundation/mpl-toolbox'
+import { TransactionBuilder, signerIdentity } from '@metaplex-foundation/umi'
+import { base58 } from '@metaplex-foundation/umi/serializers'
+
+const defaultPriorityFee
+
+const sendAndConfirmWalletAdapter = async (
+  tx: TransactionBuilder,
+  settings?: {
+    commitment?: 'processed' | 'confirmed' | 'finalized'
+    skipPreflight?: boolean
+  }
+) => {
+  const umi = useUmiStore.getState().umi
+  const currentSigner = useUmiStore.getState().signer
+  console.log('currentSigner', currentSigner)
+  umi.use(signerIdentity(currentSigner!))
+
+  const blockhash = await umi.rpc.getLatestBlockhash({
+    commitment: settings?.commitment || 'confirmed',
+  })
+
+  const transactions = tx
+    // Set the priority fee for your transaction. Can be removed if unneeded.
+    .add(setComputeUnitPrice(umi, { microLamports: BigInt(100000) }))
+    .setBlockhash(blockhash)
+
+  const signedTx = await transactions.buildAndSign(umi)
+
+  const signature = await umi.rpc
+    .sendTransaction(signedTx, {
+      preflightCommitment: settings?.commitment || 'confirmed',
+      commitment: settings?.commitment || 'confirmed',
+      skipPreflight: settings?.skipPreflight || false,
+    })
+    .catch((err) => {
+      throw new Error(`Transaction failed: ${err}`)
+    })
+
+  const confirmation = await umi.rpc.confirmTransaction(signature, {
+    strategy: { type: 'blockhash', ...blockhash },
+    commitment: settings?.commitment || 'confirmed',
+  })
+  return {
+    signature: base58.deserialize(signature),
+    confirmation,
+  }
+}
+
+export default sendAndConfirmWalletAdapter
+```
+
+### Umi State
+
+#### umiWithCurrentWalletAdapter()
+
+`umiWithCurrentWalletAdapter` fetches the current umi state with the current walletAdapter state from the `umiStore`. This can then be used to create transactions or perform operations with umi that requires the current wallet adapter user.
+
+Can be used in both `.ts` and `.tsx` files
+
+```ts
+import useUmiStore from '@/store/useUmiStore'
+import { signerIdentity } from '@metaplex-foundation/umi'
+
+const umiWithCurrentWalletAdapter = () => {
+  // Because Zustand is used to store the Umi instance, the Umi instance can be accessed from the store
+  // in both hook and non-hook format. This is an example of a non-hook format that can be used in a ts file
+  // instead of a React component file.
+
+  const umi = useUmiStore.getState().umi
+  const currentWallet = useUmiStore.getState().signer
+  if (!currentWallet) throw new Error('No wallet selected')
+  return umi.use(signerIdentity(currentWallet))
+}
+export default umiWithCurrentWalletAdapter
+```
+
+#### umiWithSigner()
+
+`umiWithSigner` allows you to pass in a signer element (`generateSigner()`, `createNoopSigner()`) as an arg which is then assigned to the `umi` instance currently stored in state. This is useful for when you want an `umi` instance that uses a private key or `generatedSigner`/`createNoopSigner`.
+
+Can be used in both `.ts` and `.tsx` files
+
+```ts
+import useUmiStore from '@/store/useUmiStore'
+import { Signer, signerIdentity } from '@metaplex-foundation/umi'
+
+const umiWithSigner = (signer: Signer) => {
+  const umi = useUmiStore.getState().umi
+  if (!signer) throw new Error('No Signer selected')
+  return umi.use(signerIdentity(signer))
+}
+
+export default umiWithSigner
+```
+
+### Example Transaction Using Helpers
+
+Within the `/lib` folder you will find a `transferSol` example transaction that utilizes both the fetching of the umi state using `umiWithCurrentWalletAdapter()` and the sending of the generated transaction using `sendAndConfirmWithWalletAdapter()`.
+
+By pulling state from the umi store with `umiWithCurrentWalletAdapter()` if any of our transaction args require the `signer` type this will be automatically pulled from the umi instance which is generated with current user of `walletAdapter`. In this case the `from` account is determined by the current signer connected to umi (walletAdapter) and auto inferred in the transaction for us.
+
+By then sending transaction with `sendAndConfirmWithWalletAdapter` the signing process will use the `walletAdapter` and ask the current user to sign the transaction. The transaction will then be sent to the chain.
+
+```ts
+// Example of a function that transfers SOL from one account to another pulling umi
+// from the useUmiStore in a ts file which is not a React component.
+
+import { transferSol } from '@metaplex-foundation/mpl-toolbox'
+import umiWithCurrentWalletAdapter from './umi/umiWithCurrentWalletAdapter'
+import { publicKey, sol } from '@metaplex-foundation/umi'
+import sendAndConfirmWalletAdapter from './umi/sendAndConfirmWithWalletAdapter'
+
+// This function transfers SOL from the current wallet to a destination account and is callable
+// from any tsx/ts or component file in the project because of the zustand global store setup.
+
+const transferSolToDestination = async ({
+  destination,
+  amount,
+}: {
+  destination: string
+  amount: number
+}) => {
+  // Import Umi from `umiWithCurrentWalletAdapter`.
+  const umi = umiWithCurrentWalletAdapter()
+
+  // Create a transactionBuilder using the `transferSol` function from the mpl-toolbox.
+  // Umi by default will use the current signer (walletAdapter) to also set the `from` account.
+  const tx = transferSol(umi, {
+    destination: publicKey(destination),
+    amount: sol(amount),
+  })
+
+  // Use the sendAndConfirmWithWalletAdapter method to send the transaction.
+  // We do not need to pass the umi stance or wallet adapter as an argument because a
+  // fresh instance is fetched from the `umiStore` in the `sendAndConfirmWithWalletAdapter` function.
+  const res = await sendAndConfirmWalletAdapter(tx)
+}
+
+export default transferSolToDestination
+```

+ 221 - 0
src/pages/mpl-hybrid/create-escrow.md

@@ -0,0 +1,221 @@
+---
+title: Creating an MPL 404 Hybrid Escrow
+metaTitle: Creating an MPL 404 Hybrid Escrows | MPL-Hybrid
+description: Learn to create the MPL 404 Hybrid Escrow account that makes 404 swaps possible.
+---
+
+## Prerequisites
+
+- A MPL Core Collection - [Link](/core/guides/javascript/how-to-create-a-core-collection-with-javascript)
+- Core NFT Assets Minted to the Collection - [Link](/core/guides/javascript/how-to-create-a-core-nft-asset-with-javascript)
+- An SPL Token created with required token amount. - [Link](/guides/javascript/how-to-create-a-solana-token)
+- An online storage of sequential metadata JSON files at a consistent gateway/uri.
+
+Initializing the escrow is the essential step that links an NFT collection with a fungible token. Before starting this step, you should have ready a Core collection address, a fungible token mint address, and a range of off-chain metadata URIs using numerically named, sequential files. The need for Base URI string consistency will limit some off-chain metadata options. Note that the authority of the escrow needs to match the authority of the collection to perform metadata updates. Additionally, because the escrow is funded, there is no need to be the token authority which allows collections to be backed by existing memecoins or other fungible assets.
+
+## MPL-Hybrid Escrow Account Structure
+
+The MPL Hybrid Escrow is the heart of the program which stores all information regarding the project.
+
+{% totem %}
+{% totem-accordion title="On Chain MPL-404 Escrow Data Structure" %}
+
+The onchain account structure of an MPL-404 Escrow [Link](https://github.com/metaplex-foundation/mpl-hybrid/blob/main/programs/mpl-hybrid/src/state/escrow.rs)
+
+| Name           | Type   | Size | Description                                      |     |
+| -------------- | ------ | ---- | ------------------------------------------------ | --- |
+| collection     | Pubkey | 32   | The collection account                           |     |
+| authority      | Pubkey | 32   | The authority of the Escrow                      |     |
+| token          | Pubkey | 32   | The fungible token to be dispensed               |     |
+| fee_location   | Pubkey | 32   | The account to send token fees to                |     |
+| name           | String | 4    | The NFT name                                     |     |
+| uri            | String | 8    | The base uri for the NFT metadata                |     |
+| max            | u64    | 8    | The max index of NFTs that append to the uri     |     |
+| min            | u64    | 8    | The minimum index of NFTs that append to the uri |     |
+| amount         | u64    | 8    | The token cost to swap                           |     |
+| fee_amount     | u64    | 8    | The token fee for capturing the NFT              |     |
+| sol_fee_amount | u64    | 8    | The sol fee for capturing the NFT                |     |
+| count          | u64    | 8    | The total number of swaps                        |     |
+| path           | u16    | 1    | The onchain/off-chain metadata update path       |     |
+| bump           | u8     | 1    | The escrow bump                                  |     |
+
+{% /totem-accordion %}
+{% /totem %}
+
+## Create Escrow
+
+### Args
+
+#### name
+
+The name of your escrow. This data can be used to show the name of your escrow on a UI.
+
+```ts
+name: 'My Test Escrow'
+```
+
+#### uri
+
+This is the base uri for your metadata pool. This needs to be a static uri which also contains your metadata json files at sequential destination. i.e:
+
+```
+https://shdw-drive.genesysgo.net/.../0.json
+https://shdw-drive.genesysgo.net/.../1.json
+https://shdw-drive.genesysgo.net/.../2.json
+```
+
+```ts
+uri: 'https://shdw-drive.genesysgo.net/<bucket-id>/'
+```
+
+#### escrow
+
+The escrow address is a PDA of the two following seeds `["escrow", collectionAddress]`.
+
+```ts
+const collectionAddress = publicKey('11111111111111111111111111111111')
+
+const escrowAddress = umi.eddsa.findPda(MPL_HYBRID_PROGRAM_ID, [
+  string({ size: 'variable' }).serialize('escrow'),
+  publicKeySerializer().serialize(collectionAddress),
+])
+```
+
+#### collection
+
+The collection address being used in your MPL Hybrid 404 project.
+
+```ts
+collection: publicKey('11111111111111111111111111111111')
+```
+
+#### token
+
+The Token mint address that is being used in your MPL Hybrid 404 project.
+
+```ts
+token: publicKey('11111111111111111111111111111111')
+```
+
+#### feeLocation
+
+The wallet address which will be receiving the fees from the swaps.
+
+```ts
+feeLocation: publicKey('11111111111111111111111111111111')
+```
+
+#### feeAta
+
+The Token Account of the wallet that will be receiving the tokens.
+
+```ts
+feeAta: findAssociatedTokenPda(umi, {
+  mint: publicKey('111111111111111111111111111111111'),
+  owner: publicKey('22222222222222222222222222222222'),
+})
+```
+
+#### min and max
+
+The min and max represent the min and max indexes available in your metadata pool.
+
+```
+Lowest index: 0.json
+...
+Highest index: 4999.json
+```
+
+This would then translate into the min and max args.
+
+```ts
+min: 0,
+max: 4999
+```
+
+#### fees
+
+There are 3 separate fees that can be set.
+
+```ts
+// Amount of tokens to receive when swapping an NFT to tokens.
+// This value is in lamports and you will need to take into account
+// the number of decimals the token has. If the token has 5 decimals
+// and you wish to charge 1 whole token then feeAmount would be `100000`.
+
+amount: swapToTokenValueReceived,
+```
+
+```ts
+// Fee amount to pay when swapping Tokens to an NFT. This value is
+// in lamports and you will need to take into account the number of
+// decimals the token has. If the token has 5 decimals and you wish
+// to charge 1 whole token then feeAmount would be `100000`.
+
+feeAmount: swapToNftTokenFee,
+```
+
+```ts
+// Optional fee to pay when swapping from Tokens to NFT.
+// This is in lamports so you can use `sol()` to calculate
+// the lamports.
+
+solFeeAmount: sol(0.5).basisPoints,
+```
+
+#### path
+
+The `path` arg either enables of disables the metadata rerolling function on the mpl-hybrid program.
+
+```ts
+// Reroll metadata on swap 0 = true, 1 = false
+path: rerollEnabled,
+```
+
+#### associatedTokenProgram
+
+The `SPL_ASSOCIATED_TOKEN_PROGRAM_ID` can be pulled from the `mpl-toolbox` package.
+
+```ts
+import { SPL_ASSOCIATED_TOKEN_PROGRAM_ID } from @metaplex/mpl-toolbox
+```
+
+```ts
+// Associated Token Program ID
+associatedTokenProgram: SPL_ASSOCIATED_TOKEN_PROGRAM_ID,
+```
+
+### Code
+
+```ts
+const initTx = await initEscrowV1(umi, {
+  // Escrow Name
+  name: escrowName,
+  // Metadata Pool Base Uri
+  uri: baseMetadataPoolUri,
+  // Escrow Address based on "escrow" + collection address seeds
+  escrow: escrowAddress,
+  // Collection Address
+  collection: collectionAddress,
+  // Token Mint
+  token: tokenMint,
+  // Fee Wallet
+  feeLocation: feeWallet,
+  // Fee Token Account
+  feeAta: feeTokenAccount,
+  // Min index of NFTs in the pool
+  min: minAssetIndex,
+  // Max index of NFTs in the pool
+  max: maxAssetIndex,
+  // Amount of fungible token to swap to
+  amount: swapToTokenValueReceived,
+  // Fee amount to pay when swapping to NFTs
+  feeAmount: swapToNftTokenFee,
+  // Optional additional fee to pay when swapping to NFTs
+  solFeeAmount: sol(0.5).basisPoints,
+  // Reroll metadata on swap 0 = true, 1 = false
+  path: rerollEnabled,
+  // Associated Token Program ID
+  associatedTokenProgram: SPL_ASSOCIATED_TOKEN_PROGRAM_ID,
+}).sendAndConfirm(umi)
+```

+ 51 - 0
src/pages/mpl-hybrid/fetch-escrow.md

@@ -0,0 +1,51 @@
+---
+title: Fetching an MPL 404 Hybrid Escrow
+metaTitle: Fetching an MPL 404 Hybrid Escrow | MPL-Hybrid
+description: Learn to fetch an MPL 404 Hybrid Escrow configuration.
+---
+
+## Fetching Escrow Configuration
+
+To fetch an Escrow configuration account you can use the `fetchEscrowV1` function.
+
+```ts
+const escrowAddress = publicKey('11111111111111111111111111111111')
+
+const escrow = await fetchEscrowV1(umi, escrowAddress)
+```
+
+## Returned Data Format
+
+The following object is an example of the returned data from `fetchEscrowV1`.
+
+```ts
+{
+    publicKey: '11111111111111111111111111111111',
+    header: {
+      executable: false,
+      owner: 'MPL4o4wMzndgh8T1NVDxELQCj5UQfYTYEkabX3wNKtb',
+      lamports: [Object],
+      rentEpoch: 18446744073709551616n,
+      exists: true
+    },
+    discriminator: [
+       26,  90, 193, 218,
+      188, 251, 139, 211
+    ],
+    collection: '11111111111111111111111111111111',
+    authority: '11111111111111111111111111111111',
+    token: '11111111111111111111111111111111',
+    feeLocation: '11111111111111111111111111111111',
+    name: 'My Escrow',
+    uri: 'https://mybaseuri.net/',
+    max: 100n,
+    min: 0n,
+    amount: 1000000000n,
+    feeAmount: 2n,
+    solFeeAmount: 0n,
+    count: 1n,
+    path: 0,
+    bump: 255
+  }
+
+```

+ 46 - 0
src/pages/mpl-hybrid/funding-escrow.md

@@ -0,0 +1,46 @@
+---
+title: Funding the MPL Hybrid 404 Escrow
+metaTitle: Funding the MPL Hybrid 404 Escrow | MPL-Hybrid
+description: Learn to fund the MPL 404 Hybrid Escrow account with SPL Tokens that makes 404 swaps possible.
+---
+
+Before making your smart-swap live you will need to fund the escrow. Typically if a project wants to ensure the escrow always stays funded, they start by releasing all of the NFTs or tokens and then placing all of the other assets in the escrow. This ensures that every outstanding asset is "backed" by the counter-asset in the escrow. Because the Escrow is a PDA, loading it via wallets is not widely supported. You can use the below code to transfer assets into your escrow.
+
+To fund your escrow with your Token you will need to send that token to the **escrows token account**.
+
+```ts
+// Address of your escrow configuration.
+const escrowConfigurationAddress = publicKey('11111111111111111111111111111111')
+// Address of the SPL token.
+const tokenMint = publicKey('22222222222222222222222222222222')
+
+// Generate the Token Account PDA from the funding wallet.
+const sourceTokenAccountPda = findAssociatedTokenPda(umi, {
+  owner: umi.identity.publicKey,
+  mint: tokenMint,
+})
+
+// Generate the Token Account PDA for the escrow destination.
+const escrowTokenAccountPda = findAssociatedTokenPda(umi, {
+  owner: escrowConfigurationAddress,
+  mint: tokenMint,
+})
+
+// Execute transfer of tokens while also checking if the
+// destination token account exists, if not, create it.
+await createTokenIfMissing(umi, {
+  mint: tokenMint,
+  owner: escrowConfigurationAddress,
+  token: escrowTokenAccountPda,
+  payer: umi.identity,
+})
+  .add(
+    transferTokens(umi, {
+      source: sourceTokenAccountPda,
+      destination: escrowTokenAccountPda,
+      // amount is calculated in lamports and decimals.
+      amount: 100000,
+    })
+  )
+  .sendAndConfirm(umi)
+```

+ 0 - 13
src/pages/mpl-hybrid/getting-started/index.md

@@ -1,13 +0,0 @@
----
-title: Getting Started
-metaTitle: Getting Started | MPL-Hybrid
-description: Get started with the MPL-Hybrid Smart Escrow program.
----
-
-Select the language / library you want to use below to get started with the new Metaplex Hybrid Program.
-
-{% quick-links %}
-
-{% quick-link title="JavaScript" icon="JavaScript" href="/mpl-hybrid/getting-started/js" description="Get started with our JavaScript library based on the Umi framework." /%}
-
-{% /quick-links %}

+ 0 - 35
src/pages/mpl-hybrid/getting-started/js.md

@@ -1,35 +0,0 @@
----
-title: Getting Started using MPL-Hybrid
-metaTitle: Javascript SDK | MPL-Hybrid
-description: Get started with MPL-Hybrid using JavaScript SDK.
----
-
-Metaplex provides a JavaScript library that can be used to interact with the MPL-Hybrid program. Thanks to the [Umi framework](https://github.com/metaplex-foundation/mpl-hybrid), it ships without many opinionated dependencies thus providing a lightweight library that can be used in any JavaScript project.
-
-To get started, you'll need to [install the Umi framework](https://github.com/metaplex-foundation/umi/blob/main/docs/installation.md) and the MPL-Hybrid JavaScript library.
-
-```sh
-npm install @metaplex-foundation/mpl-hybrid
-```
-
-Next, you should create your `Umi` instance and install the `mplHybrid` plugin like so.
-
-```ts
-import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
-import { mplHybrid } from '@metaplex-foundation/mpl-hybrid'
-
-// Use the RPC endpoint of your choice.
-const umi = createUmi('http://127.0.0.1:8899').use(mplHybrid())
-```
-
-From here you can decide which wallet connection method to use:
-
-- [Local keypair](/umi/connecting-to-umi#connecting-w-a-secret-key)
-- [Solana Wallet Adapter](/umi/connecting-to-umi#connecting-w-wallet-adapter).
-
-🔗 **Helpful Links:**
-
-- [Umi Framework](https://github.com/metaplex-foundation/umi)
-- [GitHub Repository](https://github.com/metaplex-foundation/mpl-hybrid)
-- [NPM Package](https://www.npmjs.com/package/@metaplex-foundation/mpl-hybrid)
-- [API References](https://mpl-hybrid.typedoc.metaplex.com/)

+ 592 - 0
src/pages/mpl-hybrid/guides/create-your-first-hybrid-collection.md

@@ -0,0 +1,592 @@
+---
+title: Create your First Hybrid Collection
+metaTitle: Create your First Hybrid Collection | Hybrid Guides
+description: Learn how to create an hybrid collection, fully end-to-end!.
+# remember to update dates also in /components/guides/index.js
+created: '09-17-2024'
+updated: '09-17-2024'
+---
+
+This guide will demonstrate how to create an **Hybrid Collection** fully end-to-end. Starting from how to create all the assets needed, to how to create the escrow and setting up all the parameters to swap from Fungible Token to Non Fungible Token and viceversa!
+
+{% callout title="What is MPL-Hybrid?" %}
+
+MPL-Hybrid is a new model for digital assets, web3 games, and onchain communities. At the core of the model is a swap program that trades a fixed number of fungible assets for a non-fungible asset and vice versa. 
+
+{% /callout %}
+
+## Prerequisite
+
+- Code Editor of your choice (recommended **Visual Studio Code**)
+- Node **18.x.x** or above.
+
+## Initial Setup
+
+This guide will teach you how to create an Hybrid Collection using Javascript! You may need to modify and move functions around to suit your needs.
+
+### Initializing the Project
+
+Start by initializing a new project (optional) with the package manager of your choice (npm, yarn, pnpm, bun) and fill in required details when prompted.
+
+```js
+npm init
+```
+
+### Required Packages
+
+Install the required packages for this guide.
+
+{% packagesUsed packages=["umi", "umiDefaults", "core", "@metaplex-foundation/mpl-hybrid", "tokenMetadata" ] type="npm" /%}
+
+```js
+npm i @metaplex-foundation/umi
+```
+
+```js
+npm i @metaplex-foundation/umi-bundle-defaults
+```
+
+```js
+npm i @metaplex-foundation/mpl-core
+```
+
+```js
+npm i @metaplex-foundation/mpl-hybrid
+```
+
+```js
+npm i @metaplex-foundation/mpl-token-metadata
+```
+
+## Preparation
+
+Before setting up the escrow for the MPL-Hybrid program, which facilitates the swapping of fungible tokens for non-fungible tokens (NFTs) and vice versa, you’ll need to have both a collection of Core NFTs and fungible tokens already minted.
+
+If you’re missing any of these prerequisites, don’t worry! We’ll give you all the resources you need to go through each step.
+
+**Note**: To work, the escrow will need to be funded with NFTs, fungible tokens, or a combination of both. The simplest way to maintain balance in the escrow is to fill it entirely with one type of asset while distributing the other!
+
+### Creating the NFT Collection
+
+To utilize the metadata randomization feature in the MPL-Hybrid program, the off-chain metadata URIs need to follow a consistent, incremental structure. For this, we use the [path manifest](https://cookbook.arweave.dev/concepts/manifests.html) feature from Arweave in combination with the Turbo SDK.
+
+Manifest allows multiple transactions to be linked under a single base transaction ID and assigned human-readable file names, like this:
+- https://arweave.net/manifestID/0.json
+- https://arweave.net/manifestID/1.json
+- ...
+- https://arweave.net/manifestID/9999.json
+
+If you're unfamiliar with creating deterministic URIs, you can follow [this guide](/guides/general/create-deterministic-metadata-with-turbo) for a detailed walkthrough. Additionally, you can find instructions on creating a [collection](/core/guides/javascript/how-to-create-a-core-collection-with-javascript) and the [assets](/core/guides/javascript/how-to-create-a-core-nft-asset-with-javascript) required for the Hybrid program to function.
+
+**Note**: Currently, the MPL-Hybrid program randomly picks a number between the min and max URI index provided and does not check to see if the URI is already used. As such, swapping suffers from the [Birthday Paradox](https://betterexplained.com/articles/understanding-the-birthday-paradox/). In order for projects to benefit from sufficient swap randomization, we recommend preparing and uploading a minimum of 250k asset metadata that can be randomly picked from. The more available potential assets the better!
+
+### Creating the Fungible Tokens
+
+The MPL-Hybrid escrow requires an associated fungible token that can be used to redeem or pay for the release of an NFT. This can be an existing token that's already minted and circulating, or entirely a new one! 
+
+If you’re unfamiliar with creating a token, you can follow [this guide](/guides/javascript/how-to-create-a-solana-token) to learn how to mint your own fungible token on Solana.
+
+## Creating the Escrow
+
+**After creating both the NFT Collection and Tokens, we're finally ready to create the Escrow and start swapping!**
+
+But before jumping in the relevant information about MPL-Hybrid, it's a good idea to learn how to set up your Umi instance since we're going to do that multiple time during the guide.
+
+### Setting up Umi
+
+While setting up Umi you can use or generate keypairs/wallets from different sources. You create a new wallet for testing, import an existing wallet from the filesystem, or use `walletAdapter` if you are creating a website/dApp.  
+
+**Note**: For this example we're going to set up Umi with a `generatedSigner()` but you can find all the possible setup down below!
+
+{% totem %}
+
+{% totem-accordion title="With a New Wallet" %}
+
+```ts
+const umi = createUmi('https://api.devnet.solana.com')
+
+const signer = generateSigner(umi)
+
+umi.use(signerIdentity(signer))
+
+// This will airdrop SOL on devnet only for testing.
+console.log('Airdropping 1 SOL to identity')
+umi.rpc.airdrop(umi.identity.publicKey, sol(1));
+```
+
+{% /totem-accordion %}
+
+{% totem-accordion title="With an Existing Wallet" %}
+
+```ts
+const umi = createUmi('https://api.devnet.solana.com')
+
+// You will need to us fs and navigate the filesystem to
+// load the wallet you wish to use via relative pathing.
+const walletFile = fs.readFileSync('./keypair.json')
+  
+
+// Convert your walletFile onto a keypair.
+let keypair = umi.eddsa.createKeypairFromSecretKey(new Uint8Array(walletFile));
+
+// Load the keypair into umi.
+umi.use(keypairIdentity(keypair));
+```
+
+{% /totem-accordion %}
+
+{% totem-accordion title="With the Wallet Adapter" %}
+
+```ts
+import { walletAdapterIdentity } from '@metaplex-foundation/umi-signer-wallet-adapters'
+import { useWallet } from '@solana/wallet-adapter-react'
+
+const wallet = useWallet()
+
+const umi = createUmi('https://api.devnet.solana.com')
+// Register Wallet Adapter to Umi
+.use(walletAdapterIdentity(wallet))
+```
+
+{% /totem-accordion %}
+
+{% /totem %}
+
+**Note**: The `walletAdapter` section provides only the code needed to connect it to Umi, assuming you've already installed and set up the `walletAdapter`. For a comprehensive guide, refer to [this](https://github.com/anza-xyz/wallet-adapter/blob/master/APP.md)
+
+### Setup the Parameters
+
+After setting up your Umi instance, the next step is to configure the parameters required for the MPL-Hybrid Escrow.
+
+We'll begin by defining the general settings for the escrow contract:
+
+```javascript
+// Escrow Settings - Change these to your needs
+const name = "MPL-404 Hybrid Escrow";                       
+const uri = "https://arweave.net/manifestId";               
+const max = 15;                                             
+const min = 0;                                              
+const path = 0;                                             
+```
+
+| Parameter     | Description                                                                 |
+| ------------- | --------------------------------------------------------------------------- |
+| **Name**      | The name of the escrow contract (e.g., "MPL-404 Hybrid Escrow").             |
+| **URI**       | The base URI of the NFT collection. This should follow the deterministic metadata structure. |
+| **Max & Min** | These define the range of the deterministic URIs for the collection's metadata. |
+| **Path**      | Choose between two paths: `0` to update the NFT metadata on swap, or `1` to keep the metadata unchanged after a swap. |
+
+Next, we configure the key accounts needed for the escrow:
+
+```javascript
+// Escrow Accounts - Change these to your needs
+const collection = publicKey('<YOUR-COLLECTION-ADDRESS>'); 
+const token = publicKey('<YOUR-TOKEN-ADDRESS>');           
+const feeLocation = publicKey('<YOUR-FEE-ADDRESS>');        
+const escrow = umi.eddsa.findPda(MPL_HYBRID_PROGRAM_ID, [
+    string({ size: 'variable' }).serialize('escrow'),
+    publicKeySerializer().serialize(collection),
+]);                                                        
+```
+
+| Account           | Description                                                                 |
+| ----------------- | --------------------------------------------------------------------------- |
+| **Collection**    | The collection being swapped to or from. This is the address of the NFT collection. |
+| **Token**         | The token being swapped to or from. This is the address of the fungible token. |
+| **Fee Location**  | The address where any fees from the swaps will be sent. |
+| **Escrow**        | The derived escrow account, which is responsible for holding the NFTs and tokens during the swap process. |
+
+Lastly, we define the token-related parameters and create a helper function, `addZeros()`, to adjust token amounts for decimals:
+
+```javascript
+// Token Swap Settings - Change these to your needs
+const tokenDecimals = 6;                                    
+const amount = addZeros(100, tokenDecimals);                
+const feeAmount = addZeros(1, tokenDecimals);               
+const solFeeAmount = addZeros(0, 9);                       
+
+// Function that adds zeros to a number, needed for adding the correct amount of decimals
+function addZeros(num: number, numZeros: number): number {
+  return num * Math.pow(10, numZeros)
+}
+```
+
+| Parameter         | Description                                                                 |
+| ----------------- | --------------------------------------------------------------------------- |
+| **Amount**         | The amount of tokens the user will receive during the swap, adjusted for decimals. |
+| **Fee Amount**     | The amount of the token fee the user will pay when swapping to an NFT.       |
+| **Sol Fee Amount** | An additional fee (in SOL) that will be charged when swapping to NFTs, adjusted for Solana's 9 decimal places. |
+
+### Initialize the Escrow 
+
+We can now initialize the escrow using the `initEscrowV1()` method, passing in all the parameters and variables we’ve set up. This will create your own MPL-Hybrid Escrow.
+
+```javascript
+const initEscrowTx = await initEscrowV1(umi, {
+  name,
+  uri,
+  max,
+  min,
+  path,
+  escrow,
+  collection,
+  token,
+  feeLocation,
+  amount,
+  feeAmount,
+  solFeeAmount,
+}).sendAndConfirm(umi);
+
+const signature = base58.deserialize(initEscrowTx.signature)[0]
+console.log(`Escrow created! https://explorer.solana.com/tx/${signature}?cluster=devnet`)
+```
+
+**Note**: As we said before, simply creating the escrow won’t make it "ready" for swapping. You’ll need to populate the escrow with either NFTs or tokens (or both). **Here’s how**:
+
+{% totem %}
+
+{% totem-accordion title="Send Assets to the Escrow" %}
+
+```javascript
+import { transfer } from '@metaplex-foundation/mpl-core'
+
+// Derive the Escrow
+const escrow = umi.eddsa.findPda(MPL_HYBRID_PROGRAM_ID, [
+  string({ size: 'variable' }).serialize('escrow'),
+  publicKeySerializer().serialize(collection),
+])[0];
+
+// Transfer Asset to it
+const transferAssetTx = await transfer(umi, {
+  asset,
+  collection,
+  newOwner: escrow
+}).sendAndConfirm(umi);
+```
+
+{% /totem-accordion %}
+
+{% totem-accordion title="Send Fungible Tokens to the Escrow" %}
+
+```javascript
+import { transactionBuilder } from '@metaplex-foundation/umi'
+import { createTokenIfMissing, transferTokens } from '@metaplex-foundation/mpl-toolbox'
+
+// Derive the Escrow
+const escrow = umi.eddsa.findPda(MPL_HYBRID_PROGRAM_ID, [
+  string({ size: 'variable' }).serialize('escrow'),
+  publicKeySerializer().serialize(collection),
+])[0];
+
+// Transfer Fungible Tokens to it (after creating the ATA if needed)
+const transferTokenTx = await transactionBuilder().add(
+  createTokenIfMissing(umi, { 
+      mint: token, 
+      owner: escrow 
+  })
+).add(
+  transferTokens(umi, {
+      source: findAssociatedTokenPda(umi, { mint: token, owner: umi.identity.publicKey }),
+      destination: findAssociatedTokenPda(umi, { mint: token, owner: escrow }),
+      amount,
+  })
+).sendAndConfirm(umi)
+```
+{% /totem-accordion %}
+
+{% /totem %}
+
+### Full Code Example
+
+If you want to simply copy and paste the full code for creating the escrow, here it is! 
+
+{% totem %}
+
+{% totem-accordion title="Full Code Example" %}
+
+```javascript
+import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
+import { publicKey, signerIdentity, generateSigner, sol } from '@metaplex-foundation/umi'
+import { mplHybrid, MPL_HYBRID_PROGRAM_ID, initEscrowV1 } from '@metaplex-foundation/mpl-hybrid'
+import { mplTokenMetadata } from '@metaplex-foundation/mpl-token-metadata'
+import { string, base58, publicKey as publicKeySerializer } from '@metaplex-foundation/umi/serializers'
+
+(async () => {
+  /// Step 1: Setup Umi
+  const umi = createUmi('https://api.devnet.solana.com')
+    .use(mplHybrid())
+    .use(mplTokenMetadata())
+
+  let signer = generateSigner(umi);
+
+  umi.use(signerIdentity(signer)).rpc.airdrop(umi.identity.publicKey, sol(1));
+
+  /// Step 2: Setup the Escrow
+
+  // Escrow Settings - Change these to your needs
+  const name = "MPL-404 Hybrid Escrow";                       // The name of the escrow
+  const uri = "https://arweave.net/manifestId";               // The base URI of the collection
+  const max = 15;                                             // The max URI
+  const min = 0;                                              // The min URI
+  const path = 0;                                             // 0: Update Nft on Swap, 1: Do not update Nft on Swap
+
+  // Escrow Accounts - Change these to your needs
+  const collection = publicKey('<YOUR-COLLECTION-ADDRESS>');  // The collection we are swapping to/from
+  const token = publicKey('<YOUR-TOKEN-ADDRESS>');            // The token we are swapping to/from
+  const feeLocation = publicKey('<YOUR-FEE-ADDRESS>');        // The address where the fees will be sent
+  const escrow = umi.eddsa.findPda(MPL_HYBRID_PROGRAM_ID, [
+    string({ size: 'variable' }).serialize('escrow'),
+    publicKeySerializer().serialize(collection),
+  ]);                                                         // The derived escrow account
+
+  // Token Swap Settings - Change these to your needs
+  const tokenDecimals = 6;                                    // The decimals of the token
+  const amount = addZeros(100, tokenDecimals);                // The amount the user will receive when swapping
+  const feeAmount = addZeros(1, tokenDecimals);               // The amount the user will pay as fee when swapping to NFT
+  const solFeeAmount = addZeros(0, 9);                        // Additional fee to pay when swapping to NFTs (Sol has 9 decimals)
+
+  /// Step 3: Create the Escrow
+  const initEscrowTx = await initEscrowV1(umi, {
+    name,
+    uri,
+    max,
+    min,
+    path,
+    escrow,
+    collection,
+    token,
+    feeLocation,
+    amount,
+    feeAmount,
+    solFeeAmount,
+  }).sendAndConfirm(umi);
+
+  const signature = base58.deserialize(initEscrowTx.signature)[0]
+  console.log(`Escrow created! https://explorer.solana.com/tx/${signature}?cluster=devnet`)
+})()
+
+// Function that adds zeros to a number, needed for adding the correct amount of decimals
+function addZeros(num: number, numZeros: number): number {
+  return num * Math.pow(10, numZeros)
+}
+```
+
+{% /totem-accordion %}
+
+{% /totem %}
+
+## Capture & Release
+
+### Setup the Accounts 
+
+After setting up Umi (as we did in the [previous section](#setting-up-umi)), the next step is configuring the accounts needed for the `Capture` & `Release` process. These accounts will feel familiar since they’re similar to what we used earlier and they are the same for both instructions:
+
+```javascript
+// Step 2: Escrow Accounts - Change these to your needs
+const collection = publicKey('<YOUR-COLLECTION-ADDRESS>');
+const token = publicKey('<YOUR-TOKEN-ADDRESS>');
+const feeProjectAccount = publicKey('<YOUR-FEE-ADDRESS>');
+const escrow = umi.eddsa.findPda(MPL_HYBRID_PROGRAM_ID, [
+    string({ size: 'variable' }).serialize('escrow'),
+    publicKeySerializer().serialize(collection),
+]);
+```
+
+**Note**: The `feeProjectAccount` is the same as the `feeLocation` field from the last script.
+
+### Choose the Asset to Capture/Release
+
+How you choose the asset to caputre and release, depends on the path you selected when creating the Escrow:
+- **Path 0**: If the path is set to `0`, the NFT metadata will be updated during the swap, so you can just grab a random asset from the escrow since this will not matter.
+- **Path 1**: If the path is set to `1`, the NFT metadata stays the same after the swap, so you could let the user choose which specific NFT they want to swap into.
+
+**For Capture**
+
+If you're capturing an NFT, here's how you can pick a random asset owned by the escrow:
+
+```javascript
+// Fetch all the assets in the collection
+const assetsListByCollection = await fetchAssetsByCollection(umi, collection, {
+    skipDerivePlugins: false,
+})
+
+// Find the assets owned by the escrow
+const asset = assetsListByCollection.filter(
+    (a) => a.owner === publicKey(escrow)
+)[0].publicKey
+```
+
+**For Release**
+
+If you're releasing an NFT, it’s generally up to the user to choose which one they want to release. But for this example, we’ll just select a random asset owned by the user:
+
+```javascript
+// Fetch all the assets in the collection
+const assetsListByCollection = await fetchAssetsByCollection(umi, collection, {
+    skipDerivePlugins: false,
+})
+
+// Usually the user choose what to exchange
+const asset = assetsListByCollection.filter(
+    (a) => a.owner === umi.identity.publicKey
+)[0].publicKey
+```
+
+### Capture (Fungible to Non-Fungible)
+
+Now, let’s finally talk about the Capture instruction. This is the process where you swap fungible tokens for an NFT (The amount of tokens needed for the swap is set at escrow creation).
+
+```javascript
+// Capture an NFT by swapping fungible tokens
+const captureTx = await captureV1(umi, {
+  owner: umi.identity.publicKey,
+  escrow,
+  asset,
+  collection,
+  token,
+  feeProjectAccount,
+  amount,
+}).sendAndConfirm(umi);
+
+const signature = base58.deserialize(captureTx.signature)[0];
+console.log(`Captured! Check it out: https://explorer.solana.com/tx/${signature}?cluster=devnet`);
+```
+
+### Release (Non-Fungible to Fungible)
+
+Releasing is the opposite of capturing—here you swap an NFT for fungible tokens:
+
+```javascript
+// Release an NFT and receive fungible tokens
+const releaseTx = await releaseV1(umi, {
+  owner: umi.payer,
+  escrow,
+  asset,
+  collection,
+  token,
+  feeProjectAccount,
+}).sendAndConfirm(umi);
+
+const signature = base58.deserialize(releaseTx.signature)[0];
+console.log(`Released! Check it out: https://explorer.solana.com/tx/${signature}?cluster=devnet`);
+```
+
+### Full Code Example
+
+Here's the full code for `Capture` and `Release`
+
+{% totem %}
+
+{% totem-accordion title="Capture" %}
+
+```javascript
+import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
+import { generateSigner, signerIdentity, publicKey, sol } from '@metaplex-foundation/umi'
+import { mplHybrid, MPL_HYBRID_PROGRAM_ID, captureV1 } from '@metaplex-foundation/mpl-hybrid'
+import { mplTokenMetadata } from '@metaplex-foundation/mpl-token-metadata'
+import { base58, string, publicKey as publicKeySerializer } from '@metaplex-foundation/umi/serializers'
+import { fetchAssetsByCollection } from '@metaplex-foundation/mpl-core'
+
+(async () => {
+  /// Step 1: Setup Umi
+  const umi = createUmi('https://api.devnet.solana.com')
+    .use(mplHybrid())
+    .use(mplTokenMetadata())
+
+  let signer = generateSigner(umi);
+
+  umi.use(signerIdentity(signer)).rpc.airdrop(umi.identity.publicKey, sol(1));
+
+  // Step 2: Escrow Accounts - Change these to your needs
+  const collection = publicKey('<YOUR-COLLECTION-ADDRESS>');  // The collection we are swapping to/from
+  const token = publicKey('<YOUR-TOKEN-ADDRESS>');            // The token we are swapping to/from
+  const feeProjectAccount = publicKey('<YOUR-FEE-ADDRESS>');  // The address where the fees will be sent
+  const escrow = umi.eddsa.findPda(MPL_HYBRID_PROGRAM_ID, [
+    string({ size: 'variable' }).serialize('escrow'),
+    publicKeySerializer().serialize(collection),
+  ]);                    
+
+  // Fetch all the assets in the collection
+  const assetsListByCollection = await fetchAssetsByCollection(umi, collection, {
+    skipDerivePlugins: false,
+  })
+
+  // Find the assets owned by the escrow
+  const asset = assetsListByCollection.filter(
+    (a) => a.owner === publicKey(escrow)
+  )[0].publicKey
+
+  /// Step 3: "Capture" (Swap from Fungible to Non-Fungible) the Asset
+  const captureTx = await captureV1(umi, {
+    owner: umi.payer,
+    escrow,
+    asset,
+    collection,
+    token,
+    feeProjectAccount,
+  }).sendAndConfirm(umi)
+  const signature = base58.deserialize(captureTx.signature)[0]
+  console.log(`Captured! https://explorer.solana.com/tx/${signature}?cluster=devnet`)})();
+```
+
+{% /totem-accordion %}
+
+{% totem-accordion title="Release" %}
+
+```javascript
+import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
+import { generateSigner, signerIdentity, publicKey, sol } from '@metaplex-foundation/umi'
+import { mplHybrid, MPL_HYBRID_PROGRAM_ID, releaseV1 } from '@metaplex-foundation/mpl-hybrid'
+import { mplTokenMetadata } from '@metaplex-foundation/mpl-token-metadata'
+import { base58, string, publicKey as publicKeySerializer } from '@metaplex-foundation/umi/serializers'
+import { fetchAssetsByCollection } from '@metaplex-foundation/mpl-core'
+
+import walletFile from "/Users/leo/.config/solana/id.json";
+
+(async () => {
+  /// Step 1: Setup Umi
+  const umi = createUmi('https://api.devnet.solana.com')
+    .use(mplHybrid())
+    .use(mplTokenMetadata())
+
+  let signer = generateSigner(umi);
+
+  umi.use(signerIdentity(signer)).rpc.airdrop(umi.identity.publicKey, sol(1));
+
+  // Step 2: Escrow Accounts - Change these to your needs
+  const collection = publicKey('<YOUR-COLLECTION-ADDRESS>');  // The collection we are swapping to/from
+  const token = publicKey('<YOUR-TOKEN-ADDRESS>');            // The token we are swapping to/from
+  const feeProjectAccount = publicKey('<YOUR-FEE-ADDRESS>');  // The address where the fees will be sent
+  const escrow = umi.eddsa.findPda(MPL_HYBRID_PROGRAM_ID, [
+    string({ size: 'variable' }).serialize('escrow'),
+    publicKeySerializer().serialize(collection),
+  ]);                  
+
+  // Fetch all the assets in the collection
+  const assetsListByCollection = await fetchAssetsByCollection(umi, collection, {
+    skipDerivePlugins: false,
+  })
+
+  // Usually the user choose what to exchange
+  const asset = assetsListByCollection.filter(
+    (a) => a.owner === umi.identity.publicKey
+  )[0].publicKey
+
+  /// Step 3: "Capture" (Swap from Fungible to Non-Fungible) the Asset
+  const releaseTx = await releaseV1(umi, {
+    owner: umi.payer,
+    escrow,
+    asset,
+    collection,
+    token,
+    feeProjectAccount,
+  }).sendAndConfirm(umi)
+  
+  const signature = base58.deserialize(releaseTx.signature)[0]
+  console.log(`Released! https://explorer.solana.com/tx/${signature}?cluster=devnet`)
+})();
+```
+
+{% /totem-accordion %}
+
+{% /totem %}

+ 13 - 0
src/pages/mpl-hybrid/guides/index.md

@@ -0,0 +1,13 @@
+---
+title: Guides
+metaTitle: Guides | Hybrid
+description: A list of guides and tutorials for the Metaplex Hybrid standard on the Solana blockchain.
+---
+
+The following Guides for Mpl Hybrid are currently available:
+
+{% quick-links %}
+
+{% quick-link title="Create your first Hybrid Collection" icon="CodeBracketSquare" href="/mpl-hybrid/guides/create-your-first-hybrid-collection" description="Learn how to create an hybrid collection, fully end-to-end!" /%}
+
+{% /quick-links %}

+ 13 - 0
src/pages/mpl-hybrid/sdk/index.md

@@ -0,0 +1,13 @@
+---
+title: MPL-Hybrid SDKs
+metaTitle: SDKs | MPL-Hybrid
+description: View the available SDKs for the MPL-Hybrid Metaplex program.
+---
+
+Below are the available SDKs for the MPL-Hybrid 404 program that guide you through initial client setup.
+
+{% quick-links %}
+
+{% quick-link title="JavaScript SDK" icon="JavaScript" href="/mpl-hybrid/sdk/javascript" description="Get started with our MPL-Hybrid 404 JavaScript library based on the Umi framework." /%}
+
+{% /quick-links %}

+ 42 - 0
src/pages/mpl-hybrid/sdk/javascript.md

@@ -0,0 +1,42 @@
+---
+title: MPL-Hybrid Javascript SDK
+metaTitle: Javascript SDK | MPL-Hybrid
+description: Learn how to set up your project to run the MPL-Hybrid Javascript SDK.
+---
+
+Metaplex provides a JavaScript library that can be used to interact with the MPL-Hybrid 404 program. Thanks to the [Umi Framework](/umi), it ships without many opinionated dependencies thus providing a lightweight library that can be used in any JavaScript project.
+
+To get started, you'll need to [install the Umi framework](/umi/getting-started) and the MPL-Hybrid JavaScript library.
+
+## Installation
+
+Installation can be executed with any of the JS package managers, npm, yarn, bun etc...
+
+```sh
+npm install @metaplex-foundation/mpl-hybrid
+```
+
+## Umi Setup
+
+
+An `umi` instance is required to interact with the Metaplex Javascript SDKs. If you haven't set up and configured an `umi` instance yet then you can get checkout the [Umi Getting Started](/umi/getting-started) page.
+
+
+During the initialization of the `umi` instance you can add the mpl-hybrid package to `umi` using
+
+```js
+.use(mplHybrid())
+```
+
+You can add the `mplHybrid()` package anywhere in your umi instance creation.
+```ts
+import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
+import { mplHybrid } from '@metaplex-foundation/mpl-hybrid'
+
+// Use the RPC endpoint of your choice.
+const umi = createUmi('http://api.devenet.solana.com')
+... // additional umi settings and packages
+.use(mplHybrid())
+```
+
+From here your `umi` instance will have access to the mpl-hybrid package.

+ 28 - 0
src/pages/mpl-hybrid/swapping-nfts-to-tokens.md

@@ -0,0 +1,28 @@
+---
+title: Swapping an NFT to Tokens in MPL Hybrid
+metaTitle: Swapping an NFT to Tokens | MPL-Hybrid
+description: Learn to write a swap function so users can swap their NFT into Tokens in the MPL-Hybrid Program.
+---
+
+The action of swapping Tokens in your possession to an NFT held in the escrow in the MPL-Hybrid program is called a `capture`.
+
+
+
+## Swapping an NFT
+
+```ts
+await releaseV1(umi, {
+    // The owner of the asset being swapped.
+    owner: umi.identity,
+    // The escrow configuration address.
+    escrow: publicKey("11111111111111111111111111111111"),
+    // The Asset that will be swapped for SPL Tokens.
+    asset: publicKey("22222222222222222222222222222222"),
+    // The collection assigned to the escrow configuration.
+    collection: publicKey("33333333333333333333333333333333"),
+    // The fee wallet address.
+    feeProjectAccount: publicKey("44444444444444444444444444444444"),
+    // The Token Account of the Wallet.
+    token: publicKey("55555555555555555555555555555555"),
+  }).sendAndConfirm(umi);
+```

+ 28 - 0
src/pages/mpl-hybrid/swapping-tokens-to-nfts.md

@@ -0,0 +1,28 @@
+---
+title: Swapping Tokens to NFTs
+metaTitle: Swapping Tokens to NFTs with MPL-Hybrid 404 | MPL-Hybrid
+description: Learn to your SPL tokens to an NFT in the MPL-Hybrid Program.
+---
+
+The action of swapping Tokens for an NFT in the escrow of the MPL-Hybrid program is called a `capture`. The process involves the user capturing an NFT from the escrow in exchange for a set amount of tokens.
+
+If reroll (path) is enabled in the escrow configuration then the metadata index written to the NFT will be picked at random from the pool of available indexes `min`, `max`.
+
+## Swapping Tokens
+
+```ts
+await captureV1(umi, {
+  // The owner of the asset being swapped.
+  owner: umi.identity,
+  // The escrow configuration address.
+  escrow: publicKey('11111111111111111111111111111111'),
+  // The Asset that will be swapped for SPL Tokens.
+  asset: publicKey('22222222222222222222222222222222'),
+  // The collection assigned to the escrow configuration.
+  collection: publicKey('33333333333333333333333333333333'),
+  // The fee wallet address.
+  feeProjectAccount: publicKey('44444444444444444444444444444444'),
+  // The Token Account of the Wallet.
+  token: publicKey('55555555555555555555555555555555'),
+}).sendAndConfirm(umi)
+```

+ 153 - 0
src/pages/mpl-hybrid/update-escrow.md

@@ -0,0 +1,153 @@
+---
+title: Updating the Configuration of an MPL Hybrid 404 Escrow
+metaTitle: Updating the Configuration of an MPL Hybrid 404 Escrow | MPL-Hybrid
+description: Learn to update the configuration of an MPL 404 Hybrid Escrow account.
+---
+
+The escrow configuration is upgradable via the `updateEscrowV1` plugin.
+
+To make things easier you can fetch your escrow account using the `fetchEscrowV1()` from the `mpl-hybrid` package and use the spread operator to supply all current field values to the update instruction and adjust only the values you wish to change as the original unchanged values will be handled by the spread operator.
+
+## Update Escrow
+
+```ts
+const escrowConfigurationAddress = publicKey("11111111111111111111111111111111");
+
+// Fetch the escrow configuration account.
+const escrowConfigurationData = await fetchEscrowV1(umi, escrowConfigurationAddress);
+
+// Use the spread operator `...` to spread the `escrowConfigurationData` fields into the object
+// and adjust any fields you wish to update.
+const res = await updateEscrowV1(umi, {
+    ...escrowConfigurationData,
+    // your escrow configuration address.
+    escrow: escrowConfigurationAddress,
+    authority: umi.identity,
+    // add any fields below that you wish to change and update.
+    feeAmount: 100000,
+}).sendAndConfirm(umi);
+
+```
+
+## Updatable Fields
+
+The updatable fields that can be passed into the arg object of `updateEscrowV1`.
+
+```ts
+{
+    name,
+    uri,
+    max,
+    min,
+    amount,
+    feeAmount,
+    solFeeAmount,
+    path
+}
+
+```
+
+### name
+
+The name of your escrow.
+
+```ts
+name: "My Test Escrow"
+```
+
+### uri
+
+This is the base uri for your metadata pool. This needs to be a static uri which also contains your metadata json files at sequential destination. ie:
+```
+https://shdw-drive.genesysgo.net/.../0.json
+https://shdw-drive.genesysgo.net/.../1.json
+https://shdw-drive.genesysgo.net/.../2.json
+```
+
+```ts
+uri: "https://shdw-drive.genesysgo.net/<bucket-id>/"
+```
+
+### token
+
+The Token mint address that is being used in your MPL Hybrid 404 project. 
+
+```ts
+token: publicKey("11111111111111111111111111111111")
+```
+
+### feeLocation
+
+The wallet address which will be receiving the fees from the swaps.
+
+```ts
+feeLocation: publicKey("11111111111111111111111111111111")
+```
+
+### feeAta
+
+The Token Account of the wallet that will be receiving the tokens.
+
+```ts
+feeAta: findAssociatedTokenPda(umi, {
+    mint: publicKey("111111111111111111111111111111111"),
+    owner: publicKey("22222222222222222222222222222222"),
+  });
+```
+
+### min and max
+
+The min and max represent the min and max indexes available in your metadata pool.
+
+```
+Lowest index: 0.json
+...
+Highest index: 4999.json
+```
+
+This would then translate into the min and max args.
+```ts
+min: 0,
+max: 4999
+```
+
+### fees
+
+There are 3 separate fees that can be updated.
+
+```ts
+// Amount of tokens to receive when swapping an NFT to tokens. 
+// This value is in lamports and you will need to take into account 
+// the number of decimals the token has. If the token has 5 decimals 
+// and you wish to charge 1 whole token then feeAmount would be `100000`
+
+amount: 100000,
+```
+
+```ts
+// Fee amount to pay when swapping Tokens to an NFT. 
+// This value is in lamports and you will need to take into 
+// account the number of decimals the token has. 
+// If the token has 5 decimals and you wish to charge 1 whole token 
+// then feeAmount would be `100000`
+
+feeAmount: 100000,
+```
+
+```ts
+// Optional fee to pay when swapping from Tokens to NFT. 
+// This is in lamports so you can use `sol()` to calculate 
+// the lamports.
+
+solFeeAmount: sol(0.5).basisPoints,
+```
+
+### path
+
+The `path` arg either enables of disables the metadata rerolling function on the mpl-hybrid program.
+
+```ts
+// Reroll metadata on swap 0 = true, 1 = false
+path: 0,
+```
+

+ 3 - 2
src/pages/stability-index.md

@@ -8,7 +8,7 @@ Below is a list of our products and their stability levels.
 
 | Product Name          | Stability Level  |
 | --------------------- | ---------------- |
-| Token Metadata        | 2 (Stable)       |
+| Token Metadata        | 3 (Code Freeze)  |
 | Token Auth Rules      | 2 (Stable)       |
 | Bubblegum             | 2 (Stable)       |
 | Candy Machine v3      | 2 (Stable)       |
@@ -43,7 +43,8 @@ The stability indices are as follows:
 - **Stability: 0 - Deprecated**. The feature may emit warnings. Backward compatibility is not guaranteed.
 - **Stability: 1 - Experimental**. The feature may emit warnings. The feature is not subject to [Semantic Versioning](https://semver.org) rules. Non-backward compatible changes or removal may occur in any future release. Use of the feature is not recommended in production or mainnet environments.
 - **Stability: 2 - Stable**. Compatibility with the ecosystem is a high priority.
-- **Stability: 3 - Legacy**. The feature is no longer recommended for use. While it likely will not be removed, and is still covered by semantic-versioning guarantees, use of the feature should be avoided.
+- **Stability: 3 - Code Freeze**. Functionality and features of program are finalized. Security firms perform final audits before upgrade authority is destroyed.
+- **Stability: 4 - Immutable**. Program is immutable. This allows the program to inherit the full security guarantees of Solana or the SVM.
 
 Use caution when making use of Experimental features. Users may not be aware
 that experimental features are being used. Bugs or behavior changes may

+ 111 - 0
src/pages/token-metadata/faq.md

@@ -24,6 +24,117 @@ There are several ways to solve this problem:
 
 As mentioned in the question above, filtering by fields present after the `creators` array is a challenging task because it is not a field of fixed size. We recommend to use DAS for the fastest and easiest method to get collection mints. If you want to get the data directly from chain you can use the following method, but we have a [Guide](/token-metadata/guides/get-by-collection) showing three different Methods to get all the NFTs in a collection.
 
+## How to create a Soulbound Asset?
+
+Token Metadata allows you to create Soulbound Assets. The best way to achieve this is using Token22 as the base SPL token, along with the `non-transferrable` Token Extension.
+
+{% dialect-switcher title="Create a Soulbound asset" %}
+{% dialect title="JavaScript" id="js" %}
+
+```ts
+import { createV1 } from "@metaplex-foundation/mpl-token-metadata";
+import { createAccount } from '@metaplex-foundation/mpl-toolbox';
+import {
+  ExtensionType,
+  createInitializeMintInstruction,
+  getMintLen,
+  createInitializeNonTransferableMintInstruction,
+} from '@solana/spl-token';
+import {
+  fromWeb3JsInstruction,
+  toWeb3JsPublicKey,
+} from '@metaplex-foundation/umi-web3js-adapters';
+
+const SPL_TOKEN_2022_PROGRAM_ID: PublicKey = publicKey(
+  'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb'
+);
+
+const umi = await createUmi();
+const mint = generateSigner(umi);
+
+const extensions = [ExtensionType.NonTransferable];
+const space = getMintLen(extensions);
+const lamports = await umi.rpc.getRent(space);
+
+// Create the mint account.
+const createAccountIx = createAccount(umi, {
+  payer: umi.identity,
+  newAccount: mint,
+  lamports,
+  space,
+  programId: SPL_TOKEN_2022_PROGRAM_ID,
+}).getInstructions();
+
+// Initialize the non-transferable extension.
+const createInitNonTransferableMintIx =
+  createInitializeNonTransferableMintInstruction(
+    toWeb3JsPublicKey(mint.publicKey),
+    toWeb3JsPublicKey(SPL_TOKEN_2022_PROGRAM_ID)
+  );
+
+// Initialize the mint.
+const createInitMintIx = createInitializeMintInstruction(
+  toWeb3JsPublicKey(mint.publicKey),
+  0,
+  toWeb3JsPublicKey(umi.identity.publicKey),
+  toWeb3JsPublicKey(umi.identity.publicKey),
+  toWeb3JsPublicKey(SPL_TOKEN_2022_PROGRAM_ID)
+);
+
+// Create the transaction with the Token22 instructions.
+const blockhash = await umi.rpc.getLatestBlockhash();
+const tx = umi.transactions.create({
+  version: 0,
+  instructions: [
+    ...createAccountIx,
+    fromWeb3JsInstruction(createInitNonTransferableMintIx),
+    fromWeb3JsInstruction(createInitMintIx),
+  ],
+  payer: umi.identity.publicKey,
+  blockhash: blockhash.blockhash,
+});
+
+// Sign, send, and confirm the transaction.
+let signedTx = await mint.signTransaction(tx);
+signedTx = await umi.identity.signTransaction(signedTx);
+const signature = await umi.rpc.sendTransaction(signedTx);
+await umi.rpc.confirmTransaction(signature, {
+  strategy: { type: 'blockhash', ...blockhash },
+  commitment: 'confirmed',
+});
+
+// Create the Token Metadata accounts.
+await createV1(umi, {
+  mint,
+  name: 'My Programmable NFT',
+  uri: 'https://example.com/my-programmable-nft.json',
+  sellerFeeBasisPoints: percentAmount(5.5),
+  tokenStandard: TokenStandard.ProgrammableNonFungible,
+  splTokenProgram: SPL_TOKEN_2022_PROGRAM_ID,
+}).sendAndConfirm(umi);
+
+// Derive the token PDA.
+const token = findAssociatedTokenPda(umi, {
+  mint: mint.publicKey,
+  owner: umi.identity.publicKey,
+  tokenProgramId: SPL_TOKEN_2022_PROGRAM_ID,
+});
+
+// Mint the token.
+await mintV1(umi, {
+  mint: mint.publicKey,
+  token,
+  tokenOwner: umi.identity.publicKey,
+  amount: 1,
+  splTokenProgram: SPL_TOKEN_2022_PROGRAM_ID,
+  tokenStandard: TokenStandard.ProgrammableNonFungible,
+}).sendAndConfirm(umi);
+```
+{% /dialect %}
+{% /dialect-switcher %}
+
+If it is required to use TokenKeg SPL tokens, you can create a Soulbound Asset using the [Locked Transfer Delegate](/token-metadata/delegates#locked-transfer-delegate-pnft-only) on a pNFT and then locking the pNFT.  Note however that this will not only prevent the owner from transferring the pNFT, but will also prevent the owner from burning it.  This is why the recommendation for Soulbound Assets is to use Token22 tokens.
+
 ## Why are the mint and freeze authorities transferred to the Edition PDA?
 
 One question we often receive is: Why does the Token Metadata program transfer the `Mint Authority` and the `Freeze Authority` of the Mint Account to the Edition PDA when creating NFTs? Why not just void them by setting them to `None`?

+ 54 - 12
src/pages/token-metadata/guides/account-size-reduction.md

@@ -1,35 +1,77 @@
 ---
 title: FAQ - Token Metadata Account Size Reduction
 metaTitle: Token Metadata Account Size Reduction | Token Metadata Guides
-description: Learn about the impacts of the TM Account Size reduction
+description: Learn about the impacts of the TM Account Size reduction.
 ---
 
-On 9th of September 2024 a Metaplex Token Metadata program change was deployed to devnet that reduces the size of all future metadata accounts created. If your product fetches any metadata from TM, this change might affect you. Make sure to read below and implement any necessary changes so things don't break!
-
+On 10th of October 2024 a Metaplex Token Metadata program change was deployed to mainnet that reduces the size of all new metadata accounts created. Subsequently on October 25th, Metaplex introduced a new Resize instruction that enables existing metadata accounts to be resized. If your product fetches any metadata from TM, this change might affect you. Make sure to read below and implement any necessary changes so things don't break!
 
 ## What is the reason for this update?
-The update reduces new metadata account sizes, lowering the data storage cost, while making Solana lighter & more cost-efficient.
+
+The update reduces metadata account sizes, lowering the data storage cost, while making Solana lighter & more cost-efficient.
+
+## Why is this important?
+
+There is ~15GB of state taken up with unused Token Metadata buffer space, which is an added tax on those who maintain and power the network. This optimization is an important step in benefitting every Solana user and the validator infrastructure the network depends on.
 
 ## How much is the Size reduced?
+
 The Account sizes are reduced as you can see in the table below.
 
-|Account           | Size (bytes) | New Size (bytes)|
-|------------------|--------------|-----------------|
-|Metadata          | 679          | 607             |
-|Master Edition v1 | 282          | 20              |
-|Master Edition v2 | 282          | 20              |
-|Edition           | 241          | 42              |
+| Account           | Size (bytes) | New Size (bytes) |
+| ----------------- | ------------ | ---------------- |
+| Metadata          | 679          | 607              |
+| Master Edition v1 | 282          | 20               |
+| Master Edition v2 | 282          | 20               |
+| Edition           | 241          | 42               |
+
+## What does it mean to "resize" an asset?
+
+We've allowed NFT holders to optimize the network directly by adding a Resize instruction which enables the release of excess SOL not previously accessible without fully closing (aka burning) the Token Metadata accounts.
+
+## How many Token Metadata accounts are eligible to be resized and how much SOL can I claim?
+
+There are 25.7M eligible Token Metadata NFTs with Token Metadata accounts that are eligible to be resized (as of October 30th). 0.0023 excess SOL can be claimed per Master Edition and 0.0019 excess SOL can be claimed per Edition.
+
+## Where can I resize my NFT?
+
+You can use our free UI at [resize.metaplex.com](https://resize.metaplex.com) to resize your eligible assets between now and April 25th, 2025.
+
+Alternative tools exist that offer a paid service for processing the Resize instruction, such as [Sol Incinerator](https://sol-incinerator.com/) (5% of the resize amount).
+
+## If I resize my NFT now, can I still burn the NFT later and claim the remaining rent?
+
+Yes, resizing the NFT now and burning the NFT later would enable NFT holders to receive the same amount of SOL as if they closed all of the Token Metadata accounts today.
+
+## What happens to my NFT if I resize?
+
+Resize does not impact your NFT’s functionality. TM accounts associated with your NFT will be optimized to free up space on the Solana network. You will receive the excess SOL from the optimization. However, as stated below, programs
+using exceedingly old SDK versions will need to update to handle the smaller Token Metadata accounts.
+
+## Why does the the resize window end after 6 months? And why will the remaining excess SOL no longer needed for rent be transferred to the Metaplex DAO?
+
+The goal of the resize initiative is to improve performance of the Solana network.
+
+In order to do this, we've allowed NFT holders to optimize the network directly by adding a Resize instruction which enables the release of excess SOL not previously accessible without fully closing (aka burning) the Token Metadata accounts.
+
+Six months is a reasonable time frame to allow NFT holders to access this new benefit while also making sure the benefits to the network are realized in a timely manner.
+
+After that point any remaining SOL will be contributed to the Metaplex DAO which is responsible for stewarding the Protocol, at which point the DAO can vote to airdrop the SOL, distribute grants to ecosystem builders, or other initiatives.
 
 ## Who is affected by the Change?
+
 Every Program and Tool that is based on our Rust SDK and deserializes Data from the TM Accounts and is using very old SDK Versions might be affected.
 
 ## Which SDK Versions are affected?
+
 - **Javascript**: The JS SDKs (both @metaplex-foundation/js and Umi-based SDKs) are not affected
 - **Rust**: The Rust SDK became compatible over a Year ago starting with v2.0.0 (Available since August 2023)
 - **Anchor**: Anchor 0.29 and above is compatible to the new sizes (Available since October 2023)
 
 ## How to make your Program compatible?
-If you are using an older SDK Version than listed above and deserializing Token Metadata Data it is recommended to update the used Packages to ensure compatability.
+
+If you are using an older SDK Version than listed above and deserializing Token Metadata Data it is recommended to update the used Packages to ensure compatibility.
 
 ## Where can I find Help and more Information?
-In case something is unclear after reading this FAQ please feel free to join our [Discord](https://discord.gg/metaplex) and drop your questions!
+
+In case something is unclear after reading this FAQ please feel free to join our [Discord](https://discord.gg/metaplex) and drop your questions!

+ 6 - 0
src/pages/umi/transactions.md

@@ -133,6 +133,12 @@ At this point, you could use the built transaction and get all deduplicated sign
 const signedTransaction = await builder.buildAndSign(umi)
 ```
 
+Transactions that were already built but not signed yet can be pushed into an Array and signed all at once using `signAllTransactions`.
+
+```ts
+const signedTransactions = await signAllTransactions(transactionArray);
+```
+
 ## Sending transactions
 
 Now that we have a signed transaction, let's see how we can send it to the blockchain.

+ 1 - 1
src/styles/scrollbar.css

@@ -1,7 +1,7 @@
 .scrollbar {
 ::-webkit-scrollbar {
     width: 4px;
-    height: 10px;
+    height: 4px;
   }
   ::-webkit-scrollbar-button {
     width: 0px;