> ## Documentation Index
> Fetch the complete documentation index at: https://docs.snagsolutions.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Rewards Shop

> Create NFT contracts, mint assets, and list them for purchase, auction, or sweepstakes in your Snag rewards shop. Supports ERC-721 and ERC-1155.

<head>
  <script type="application/ld+json">
    {JSON.stringify({
            "@context": "https://schema.org",
            "@graph": [
              {
                "@type": "TechArticle",
                "headline": "Rewards Shop",
                "description": "Create NFT contracts, mint assets, and list them for purchase, auction, or sweepstakes in your Snag rewards shop. Supports ERC-721 and ERC-1155.",
                "author": {"@type": "Organization", "name": "Snag Solutions", "url": "https://www.snagsolutions.io/"},
                "publisher": {"@type": "Organization", "name": "Snag Solutions", "url": "https://www.snagsolutions.io/", "logo": {"@type": "ImageObject", "url": "https://assets.snagsolutions.io/public/docs/snag-logo-dark-no-bg.svg"}},
                "mainEntityOfPage": "https://docs.snagsolutions.io/create/rewards-shop"
              },
              {
                "@type": "BreadcrumbList",
                "itemListElement": [
                  {"@type": "ListItem", "position": 1, "name": "Home", "item": "https://docs.snagsolutions.io/welcome"},
                  {"@type": "ListItem", "position": 2, "name": "Create", "item": "https://docs.snagsolutions.io/create/page-builder"},
                  {"@type": "ListItem", "position": 3, "name": "Rewards shop"}
                ]
              }
            ]
          })}
  </script>
</head>

## Overview

[Our asset creation tab in the admin tool](https://admin.snagsolutions.io/) consists of three separate flows that comprise the entire contract set and minting & auction page creation process:

1. Contract set up
2. Metadata configuration / asset creation
3. Listing or auction creation

## Step by step

### Contract set up

1. Access the Asset Creation tab in the menu on the left and hit the create new contract button in the top right.

<Frame>
  ![Snag admin asset creation tab with create new contract button](https://assets.snagsolutions.io/public/docs/create-new-contract.png)
</Frame>

1. Hit the 'Create New Contract' button on the next modal to confirm you'll be the owner on the contract. On the next tab you'll:

   1. Input the collection name, symbol, and image along with optional description and external link.

   2. Choose the token type ([ERC-721](https://eips.ethereum.org/EIPS/eip-721) or [ERC-1155](https://eips.ethereum.org/EIPS/eip-1155)). Only one token type can exist on the NFT smart contract.

   3. Choose chain

   4. *Optional:* enable meta transaction if you plan to enable gasless transactions in the future via Stratus relayers

      .

   5. Choose the wallet address for mint and royalty payouts

<Frame>
  ![Contract setup modal with collection name, symbol, and image fields](https://assets.snagsolutions.io/public/docs/create-contract-pt-1a.png)
</Frame>

<Frame>
  ![Contract details with token type, chain, and payout wallet settings](https://assets.snagsolutions.io/public/docs/create-contract-pt-2a.png)
</Frame>

#### Token gating

Token gating lets you restrict who can claim or buy from this contract by checking wallet ownership or badges at checkout. Configure it during contract set up so every listing tied to the contract uses the same gating rules.

<Frame>
  ![Token gating configuration for restricting minting by wallet holdings](https://assets.snagsolutions.io/public/docs/token-gating-minting.png)
</Frame>

When token gating is enabled, you can choose which assets grant access (NFTs, tokens, specific collections, or badges) and set the minimum balance required. You can use this to:

* limit claims to holders of a partner collection
* require a minimum token balance before minting
* unlock drops for badge holders or onchain members

Configuration options:

* Free-claim versions are supported by the checkbox under each option. Users can claim for free if they own the required tokens or badges.
* Limit the amount of mints to the number of tokens or badges the user owns.
* Require the user to own all the required tokens or badges. In this case, they will be able to mint only one item.

### Add Assets to Contract

1. The next step is to configure metadata to the contract as individual assets. We support individual asset creation to start, with .CSV upload and other multi-asset upload capabilities coming soon.
   1. Add asset name, description, info about the artist, media, and optional trait info
   2. Optional fields
      1. *Add quantity limit*: maximum amount mintable/wallet
      2. *Add minimum*: minimum amount to mint/transaction
      3. *Burn to redeem*: if you want users to burn the NFT to redeem it. You can add a field they need to fill in, show a blank text or collect shipping details(needs API integration).
   3. Click 'Finish' when you're done or 'Duplicate' / 'New Asset' to add more

<Frame>
  ![Asset metadata form with name, description, artist info, and media upload](https://assets.snagsolutions.io/public/docs/add-assets-pt-1.png)
</Frame>

<Frame>
  ![Asset optional fields for quantity limit, minimum mint, and burn to redeem](https://assets.snagsolutions.io/public/docs/add-assets-pt-2.png)
</Frame>

### Listing

1. You can now hit 'List Assets' on the contract modal in the assets creation tab when ready to either list, create an auction or create a sweepstakes.

<Frame>
  ![List assets button on the contract modal in asset creation tab](https://assets.snagsolutions.io/public/docs/list-assets.png)
</Frame>

<Frame>
  ![Listing type selection with purchase, auction, and sweepstakes options](https://assets.snagsolutions.io/public/docs/choose-listing.png)
</Frame>

#### List assets for purchase

<Frame>
  ![Listing configuration form for currency, price, and duration settings](https://assets.snagsolutions.io/public/docs/list-asset-marketplace.png)
</Frame>

1. Choose the currency you'd like to list in (native currency or loyalty points if enabled)
2. Choose the price and the asset you'd like to list
   1. You can select "free claim", if you want users to claim for free and pay only gas fees.
3. Choose listing duration start and (optional) end time

Allowlist (optional)

* You can add a allowlist to the listing to restrict who can claim the asset.
* You can either upload a CSV file with the wallet addresses or add them via the [API](https://docs.snagsolutions.io/api-reference/minting/upsert-minting-allowlist-entries)
* Allowlisted wallets can always claim the asset for free, only paying gas fees.

#### List assets for auction

1. Add an auction name, description, duration, and currency
2. **Note:** Auctions can be tied to one or more than one onchain asset, so please add sufficient description to explain to the user what they're bidding on
3. Choose your auction mechanics:
   1. Minimum Bid: The minimum amount any bidder can start the auction with
   2. Minimum Bid Increment: The minimum increase on any bid from the previous highest bid
   3. Reserve Price: The minimum bid that is valid to win the auction, this is the most important of these three inputs
4. **Multi-Winner Auctions Only:** By default, multi-winner auctions charge each bidder the amount they bid, if 'Reverse Dutch Auction' is toggled we'll instead refund all winners the difference of their bid and the lowest price winning bid
5. **Blind Auctions Only:** If toggled on, users can only see their own bids. All other bids are hidden
6. Choose the prize!

<Frame>
  ![Auction setup form with name, description, duration, and bid settings](https://assets.snagsolutions.io/public/docs/create-auction-pt-1.png)
</Frame>

<Frame>
  ![Auction prize selection and multi-winner auction options](https://assets.snagsolutions.io/public/docs/create-auction-pt-2.png)
</Frame>

#### List assets for sweepstakes

1. Add the sweepstakes name, description, duration, and entry token purchase currency
2. Choose the entry ticket and specify the total number of available tickets.
3. Choose the prize(s).

<Frame>
  ![Sweepstakes setup form with name, description, and entry ticket config](https://assets.snagsolutions.io/public/docs/sweepstake-pt-1.png)
</Frame>

<Frame>
  ![Sweepstakes prize selection and ticket availability settings](https://assets.snagsolutions.io/public/docs/sweepstake-pt-2.png)
</Frame>

1. You're live! View these listings as part of your marketplace!

### Hide a collection from template pages

To prevent a collection from appearing on template pages (for example, the Loyalty page), you can hide it from the Rewards Shop.

1. Click the 'Edit Rewards page' button in the top right of the Rewards Shop.

<Frame>
  ![Edit Rewards page button in Snag admin Rewards Shop](https://assets.snagsolutions.io/public/docs/edit+rewards+page+button+for+reward+shop.png)
</Frame>

2. In the Edit Rewards page settings, use the visibility control to hide or unhide the collection so it does not show on template pages (e.g., Loyalty).

<Frame>
  ![Visibility toggle to hide or show a collection on template pages](https://assets.snagsolutions.io/public/docs/hide+toggle+for+reward+shop.png)
</Frame>

## API-based integration

Use the minting API to power a custom checkout or backend-driven flow. This mirrors the same flow used by the Rewards Shop frontend.

<Info>
  API requests use the `X-API-KEY` header. Use the `assetId` and `contractId`
  (UUID or onchain address) from the Rewards Shop.
</Info>

<Steps>
  <Step title="Create a mint request">
    Call `POST /api/minting/contracts/mint` with the `assetId`, `contractId`, `walletAddress`, and `quantity`

    ```json theme={null}
    {
      "assetId": "asset_uuid",
      "contractId": "contract_uuid_or_address",
      "walletAddress": "0xabc...",
      "quantity": 1,
    }
    ```

    The response returns `mintingContractAssetMintStatusId`, which you will poll
    for status and signature.
  </Step>

  <Step title="Poll for a signature (or relayer tx)">
    Call `GET /api/minting/status/{id}` every 1-2 seconds until a `signature` is
    returned or the status moves to `failed`.

    * If `signature` is returned, you need to submit the onchain transaction
      yourself.
    * If `relayerTxId` is present, this is a gasless transaction and you can ignore the next step.
  </Step>

  <Step title="Execute the onchain mint">
    Use the returned signature to execute the mint on the correct chain by
    calling the mint function on the contract.

    * **EVM**: call [mintWithSignature](https://portal.thirdweb.com/references/typescript/v5/erc721/mintWithSignature) for ERC-721/1155. If `currency` is an
      ERC-20 and `price > 0`, approve `price * quantity` before minting.
    * **Solana**: execute the base64 transaction using the provided blockhash
      and last valid block height.

    <AccordionGroup>
      <Accordion title="Stripped down version of frontend integration">
        <Info>
          This is how our internal implementation looks like. For contract interaction we use:

          * For EVM chains, [thirdweb](https://portal.thirdweb.com/references/typescript/v5/erc721/mintWithSignature).
          * For Solana, [metaplex](https://docs.metaplex.com/sdk-js/minting-tokens).
        </Info>

        ```ts theme={null}
          import {
            sendTransaction,
            getContract,
            waitForReceipt
          } from "thirdweb";
        const buyToken = async (
          props?: BuyTokenProps
        ): Promise<string | undefined> => {
          const res = await axios.post(`/api/minting/contracts/mint`, {
            quantity,
            assetId,
            shippingId: props?.shippingId,
            shippingOrderType: props?.shippingOrderType,
            emailAddress: props?.emailAddress,
            contractId: contractAddress,
            agreedToSendW9: props?.agreedToSendW9,
            customInputValue: props?.customInputValue,
          })

          if (res.status !== 202) {
            return undefined
          }

          const mintStatusId = res.data.mintingContractAssetMintStatusId

          while (true) {
            const { data } = await axios.get(`/api/minting/status/${mintStatusId}`)

            if (data.relayerTxId && data.status === MintStatus.minted) {
              return data.txHash
            }

            if (data.signature) {
              const signature = JSON.parse(data.signature)

              if (network === NetworkType.solana) {
                const umi = createUmi()

                const { signature: txSig } = await executeBase64Txn(
                  umi,
                  signature.signature,
                  signature.latest
                )
                await updateMintStatus(mintStatusId, {
                  status: MintStatus.minted,
                  txHash: txSig,
                })
                return txSig
              }

              const contract = getContract({
                address: contractAddress,
                client: thirdwebClient,
                chain: defineThirdwebChain(chainId),
              })
              const fnToUse =
                tokenType === 'erc721'
                  ? mintWithSignatureErc721
                  : mintWithSignatureErc1155
              const preparedTx = fnToUse({
                contract,
                signature: signature.signature,
                payload: signature.payload,
              })
              const sentTx = await sendTransaction({
                account,
                transaction: preparedTx,
              })
              await waitForReceipt({
                client: thirdwebClient,
                chain: defineThirdwebChain(chainId),
                transactionHash: sentTx.transactionHash,
              })
              await axios.post(`/api/minting/status/${mintStatusId}`, {
                status: 'minted',
                txHash: sentTx.transactionHash,
              })
              return sentTx.transactionHash
            }
          }
        ```
      </Accordion>
    </AccordionGroup>
  </Step>

  <Step title="Update mint status (optional)">
    After the transaction confirms, you can call `POST /api/minting/status/{id}` with
    `status: minted` and the `txHash`. This will be done automatically if you don't call it, but can improve the UX.
  </Step>
</Steps>

## Troubleshooting

### NFTs not showing on your frontend (e.g., `https://example.com/loyalty`)

If some NFTs created in the admin are not visible on your public frontend, walk through the checks below.

<Info>
  These steps apply when your site consumes listings/collections configured in
  the admin. If your frontend uses a custom integration, ensure it queries the
  same contracts, networks, and listing types you configure here.
</Info>

<Steps>
  <Step title="Ensure the collection is visible on template pages">
    Open the Rewards Shop and click 'Edit Rewards page' in the top right, then make sure the collection is set to Visible so it can appear on template pages (e.g., Loyalty).

    <Check>
      After saving, the collection becomes eligible to render on template-driven pages.
    </Check>
  </Step>

  <Step title="Verify the assets are actually listed and currently active">
    Creating assets alone will not show them to users. Confirm you used 'List Assets' and that:

    * The assets are included in the listing (IDs/ranges set correctly)
    * The listing start time is in the past and the end time (if any) has not passed
    * The listing status is active

    <Tip>
      On the listing modal, toggle to 'Unlisted Assets' to find items you may have missed.
    </Tip>
  </Step>

  <Step title="Match the page filters and currency type">
    Template pages may filter by collection, category, or currency. For example, a Loyalty page typically shows listings priced in loyalty points. If you listed in a different currency (e.g., native token), those items may not appear on that page.

    <Warning>
      If your page is configured to only show loyalty listings, standard crypto-priced listings won't display there.
    </Warning>
  </Step>

  <Step title="Confirm the correct chain/network">
    Ensure your contract and listings are on the same chain your frontend is reading from. If your site points to a different network (e.g., testnet vs mainnet), listings will not appear.
  </Step>

  <Step title="Allow indexing time and hard refresh the page">
    After creating or updating listings, allow a few minutes for indexing and caching to update, then hard refresh your frontend.

    <Tip>
      If you use server-side caching or a CDN, purge the cache for the affected pages.
    </Tip>
  </Step>
</Steps>

<AccordionGroup>
  <Accordion title="Still not seeing certain items?">
    * **Collection not attached to the page**: In 'Edit Rewards page', verify
      the targeted collection is included in the page layout/sections. -
      **Incorrect ID ranges**: If you used ranges (e.g., 1-10), make sure all
      intended IDs fall within those ranges. - **Missing media or required
      metadata**: Some templates hide items with incomplete media/fields; confirm
      each asset has the required fields and media URLs. - **Frontend filters**:
      Check user-facing filters (collection, price type, availability) on the site
      aren't excluding your items.
  </Accordion>
</AccordionGroup>

### Solana Mint Setup & Account Initialization

On Solana, accounts must be initialized on-chain before they can be used in transactions. While initialization instructions can be added to the main transaction, this often increases the transaction size beyond Solana's limits.

Account initialization is required in two main scenarios:

1. **Asset/Token Minting**: The asset or token being minted needs an initialized account.
2. **Payment Receipt**: The wallet receiving payment must be initialized for the specific currency (e.g., USDC).

#### Solutions & UX Considerations

To handle these requirements without hitting transaction size limits, we use the following approaches:

1. **Pre-mint for Assets**: We perform a "pre-mint" operation—a free mint that initializes the account for the asset/token. This keeps the main transaction size within limits.

   You can perform this pre-mint action directly from the admin dashboard on the listing modal. If you encounter the "Transaction Too Large" error, try running this pre-mint step first.

   <Frame>
     ![Pre-mint option on the listing modal in Snag admin](https://assets.snagsolutions.io/public/docs/pre-mint-asset.png)
   </Frame>

   <Note>
     This pre-mint action currently lacks a specific success message or loading indicator in the UI. It does not count towards the visible mint count (which may still show 0), even though 1 quantity is minted on-chain.
   </Note>

2. **Wallet Initialization**: For the payment recipient wallet, we send a small amount of the required currency (e.g., USDC or SOL) to initialize the account. This ensures the fee recipient address is ready to receive mint payments.

#### Common Errors

If account initialization is not handled correctly, you may encounter the following errors:

**Transaction Simulation Failed**

```text theme={null}
Simulation failed. Message: Transaction simulation failed: Error processing Instruction 0: invalid account data for instruction.
Logs: [
  "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [1]",
  "Program log: Instruction: Transfer",
  "Program log: Error: InvalidAccountData",
  "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 2799 of 600000 compute units",
  "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA failed: invalid account data for instruction"
]
```

**Transaction Too Large**

```text theme={null}
VersionedTransaction too large
```

## Related pages

<CardGroup cols={2}>
  <Card title="Badges" icon="badge-check" href="/create/badges">
    Create achievement badges to gamify your loyalty program.
  </Card>

  <Card title="Stratus Relayers" icon="key" href="/stratus/relayers">
    Enable gasless minting with managed private keys.
  </Card>

  <Card title="Loyalty Overview" icon="stars" href="/loyalty/loyalty-overview">
    Set up the loyalty program that powers your rewards shop.
  </Card>

  <Card title="Page Builder" icon="pen-ruler" href="/create/page-builder">
    Build custom pages to showcase your collections and rewards.
  </Card>
</CardGroup>
