# Table of Contents - [Anvil API | Anvil Development Agency](#anvil-api-anvil-development-agency) - [Cardano Basics | Anvil Development Agency](#cardano-basics-anvil-development-agency) - [Usage | Anvil Development Agency](#usage-anvil-development-agency) - [Fees | Anvil Development Agency](#fees-anvil-development-agency) - [Authentication | Anvil Development Agency](#authentication-anvil-development-agency) - [Troubleshooting | Anvil Development Agency](#troubleshooting-anvil-development-agency) - [FAQ | Anvil Development Agency](#faq-anvil-development-agency) - [Sign Transaction | Anvil Development Agency](#sign-transaction-anvil-development-agency) - [Select Cardano Environment | Anvil Development Agency](#select-cardano-environment-anvil-development-agency) - [Deno & Fetch | Anvil Development Agency](#deno-fetch-anvil-development-agency) - [Support | Anvil Development Agency](#support-anvil-development-agency) - [Submit Transaction | Anvil Development Agency](#submit-transaction-anvil-development-agency) - [Create Basic Transaction | Anvil Development Agency](#create-basic-transaction-anvil-development-agency) - [Bash & cURL | Anvil Development Agency](#bash-curl-anvil-development-agency) - [Next.js & Weld | Anvil Development Agency](#next-js-weld-anvil-development-agency) - [Create Custom Transaction | Anvil Development Agency](#create-custom-transaction-anvil-development-agency) - [Deno & Fetch | Anvil Development Agency](#deno-fetch-anvil-development-agency) - [Create Transaction with Metadata (CIP-20) | Anvil Development Agency](#create-transaction-with-metadata-cip-20-anvil-development-agency) - [Transaction | Anvil Development Agency](#transaction-anvil-development-agency) - [Part 1: Project Setup | Anvil Development Agency](#part-1-project-setup-anvil-development-agency) - [Part 2: Wallet Integration | Anvil Development Agency](#part-2-wallet-integration-anvil-development-agency) - [Deno & Fetch | Anvil Development Agency](#deno-fetch-anvil-development-agency) - [Bash & cURL | Anvil Development Agency](#bash-curl-anvil-development-agency) - [Bash & cURL | Anvil Development Agency](#bash-curl-anvil-development-agency) - [Select UTXOs | Anvil Development Agency](#select-utxos-anvil-development-agency) - [Policy Units | Anvil Development Agency](#policy-units-anvil-development-agency) - [Delegations | Anvil Development Agency](#delegations-anvil-development-agency) - [Validators | Anvil Development Agency](#validators-anvil-development-agency) - [Deno & Fetch | Anvil Development Agency](#deno-fetch-anvil-development-agency) - [Bash & cURL | Anvil Development Agency](#bash-curl-anvil-development-agency) - [Mint NFTs (CIP-68) | Anvil Development Agency](#mint-nfts-cip-68-anvil-development-agency) - [Mint NFTs (CIP-25) | Anvil Development Agency](#mint-nfts-cip-25-anvil-development-agency) - [Delegate to a DRep | Anvil Development Agency](#delegate-to-a-drep-anvil-development-agency) - [Delegate to a Stake Pool | Anvil Development Agency](#delegate-to-a-stake-pool-anvil-development-agency) - [Bash & cURL | Anvil Development Agency](#bash-curl-anvil-development-agency) - [Deno & Fetch | Anvil Development Agency](#deno-fetch-anvil-development-agency) - [Deno & Fetch | Anvil Development Agency](#deno-fetch-anvil-development-agency) - [Deno & Fetch | Anvil Development Agency](#deno-fetch-anvil-development-agency) - [Profile Information | Anvil Development Agency](#profile-information-anvil-development-agency) - [Part 3: Building Transactions | Anvil Development Agency](#part-3-building-transactions-anvil-development-agency) - [Creating Native Scripts | Anvil Development Agency](#creating-native-scripts-anvil-development-agency) - [Smart Contracts | Anvil Development Agency](#smart-contracts-anvil-development-agency) - [Collection Information | Anvil Development Agency](#collection-information-anvil-development-agency) - [Get Collection Assets | Anvil Development Agency](#get-collection-assets-anvil-development-agency) - [Make Offer | Anvil Development Agency](#make-offer-anvil-development-agency) - [Native Assets (NFTs/FTs) | Anvil Development Agency](#native-assets-nfts-fts-anvil-development-agency) - [Spend Validator | Anvil Development Agency](#spend-validator-anvil-development-agency) - [CLI Tool to Mint Assets | Anvil Development Agency](#cli-tool-to-mint-assets-anvil-development-agency) - [Blueprint Management (CIP-57) | Anvil Development Agency](#blueprint-management-cip-57-anvil-development-agency) - [Wallet CLI | Anvil Development Agency](#wallet-cli-anvil-development-agency) - [Native Scripts | Anvil Development Agency](#native-scripts-anvil-development-agency) - [Metadata | Anvil Development Agency](#metadata-anvil-development-agency) - [Web3 Authentication | Anvil Development Agency](#web3-authentication-anvil-development-agency) - [Utility Functions | Anvil Development Agency](#utility-functions-anvil-development-agency) - [Overview | Anvil Development Agency](#overview-anvil-development-agency) - [Submit | Anvil Development Agency](#submit-anvil-development-agency) - [End-to-End Next.js Escrow Guide | Anvil Development Agency](#end-to-end-next-js-escrow-guide-anvil-development-agency) - [Smart Contract Utilities | Anvil Development Agency](#smart-contract-utilities-anvil-development-agency) - [Submit Transaction | Anvil Development Agency](#submit-transaction-anvil-development-agency) - [Integration Flow | Anvil Development Agency](#integration-flow-anvil-development-agency) - [Registration | Anvil Development Agency](#registration-anvil-development-agency) - [Buy NFT | Anvil Development Agency](#buy-nft-anvil-development-agency) - [Update Listing | Anvil Development Agency](#update-listing-anvil-development-agency) - [Create Listing | Anvil Development Agency](#create-listing-anvil-development-agency) - [Build Transaction | Anvil Development Agency](#build-transaction-anvil-development-agency) - [Setup | Anvil Development Agency](#setup-anvil-development-agency) - [Wallet Integration | Anvil Development Agency](#wallet-integration-anvil-development-agency) - [Unlist Listing | Anvil Development Agency](#unlist-listing-anvil-development-agency) - [Profile Listings | Anvil Development Agency](#profile-listings-anvil-development-agency) - [Transaction Dashboard | Anvil Development Agency](#transaction-dashboard-anvil-development-agency) - [Fee Structure | Anvil Development Agency](#fee-structure-anvil-development-agency) - [Profile Offers | Anvil Development Agency](#profile-offers-anvil-development-agency) - [Tournament | Anvil Development Agency](#tournament-anvil-development-agency) - [Hello World Smart Contract | Anvil Development Agency](#hello-world-smart-contract-anvil-development-agency) - [Fund Unlocking | Anvil Development Agency](#fund-unlocking-anvil-development-agency) - [Real-time Updates | Anvil Development Agency](#real-time-updates-anvil-development-agency) - [Fund Locking | Anvil Development Agency](#fund-locking-anvil-development-agency) - [Email Protection | Cloudflare](#email-protection-cloudflare) - [Mint with Smart Contract | Anvil Development Agency](#mint-with-smart-contract-anvil-development-agency) - [Mint Validator | Anvil Development Agency](#mint-validator-anvil-development-agency) --- # Anvil API | Anvil Development Agency The Anvil API empowers developers to rapidly build and deploy Cardano blockchain applications through a unified, developer-friendly interface. Whether you're creating a wallet with multi-signature capabilities, minting and managing NFT collections, implementing governance features, or interacting with smart contracts, Anvil eliminates the need to master complex low-level Cardano specifications while providing enterprise-grade reliability and performance. [](https://dev.ada-anvil.io/#key-benefits) Key Benefits ------------------------------------------------------------ ### [](https://dev.ada-anvil.io/#reduced-complexity) Reduced Complexity Anvil handles the detailed protocol specifics, enabling developers to create transactions and smart contracts without mastering low-level details. * **Simple transaction building**: Abstracted functions replace complex manual assembly. * **Multi-signature support**: Define required signatures for transactions to allow shared control over assets. * **Accessible learning curve**: Start building immediately, focusing on logic rather than specifications. Copy // Instead of complex manual transaction assembly, // Anvil API lets you create transactions with a clean, simple interface // The following example shows how to send ADA and assets to a single participant easily. { outputs: [\ {\ address: "addr...", // Destination address\ lovelace: 10000000, // Send 10 ADA\ assets: [ // Assets to send\ {\ policyId: "policy_id_here",\ assetName: "asset_name",\ quantity: 1\ }\ ]\ }\ ], changeAddress: "addr..." // Sender Address } ### [](https://dev.ada-anvil.io/#faster-wallet-integration) Faster Wallet Integration Quickly integrate reliable transaction functionalities into wallets or payment apps. * **Minimal coding effort**: Achieve common tasks like ADA transfers, asset transfers, delegation, minting, staking, or smart contract interactions through simple API endpoints. * **Reliable and tested**: Consistent results reduce debugging and enhance product stability. Maintenance-free integration with automatic hard fork updates handled seamlessly. ### [](https://dev.ada-anvil.io/#streamlined-nft-minting) Streamlined NFT Minting Efficiently mint NFTs through user-friendly "vending machine" style transactions and secure multisig minting capabilities. * **Single-step minting**: Automate complex NFT minting, metadata handling, and delivery through intuitive endpoints. * **Scalable & robust**: Supports both CIP-25 and CIP-68 token standards with minimal configuration. Copy // Simplified NFT minting without needing to understand the underlying protocols { mint: [\ {\ type: "simple",\ version: "cip25",\ assetName: { name: "MyNFT", format: "utf8" },\ policyId: "policy_id_here",\ quantity: 1,\ metadata: {\ name: "My NFT",\ description: "My first NFT minted with Anvil API",\ image: "ipfs://..."\ }\ }\ ] } ### [](https://dev.ada-anvil.io/#smart-contract-simplification) Smart Contract Simplification Interact with Cardano smart contracts without deep Plutus knowledge. * **Validator blueprints**: Use CIP-57 compliant blueprints to easily create and interact with Plutus scripts. * **Automated datum handling**: Parse, construct, and validate complex Plutus data types with ease. * **Contract evaluation**: Test your smart contract logic before deployment to ensure correct behavior. Copy // Easy validation of Plutus contract data // Instead of manual CBOR encoding/decoding { hash: "validator_hash", type: "datum", purpose: "spend", data: { hex: "your_datum_hex" } } ### [](https://dev.ada-anvil.io/#automatic-protocol-updates) Automatic Protocol Updates Anvil stays updated with Cardano's protocol changes, eliminating maintenance burdens. * **Zero downtime upgrades**: Continuous compatibility with the latest Cardano updates. * **Maintenance-free**: Focus on feature development instead of ongoing technical upkeep. [](https://dev.ada-anvil.io/#features) Features ---------------------------------------------------- Anvil API provides a comprehensive set of features to support your Cardano development needs: 1. **Multi-output Transaction Building**: Create complex transactions with multiple recipients 2. **Multi-signature Support**: Governance and shared control over assets with ease 3. **Token Minting**: Handles minting, editing, and burning for CIP-25 and CIP-68 tokens 4. **Staking Operations**: Streamlined delegations and certificates to stake pools and DReps 5. **Rewards Management**: Simplified reward withdrawal process 6. **Governance Support**: Delegate to DReps for voting in Cardano's governance system 7. **Transaction Optimization**: Automatic balancing and fee optimization 8. **Script Management**: Simplified on-chain script uploading 9. **Smart Contract Integration**: Contract evaluation and interaction via CIP-57 blueprints 10. **Language Agnostic API**: Build with your preferred technology stack through flexible API endpoints 11. **Multiple Protocol Support**: Choose between RESTful API and tRPC interfaces based on your development needs [](https://dev.ada-anvil.io/#next-steps) Next Steps -------------------------------------------------------- Ready to build with Anvil API? Choose your path: New to Cardano?: [Cardano Basics](https://dev.ada-anvil.io/anvil-api/cardano-basics) If you are a developer familiar with Cardano: [Create Basic Transaction](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction) [Fees](https://dev.ada-anvil.io/anvil-api/fees) [NextCardano Basics](https://dev.ada-anvil.io/anvil-api/cardano-basics) Last updated 11 days ago Was this helpful? --- # Cardano Basics | Anvil Development Agency [](https://dev.ada-anvil.io/anvil-api/cardano-basics#getting-started-with-cardano-a-comprehensive-guide) Getting Started with Cardano: A Comprehensive Guide ----------------------------------------------------------------------------------------------------------------------------------------------------------------- Welcome to Cardano! Whether you're a seasoned developer exploring Cardano's blockchain or a newcomer eager to dive into the ecosystem, this guide will help you set up, test, and understand the fundamentals of the network. Our goal is to simplify the onboarding process, offering clear recommendations and actionable steps to get started. ### [](https://dev.ada-anvil.io/anvil-api/cardano-basics#goals-of-this-page) Goals of This Page 1. Provide a clear and intuitive roadmap for setting up your first Cardano wallet. 2. Recommend the best wallets based on ease of use, features, and compatibility. 3. Offer actionable steps for testing and exploring Cardano using the Preprod Testnet. 4. Clarify the differences between Cardano environments (Mainnet, Preprod, Preview). 5. Introduce the core concepts of UTXOs and blockchain exploration tools. ### [](https://dev.ada-anvil.io/anvil-api/cardano-basics#step-1-setting-up-your-wallet) Step 1: Setting Up Your Wallet To interact with the Cardano blockchain, you'll need a wallet to store funds, manage assets, and connect to decentralized applications (dApps). Below is a comprehensive overview of compatible Cardano wallets: #### [](https://dev.ada-anvil.io/anvil-api/cardano-basics#recommended-browser-extension-wallets) Recommended Browser Extension Wallets 1. [**Lace**](https://www.lace.io/) : * Developed by IOG (creators of Cardano) * Multi-delegation staking capabilities * Clean, minimalist interface focused on user experience 2. [**Eternl**](https://eternl.io/) (Formerly CCVault): * Feature-rich with multi-address support and hardware wallet integration * Advanced UTXO management and token handling capabilities * Available as browser extension, web app, and mobile app 3. [**VESPR**](https://vespr.xyz/) : * Fast mobile-focused wallet with excellent dApp support * Optimized for speed and user experience * Advanced Rewards Dashboard with epoch tracking and rewards forecasting #### [](https://dev.ada-anvil.io/anvil-api/cardano-basics#full-node-desktop-wallets) Full-Node Desktop Wallets 1. [**Daedalus**](https://daedaluswallet.io/) : * Official full-node desktop wallet by Cardano Foundation * Downloads and validates the entire blockchain * Comprehensive security but requires significant system resources * Available for Windows, macOS, and Linux ### [](https://dev.ada-anvil.io/anvil-api/cardano-basics#step-2-testing-and-learning-on-cardano) Step 2: Testing and Learning on Cardano Testing is one of the best ways to understand how Cardano works without risking real funds. The **Preprod Testnet** is perfect for this because it mirrors the Mainnet but uses fake currency called **TestAda (TAda)**. Here's how you can get started: #### [](https://dev.ada-anvil.io/anvil-api/cardano-basics#why-use-the-preprod-testnet) Why Use the Preprod Testnet? * **Low-Risk Environment:** Test transactions and smart contracts without fear of loss. * **Realistic Experience:** It behaves like the Mainnet, making it a great learning and testing ground. #### [](https://dev.ada-anvil.io/anvil-api/cardano-basics#getting-testada-for-the-testnet) Getting TestAda for the Testnet 1. **Obtain Your Wallet Address**: Open your wallet (e.g., Eternl, Nami, Lace) and go to the **Receive** section. Copy your public address—it will look like a long string of letters and numbers. 2. **Visit the Faucet Tool**: Use a testnet faucet like [this one](https://docs.cardano.org/cardano-testnets/tools/faucet) to request free TAda. Paste your wallet address into the tool, and you’ll receive TestAda in minutes. 3. **Try Basic Operations:** * Send TAda to another wallet to see how transactions work. * Experiment with staking or interacting with decentralized applications (dApps). ### [](https://dev.ada-anvil.io/anvil-api/cardano-basics#step-3-understanding-cardano-and-the-eutxo-model) Step 3: Understanding Cardano and the EUTXO Model #### [](https://dev.ada-anvil.io/anvil-api/cardano-basics#what-is-cardano) What Is Cardano? Cardano is a blockchain platform built on cutting-edge research and designed for scalability, sustainability, and interoperability. It’s one of the most secure and innovative platforms available, ideal for decentralized applications and digital assets. #### [](https://dev.ada-anvil.io/anvil-api/cardano-basics#what-is-the-eutxo-model) What Is the EUTXO Model? Cardano uses an Extended Unspent Transaction Output (EUTXO) model, which might sound complicated but is easy to grasp with the right analogy. #### [](https://dev.ada-anvil.io/anvil-api/cardano-basics#analogy-cash-vs.-credit) Analogy: Cash vs. Credit * **EUTXO (Cardano):** Think of it like paying with **cash**. * If you want to pay $50, you hand over a $50 bill or a combination of smaller bills. * The recipient receives the cash, and any change comes back to you as a new "output." * This system is direct and secure because there's an actual "exchange" of funds. * **EVM (Ethereum):** Think of it like paying with **credit**. * When you pay with a credit card, the transaction gets logged to a ledger. * There’s no direct transfer—just an update to your account balance. * It’s flexible but less deterministic because balances can change dynamically. #### [](https://dev.ada-anvil.io/anvil-api/cardano-basics#why-eutxo-is-better-for-certain-use-cases) Why EUTXO Is Better for Certain Use Cases * Predictable execution: Transactions are processed deterministically, reducing the risk of failure. * Scalable: Cardano can handle more transactions simultaneously without congestion. If you want a deeper dive, visit Cardano's EUTXO guide. ### [](https://dev.ada-anvil.io/anvil-api/cardano-basics#step-4-choosing-your-environment) Step 4: Choosing Your Environment Cardano operates on three environments, each serving a specific purpose: 1. **Mainnet (Live Network):** * The active network where real transactions happen. * Use this when you’re ready for real-world blockchain interactions. 2. **Preprod (Testnet):** * Simulates Mainnet and allows you to test applications risk-free. * Ideal for experimenting with wallets, sending transactions, or testing dApps. 3. **Preview (Experimental Testnet):** * Used to test upcoming features before they’re deployed on Mainnet. * Often used by developers to ensure compatibility with future updates. #### [](https://dev.ada-anvil.io/anvil-api/cardano-basics#which-environment-should-i-use) Which Environment Should I Use? If you’re new to Cardano, start with **Preprod** to gain confidence. Once you’ve mastered the basics, move to Mainnet for real-world interactions. ### [](https://dev.ada-anvil.io/anvil-api/cardano-basics#step-5-exploring-the-blockchain) Step 5: Exploring the Blockchain Once you've sent your first transaction or interacted with a dApp, you might wonder: **Where can I see my activity on the blockchain?** This is where blockchain explorers come in. #### [](https://dev.ada-anvil.io/anvil-api/cardano-basics#what-is-a-blockchain-explorer) What Is a Blockchain Explorer? A blockchain explorer is like a search engine for the blockchain. You can use it to: * Look up your wallet address to see balances and transactions. * Verify the details of any transaction. * Explore staking pools and network statistics. #### [](https://dev.ada-anvil.io/anvil-api/cardano-basics#recommended-explorers) Recommended Explorers * **For Preprod (Testnet):** * [Preprod CExplorer](https://preprod.cexplorer.io/) * [Preprod Cardano Scan](https://preprod.cardanoscan.io/) * **For Mainnet (Live Network):** * [CExplorer](https://cexplorer.io/) * [Cardano Scan](https://cardanoscan.io/) * **For Preview (Experimental Network):** * [Preview CExplorer](https://preview.cexplorer.io/) * [Preview Cardano Scan](https://preview.cardanoscan.io/) #### [](https://dev.ada-anvil.io/anvil-api/cardano-basics#how-to-use-an-explorer) How to Use an Explorer 1. **Search Your Wallet Address:** Copy your public wallet address and paste it into the search bar. 2. **View Transactions:** Click on individual transactions to see details like inputs, outputs, and fees. 3. **Explore Staking Pools:** Use the explorer to find staking pools and their performance metrics. Cardano offers an innovative and user-friendly blockchain experience. With these tools and insights, you’re well-equipped to start your journey confidently. Now, it’s time to choose a wallet and dive into Cardano! 🚀 [PreviousAnvil API](https://dev.ada-anvil.io/) [NextUsage](https://dev.ada-anvil.io/anvil-api/usage) Last updated 3 months ago Was this helpful? --- # Usage | Anvil Development Agency [](https://dev.ada-anvil.io/anvil-api/usage#usage) Usage ------------------------------------------------------------- Welcome to the **Anvil API Usage** page! This section outlines the different environments available to interact with the Cardano blockchain, utilizing Anvil’s infrastructure. Each environment caters to a specific stage of development, or type of deployment, so you can choose the one that aligns best with your goals. ### [](https://dev.ada-anvil.io/anvil-api/usage#why-multiple-environments) Why Multiple Environments? Cardano supports multiple network “environments,” each intended for different purposes. These environments allow you to test without risking native ADA, preview upcoming features, or run production-grade services on the live (Mainnet) network. Anvil provides separate endpoints for each environment to seamlessly move from experimentation to production while keeping the same API methods. * * * [](https://dev.ada-anvil.io/anvil-api/usage#overview-of-anvil-environments) Overview of Anvil Environments --------------------------------------------------------------------------------------------------------------- Below is a list of our available endpoints and their corresponding Cardano environments, along with links to detailed documentation and interactive API reference (Swagger UI). ### [](https://dev.ada-anvil.io/anvil-api/usage#available-endpoints) Available Endpoints Environment Endpoint Mainnet `https://prod.api.ada-anvil.app/v2/services/` Preprod `https://preprod.api.ada-anvil.app/v2/services/` Preview `https://preview.api.ada-anvil.app/v2/services/` Staging (Not for public use) `https://staging.api.ada-anvil.app/v2/services/` Development (Not for public use) `https://dev.api.ada-anvil.app/v2/services/` Test (Not for public use) `https://test.api.ada-anvil.app/v2/services/` Private Cluster Contact Us for details **Note**: To access any of these endpoints, you must include your **API Key** in the request header. See the next page on Authentication for details on generating and using your API Key. ### [](https://dev.ada-anvil.io/anvil-api/usage#documentation-links) Documentation Links Environment Cardano Documentation OpenAPI (Swagger) Mainnet [Mainnet Documentation](https://book.play.dev.cardano.org/env-mainnet.html) [Swagger UI](https://prod.api.ada-anvil.app/v2/services/swagger/ui) Preprod [Preprod Documentation](https://book.play.dev.cardano.org/env-preprod.html) [Swagger UI](https://preprod.api.ada-anvil.app/v2/services/swagger/ui) Preview [Preview Documentation](https://book.play.dev.cardano.org/env-preview.html) [Swagger UI](https://preview.api.ada-anvil.app/v2/services/swagger/ui) Staging (Not for public use) \- [Swagger UI](https://staging.api.ada-anvil.app/v2/services/swagger/ui) Development (Not for public use) \- [Swagger UI](https://dev.api.ada-anvil.app/v2/services/swagger/ui) Test (Not for public use) \- [Swagger UI](https://test.api.ada-anvil.app/v2/services/swagger/ui) Private Cluster \- \- * * * ### [](https://dev.ada-anvil.io/anvil-api/usage#choosing-the-right-environment) Choosing the Right Environment * **Mainnet** * **Who It’s For**: Production-ready dApps, applications handling real ADA or NFTs, and live user interactions. * **Key Benefits**: Fully decentralized, real economic value, backed by Cardano’s production network. * **Potential Risks**: Transactions have real financial implications and network fees. * **Preprod** * **Who It’s For**: Users testing smart contracts, token minting, or new features without risking actual ADA. * **Key Benefits**: Closely mimics Mainnet behavior, using test tokens (TestAda). * **Ideal Use Cases**: Finalizing a project’s functionality before going live. * **Preview** * **Who It’s For**: Developers who want to experiment with upcoming Cardano features. * **Key Benefits**: See how new Cardano protocol changes may impact your code. * **Ideal Use Cases**: Testing compatibility with future network updates. * **Private Cluster** * **Who It’s For**: Enterprise solutions requiring a dedicated environment, or specialized compliance needs. * **Key Benefits**: Customizable settings, enhanced privacy, and dedicated resources. * **How to Access**: Reach out to Anvil to discuss your specific requirements. * **Development, Staging & Test (Internal)** * **Who It’s For**: Anvil’s internal testing, staging, and QA processes. * **Availability**: Not publicly accessible—used for advanced in-house development. * * * [](https://dev.ada-anvil.io/anvil-api/usage#getting-started-your-first-api-call) Getting Started: Your First API Call -------------------------------------------------------------------------------------------------------------------------- Once you have chosen an environment and [obtained your API key](https://dev.ada-anvil.io/anvil-api/authentication) , you can make your first request. 1. **Construct the Request**: Use the correct endpoint for your chosen environment from the table above. 2. **Include Your API Key**: Add the `X-Api-Key` header with your key. Here is a basic example using cURL to check the health of the Preprod environment: Copy curl -X GET \ -H "Content-Type: application/json" \ -H "X-Api-Key: YOUR_API_KEY_HERE" \ https://preprod.api.ada-anvil.app/v2/services/health A successful request will return a JSON response confirming the service is operational. * * * [](https://dev.ada-anvil.io/anvil-api/usage#next-steps) Next Steps ----------------------------------------------------------------------- Now that you've made a successful call, you're ready to explore more complex features: * **Dive Into Guides**: Check out the detailed guides (e.g., **Minting**, **Transactions**) in the sidebar. * **Review Best Practices**: Read through our [Troubleshooting](https://dev.ada-anvil.io/anvil-api/troubleshooting) and [FAQ](https://dev.ada-anvil.io/anvil-api/faq) pages for common tips and solutions. * * * [](https://dev.ada-anvil.io/anvil-api/usage#common-pitfalls) Common Pitfalls --------------------------------------------------------------------------------- * **Mixing Environments**: Ensure your transaction details (like UTXOs), and wallets match the environment you are calling (e.g., don't use Mainnet assets on Preprod). * **Forgetting the API Key**: Requests without a valid `X-Api-Key` header will be rejected. [PreviousCardano Basics](https://dev.ada-anvil.io/anvil-api/cardano-basics) [NextAuthentication](https://dev.ada-anvil.io/anvil-api/authentication) Last updated 16 days ago Was this helpful? --- # Fees | Anvil Development Agency ### [](https://dev.ada-anvil.io/anvil-api/fees#fee-structure) Fee Structure Anvil API charges the following fees for operations on the Cardano blockchain: These fees are paid by the end-customer and not the developers building on top of the Anvil API. Operation Fee Transaction 0.15 ADA Smart Contract Interaction 0.15 ADA Minting 1 ADA #### [](https://dev.ada-anvil.io/anvil-api/fees#fee-details) Fee Details **Processing Fee** A fee of **0.15 ADA** is charged to the end user for each Anvil API interaction processed. This covers the cost of processing and validating the interaction on the Cardano blockchain. **Smart Contract Interaction Fee** A fee of **0.15 ADA** is charged to the end user for each smart contract interaction processed. This includes contract deployment and any other operations that involve interacting with smart contracts on the Cardano blockchain. **Minting Fee** A fee of **1 ADA** is charged to the end user for each minting operation. This covers the additional resources required for token creation and metadata management when minting NFTs or fungible tokens. > **UI/UX Note for Developers**: To ensure a transparent user experience, applications should clearly communicate the total cost of interaction to end users before they approve transactions. This includes explaining that Anvil API fees will appear as charges from an address that is not your application's address. ### [](https://dev.ada-anvil.io/anvil-api/fees#billing-and-payment) Billing and Payment Fees are automatically calculated and deducted during the respective operations. No additional action is required from your side to process these payments. ### [](https://dev.ada-anvil.io/anvil-api/fees#questions) Questions If you have any questions about our fee structure or billing process, please contact our support team for assistance at [\[email protected\]](https://dev.ada-anvil.io/cdn-cgi/l/email-protection) [PreviousAuthentication](https://dev.ada-anvil.io/anvil-api/authentication) [NextFAQ](https://dev.ada-anvil.io/anvil-api/faq) Last updated 16 days ago Was this helpful? --- # Authentication | Anvil Development Agency [](https://dev.ada-anvil.io/anvil-api/authentication#authentication) Authentication ---------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/anvil-api/authentication#overview) Overview To interact with the Anvil API, you’ll need an **API Key**. Think of the API Key as your personal pass to our Cardano infrastructure—each key enforces daily usage limits and burst configurations tailored to your project’s needs. **Important**: Treat your API Key like a password. If someone else gains access to it, they can use your quota and potentially access your resources. * * * ### [](https://dev.ada-anvil.io/anvil-api/authentication#how-to-get-your-api-key) How to Get Your API Key Visit our **self-serve portal** at [ada-anvil.io/api-keys](https://ada-anvil.io/api-keys) to generate your keys instantly. If the portal is unavailable, please email us at `[[email protected]](https://dev.ada-anvil.io/cdn-cgi/l/email-protection) `. * * * [](https://dev.ada-anvil.io/anvil-api/authentication#how-to-use-your-api-key) How to Use Your API Key ---------------------------------------------------------------------------------------------------------- You will receive two API keys from Anvil: * **Testnet Key**: Use this key for all non-production Cardano environments, including **Preprod** and **Preview**. * **Mainnet Key**: Use this key exclusively for the Cardano **Mainnet** environment. To authenticate your requests, include the appropriate key in the `X-Api-Key` header. ### [](https://dev.ada-anvil.io/anvil-api/authentication#basic-curl-example) Basic cURL Example Copy curl -X GET \ -H "Content-Type: application/json" \ -H "X-Api-Key: YOUR_API_KEY_HERE" \ https://prod.api.ada-anvil.app/v2/services/health **Endpoint**: Replace with the environment you want to interact with (e.g., `preprod.api.ada-anvil.app`, `preview.api.ada-anvil.app`, etc.). **Method**: Varies depending on the API call (GET, POST, etc.). **Headers**: Always include your `X-Api-Key`. ### [](https://dev.ada-anvil.io/anvil-api/authentication#basic-nodejs-example) Basic NodeJS Example Copy async function checkHealth() { try { const response = await fetch( "https://prod.api.ada-anvil.app/v2/services/health", { headers: { "X-Api-Key": "YOUR_API_KEY_HERE", }, }, ); console.log("Health Check:", await response.json()); } catch (error) { console.error("Error:", error); } } (async () => { await checkHealth(); })(); ### [](https://dev.ada-anvil.io/anvil-api/authentication#python-requests-example) Python (Requests) Example Copy import requests def check_health(): url = "https://prod.api.ada-anvil.app/v2/services/health" headers = { "X-Api-Key": "YOUR_API_KEY_HERE", "Content-Type": "application/json" } response = requests.get(url, headers=headers) if response.status_code == 200: print("Health Check:", response.json()) else: print("Error:", response.status_code, response.text) check_health() * * * [](https://dev.ada-anvil.io/anvil-api/authentication#rate-limits-and-quotas) Rate Limits and Quotas -------------------------------------------------------------------------------------------------------- Every API Key has **usage limits** that define: * **Maximum requests per day** * **Requests per second (RPS)** * **Burst limits** for short-term high-traffic events These are set during onboarding. If you anticipate large spikes, contact us to adjust your limits. **Tip**: Exceeding your rate limits may result in 429 (Throttled, please try again later) errors. In such cases, either reduce your request frequency or upgrade your quota. * * * [](https://dev.ada-anvil.io/anvil-api/authentication#best-practices) Best Practices ---------------------------------------------------------------------------------------- 1. **Store Keys Securely** * Use environment variables or a secure key management service. * Avoid committing keys to public Git repositories. 1. **Rotate Keys Periodically** * If you suspect your key has been exposed, email us immediately for revocation and re-issuance. * Regular key rotation is a good security measure. 1. **Use Separate Keys for Each Environment** * Keep your Preprod and Mainnet usage separate for better debugging and resource tracking. 1. **Handle Errors Gracefully** * Look for `401 Unauthorized` (invalid or missing API key) or `Throttled, please try again later.`. * Implement retry logic with exponential backoff to avoid overwhelming the service. * * * [](https://dev.ada-anvil.io/anvil-api/authentication#common-troubleshooting) Common Troubleshooting -------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/anvil-api/authentication#id-401-unauthorized) `401 Unauthorized` Check that you set the `X-Api-Key` header exactly as provided. Confirm the key belongs to the specific environment you’re calling. ### [](https://dev.ada-anvil.io/anvil-api/authentication#throttled-please-try-again-later) `Throttled, please try again later.` You may have hit your burst or daily limit. Throttle your requests or contact support for higher limits. ### [](https://dev.ada-anvil.io/anvil-api/authentication#incorrect-endpoint) `Incorrect Endpoint` Verify you’re using the correct URL (Mainnet vs. Preprod vs. Preview). Refer to the Usage page for correct endpoints. * * * [](https://dev.ada-anvil.io/anvil-api/authentication#moving-forward) Moving Forward ---------------------------------------------------------------------------------------- Once your key is active, you can start exploring all the functionality that **Anvil’s Cardano APIs** offer—everything from minting tokens to submitting transactions and checking balances. Need guidance on environment selection? Head back to our Usage page for a refresher. **Next Steps:** * Dive deeper into Select Cardano Environment to ensure you’re building on the right network for your needs. * Explore UTXOs vs. Change Address to understand Cardano’s transaction model. * Build your first transaction or NFT mint using our step-by-step guides in the sidebar. With your **API Key** in hand and the right environment chosen, you’re ready to harness the power of Cardano through Anvil—securely and efficiently! [PreviousUsage](https://dev.ada-anvil.io/anvil-api/usage) [NextFees](https://dev.ada-anvil.io/anvil-api/fees) Last updated 16 days ago Was this helpful? --- # Troubleshooting | Anvil Development Agency When building on the Anvil API (Cardano), you may encounter various errors related to transaction submission, UTXOs, API components, or authorization. This page documents common error messages, explains their causes (Cardano-specific vs Anvil-specific), and provides solutions or workarounds for each. [](https://dev.ada-anvil.io/anvil-api/troubleshooting#transaction-submission-failures) Transaction Submission Failures --------------------------------------------------------------------------------------------------------------------------- These errors occur when a transaction fails Cardano's validation rules during building or submission. They are typically Cardano-specific (originating from the Cardano node/ledger) but can surface through the Anvil API's responses. **Missing Required Signatures (MissingVKeyWitnessesUTXOW)**[](https://dev.ada-anvil.io/anvil-api/troubleshooting#missing-required-signatures-missingvkeywitnessesutxow) **What it means**: The node is telling you that one or more required transaction signatures (vkey witnesses) are missing. The error MissingVKeyWitnessesUTXOW appears when the transaction hasn't been signed by all the necessary keys needed to validate the transaction. **Cardano or Anvil**: Cardano-specific (signature validation failure). Anvil passes along the node's error. **Solution**: 1. **Include all signatures**: Ensure all required signatures are present in the transaction: * For ADA spending: Include the address's payment signing key * For minting/burning: Include the native script's keyhash (minting/burning tokens specifically requires the policy signature) * For script interactions: Include any required script witnesses 2. **Complete frontend signing**: When building via API and signing on frontend, verify the signed payload contains all required signatures **Transaction Validity Interval Error (OutsideValidityIntervalUTxO)**[](https://dev.ada-anvil.io/anvil-api/troubleshooting#transaction-validity-interval-error-outsidevalidityintervalutxo) **What it means**: The transaction's validity interval is outside the current blockchain slot range. This error indicates that the transaction is either expired (current slot > end slot/TTL) or not yet valid (current slot < start slot). **Cardano or Anvil**: Cardano-specific (timing validation). **API Details**: * In Anvil's API, you can specify the validity interval using the `validityInterval` parameter: Copy { "validityInterval": { "start": number | false, // POSIX timestamp or slot number (optional) "end": number | false // POSIX timestamp or slot number (optional) } } * **Default values** (when not specified): * `start`: Current blockchain slot (from chain indexer) * `end`: Current blockchain slot + 2 hours (from chain indexer) * Set a boundary to `false` to disable it entirely **Common scenarios**: * Transaction submitted after its TTL has expired * Transaction submitted before its start time is reached * Clock differences between systems * Chain indexer issues causing incorrect validity interval calculations **Solution**: 1. **Explicitly set validity interval**: Instead of relying on defaults, specify both `start` and `end`: * For immediate transactions: Set `start` to a recent slot and `end` to at least 30 minutes in the future * For scheduled transactions: Set appropriate future `start` time and ensure `end` provides enough window 2. **Use relative values**: Calculate values relative to current time rather than using absolute slots 3. **Chain indexer issues**: If you've verified your validity interval is correct but still get this error, the chain indexer may be out of sync. In this case contact Anvil support if the issue persists **Note on terminology**: Cardano error messages use `invalidBefore` (= Anvil's `start`) and `invalidHereafter` (= Anvil's `end`). **Smart Contract Script Failure (Validation Error 3010/3012)**[](https://dev.ada-anvil.io/anvil-api/troubleshooting#smart-contract-script-failure-validation-error-3010-3012) **What it means**: Your smart contract returned `False` during validation or encountered an execution error. Errors typically appear with codes 3010 ("Some scripts of the transaction terminated with errors") or 3012 ("Some scripts failed to evaluate to a positive outcome"). **Cardano or Anvil**: Cardano-specific (on-chain script validation). **Common causes**: * Contract logic rejected the transaction parameters * Incorrect datum or redeemer values * Missing required inputs or signatures * Execution units exceeded **Solution**: 1. **Review error traces**: The `data.validationError` and `data.traces` fields contain valuable debugging information 2. **For Aiken contracts**: Rebuild your contract with verbose mode to get detailed execution traces: Copy aiken build --verbose 3. **Check parameters**: Verify all datum values, redeemers, and required signatures This error is specific to your smart contract's logic - the solution depends entirely on the contract code and the validation conditions it requires. **Malformed Transaction Submission (Raw CBOR Decode Error)**[](https://dev.ada-anvil.io/anvil-api/troubleshooting#malformed-transaction-submission-raw-cbor-decode-error) **What it means**: The node cannot decode your transaction because the CBOR format is incorrect. Error messages include "DecoderErrorDeserialiseFailure" or "RawCborDecodeError". **Cardano or Anvil**: Cardano error (node-level validation). **Common causes**: 1. **Incorrect encoding**: Transaction must be CBOR Hex Encoded 2. **Missing signatures**: Incomplete transaction witness data 3. **Improper signing workflow**: Incorrect signature appending **Solution**: 1. **Use proper encoding**: When submitting via REST, ensure you're using CBOR Hex Encoded format 2. **Verify complete signatures**: Make sure all required signatures are included 3. **Follow correct signing flow**: When using Anvil's API where the backend returns an unsigned tx hash to be signed by the frontend, append signatures only in the designated witness fields [](https://dev.ada-anvil.io/anvil-api/troubleshooting#utxo-input-issues) UTXO/Input Issues ----------------------------------------------------------------------------------------------- Errors in this category relate to UTXOs (Unspent Transaction Outputs) and inputs selection for your transactions. They might be Cardano errors triggered by incorrect input usage or Anvil-specific validations about how inputs are provided. **Insufficient Inputs (Balance Shortage)**[](https://dev.ada-anvil.io/anvil-api/troubleshooting#insufficient-inputs-balance-shortage) **What it means**: Your transaction inputs don't have enough value to cover all outputs and fees. The error typically shows: "Insufficient input in transaction. shortage: {ada in inputs: X, ada in outputs: Y, fee: Z}". **Key requirement**: The rule "ada in inputs" must be >= ("ada in outputs" + fee) before adding change. **Cardano or Anvil**: Cardano-specific (transaction balancing). **Common causes**: * Insufficient ADA for outputs, fees, and minimum deposit requirements * Missing tokens you're trying to send * Wrong network (using testnet addresses when on mainnet or vice versa) **Solution**: 1. **Add more inputs**: Include additional UTXOs from your wallet 2. **Check token presence**: Verify the UTXO contains the specific tokens (NFTs, FTs, and/or ADA) you're sending 3. **Verify network**: Ensure you're using addresses on the correct network This error can apply to both ADA and native assets (NFTs and tokens). **Using a Spent or Invalid UTXO (BadInputsUTxO / ValueNotConservedUTxO)**[](https://dev.ada-anvil.io/anvil-api/troubleshooting#using-a-spent-or-invalid-utxo-badinputsutxo-valuenotconservedutxo) **What it means**: This error combo occurs when one of the UTXO inputs you included is not actually available to spend. BadInputsUTxO means the transaction is referencing an input that the ledger finds invalid – typically because it's already spent (or doesn't exist). **Cardano or Anvil**: Cardano-specific (UTXO set validation). Anvil just surfaces it. **Solution**: 1. **Check UTXO status**: If you recently used a UTXO in a transaction, consider it already spent 2. **Refresh your wallet**: Always fetch the latest UTXOs from the address by refreshing your wallet before building a new transaction 3. **Use different UTXOs**: For multiple back-to-back transactions, use separate UTXOs for each transaction 4. **Inspect error details**: If the error persists, check the specific TxIn hash mentioned in the error message **UTXO Fragmentation (UtxoNotEnoughFragmented / "Wallet fragmentation error")**[](https://dev.ada-anvil.io/anvil-api/troubleshooting#utxo-fragmentation-utxonotenoughfragmented-wallet-fragmentation-error) **What it means**: "Wallet fragmentation" refers to the distribution of funds across UTXOs. An error like UtxoNotEnoughFragmented means you don't have enough separate UTXOs to fulfill all the outputs you are trying to create. **Cardano or Anvil**: This is more of a higher-level Cardano wallet limitation/feature. **Solution**: 1. **Consolidate and re-split**: If your funds are in a single UTXO, first create a transaction to split it into multiple smaller UTXOs 2. **Reduce batch size**: Break large multi-recipient transactions into smaller batches with fewer outputs 3. **Check UTXO count**: Use wallet tools to verify your UTXO count matches your transaction requirements 4. **Verify balance**: Ensure you have sufficient funds available if the error is actually related to balance **Providing UTXOs vs. Using an Address (Blockfrost "Component not found")**[](https://dev.ada-anvil.io/anvil-api/troubleshooting#providing-utxos-vs.-using-an-address-blockfrost-component-not-found) **What it means**: No UTXOs found for the provided address. The error appears as "The requested component has not been found" with internal code -32603 or HTTP status 500 from Blockfrost. **Cardano or Anvil**: Blockfrost response error when querying address UTXOs. **Address format support**: * Anvil API supports both bech32 addresses (addr1...) and hex-encoded addresses * The address must be valid and properly formatted **Solution**: 1. **Fund the address first**: Ensure the address has received at least one transaction 2. **Use explicit UTXOs**: * **Frontend**: Connect through wallet interfaces (e.g., [Weld](https://github.com/Cardano-Forge/weld) ) to access UTXOs directly from the user's wallet * **Backend**: Fetch UTXOs using Blockfrost or other Cardano service providers and provide them in the `utxos` field of your build request * **Note**: Backend UTXO handling for your address may vary based on wallet settings (e.g., Eternl multi-account configuration) See [Selecting UTXOs](https://dev.ada-anvil.io/guides/transaction/selecting-utxos) for more detailed information. 3. **Verify address format**: Double-check that you're using a valid bech32 or hex-encoded address format This error simply means there are no UTXOs associated with the address you provided. **Mainnet UTXO Selection Requirement ("Utxos must be set on mainnet")**[](https://dev.ada-anvil.io/anvil-api/troubleshooting#mainnet-utxo-selection-requirement-utxos-must-be-set-on-mainnet) **What it means**: This is an Anvil API validation error (HTTP 400) that explicitly requires you to provide UTXO inputs in the request when operating on mainnet. The error message Input validation failed ... "Utxos must be set on mainnet" is telling you that unlike test environments, the mainnet API will not automatically pick UTXOs for you. **Cardano or Anvil**: Anvil-specific. This is not coming from Cardano node, but from Anvil's API input checker. **Solution**: 1. **Retrieve UTXOs**: First, fetch the UTXOs for the wallet address you want to spend from 2. **Include UTXOs in request**: Add the `utxos` field to your build transaction request, listing each UTXO you want to use as input. See [Selecting UTXOs](https://dev.ada-anvil.io/guides/transaction/selecting-utxos) for more details. 3. **Verify network**: Confirm you're correctly pointing to mainnet This requirement exists to ensure developers make deliberate decisions about which UTXOs to spend when operating on mainnet. **Policy Script Hash Mismatch (UNPROCESSABLE\_CONTENT)**[](https://dev.ada-anvil.io/anvil-api/troubleshooting#policy-script-hash-mismatch-unprocessable_content) **What it means**: The error message "Failed to build transaction > Unable to get policy scripts > Policy script and policy id do not match" indicates that the native script content doesn't match the policy ID hash being used. **Cardano or Anvil**: This is primarily a Cardano validation error that Anvil surfaces. **Common causes**: * The native script JSON in the preloaded script section was altered but the policy ID wasn't updated accordingly * The policy ID was manually entered or calculated incorrectly * Using the wrong script version for a specific policy ID **Solution**: 1. **Verify script content**: The native script (the json in the preloaded script section) is different from the hash being used. You should double check that you didn't alter the native script and generate a new hash if necessary. 2. **Regenerate hash if needed**: If you intentionally modified the script, you need to regenerate the hash to match. 3. **Use consistent script**: Ensure you're using exactly the same script content throughout your application. **Invalid Address Format (ScriptWitnessNotValidatingUTXOW)**[](https://dev.ada-anvil.io/anvil-api/troubleshooting#invalid-address-format-scriptwitnessnotvalidatingutxow) **What it means**: When you see a transaction submission error containing "ConwayUtxoValidationError" and "ScriptWitnessNotValidatingUTXOW" followed by "missing human-readable separator", it typically indicates an invalid address format is being used. The error message appears as: "Failed to submit tx > {"contents":{"contents":{"era":"ShelleyBasedEraConway","error":{"ConwayUtxoValidationError (ScriptWitnessNotValidatingUTXOW ...", code: "UNPROCESSABLE\_CONTENT", error: 'missing human-readable separator, "!"' **Cardano or Anvil**: This is a Cardano validation error related to address format checking. **Common causes**: * Using a malformed address that's neither a valid hex-encoded nor bech32 format * Truncated or corrupted address strings * Mixing address formats from different networks (testnets vs mainnet) **Solution**: 1. **Validate address format**: Ensure all addresses in your transaction are either valid bech32 format (starting with addr1..., addr\_test1..., etc.) or properly hex-encoded 2. **Check for truncation**: Verify that addresses haven't been truncated during processing 3. **Use network-appropriate addresses**: Confirm you're using addresses appropriate for the network you're targeting (mainnet vs testnet) [](https://dev.ada-anvil.io/anvil-api/troubleshooting#component-or-endpoint-errors) Component or Endpoint Errors --------------------------------------------------------------------------------------------------------------------- These errors are related to specific components of the Anvil platform or external endpoints it relies on (like Blockfrost). **External Data Not Yet Available (Policy Script/CBOR Not Found)**[](https://dev.ada-anvil.io/anvil-api/troubleshooting#external-data-not-yet-available-policy-script-cbor-not-found) **What it means**: This error occurs when Anvil tries to retrieve on-chain data that hasn't settled yet. The error message typically looks like: "Unable to get policy scripts > Failed to get cbor for tx hash > The requested component has not been found" with code 422. **Cardano or Anvil**: This is a timing issue related to blockchain confirmation times. **Common causes**: 1. **Transaction not yet settled**: The transaction containing the native script hasn't been fully confirmed **Solution**: 1. **Wait for on-chain settlement**: Allow more time for the transaction to be confirmed before trying to use its data 2. **Implement retry logic**: If building an application, add a delay and retry mechanism 3. **Check network**: Verify you're looking for the transaction on the same network where it was submitted [](https://dev.ada-anvil.io/anvil-api/troubleshooting#authorization-access-issues) Authorization/Access Issues ------------------------------------------------------------------------------------------------------------------- These errors relate to authentication (API keys) or account-level access permissions in Anvil. **Invalid or Missing API Key (401 Unauthorized)**[](https://dev.ada-anvil.io/anvil-api/troubleshooting#invalid-or-missing-api-key-401-unauthorized) **What it means**: A 401 Unauthorized response indicates the request did not have proper authentication. The Anvil API requires an API key (in the X-Api-Key header) for every request. **Solution**: 1. **Obtain API key**: Request an API key from Anvil if you don't already have one 2. **Use correct header format**: Include the header exactly as documented: `X-Api-Key: ` 3. **Verify environment URL**: Ensure you're calling the correct base URL for your key's environment (testnet vs mainnet) 4. **Secure your key**: Store your API key securely and don't expose it in client-side code **Throttled, please try again later.**[](https://dev.ada-anvil.io/anvil-api/troubleshooting#throttled-please-try-again-later) **What it means**: A Throttled response occurs when you're making requests faster than the allowed burst rate limit. **Cardano or Anvil**: Anvil-specific. This is our API rate limiting control. **Solution**: 1. **Implement backoff strategy**: Add exponential backoff with retries when you detect a Throttled response 2. **Space out requests**: Ensure your application doesn't send too many requests in quick succession 3. **Batch operations**: Where possible, combine multiple operations into single API calls 4. **Monitor usage**: Track your API consumption to anticipate potential throttling issues **Daily quota exceeded, please contact Anvil support.**[](https://dev.ada-anvil.io/anvil-api/troubleshooting#daily-quota-exceeded-please-contact-anvil-support) **What it means**: This error occurs when you've exceeded your daily API quota allocation. **Cardano or Anvil**: Anvil-specific. This is an API usage quota control. **Solution**: 1. **Review API usage**: Analyze your daily API usage patterns to identify optimization opportunities 2. **Optimize requests**: Reduce unnecessary API calls in your application code 3. **Request quota increase**: If your use case legitimately requires a higher quota, contact Anvil support at [\[email protected\]](https://dev.ada-anvil.io/cdn-cgi/l/email-protection) 4. **Implement caching**: Where appropriate, cache API responses to reduce the number of calls **Blueprint Validator Ownership Conflict (Validators in Another Blueprint)**[](https://dev.ada-anvil.io/anvil-api/troubleshooting#blueprint-validator-ownership-conflict-validators-in-another-blueprint) **What it means**: This error message: "Failed to upsert blueprint: Some validators are associated with another blueprint" (500 Internal Server Error) occurs when you attempt to register or update a smart contract blueprint (CIP-57 blueprint) that contains validators (scripts) which are already tied to a different blueprint. **Cardano or Anvil**: Anvil-specific – this is about Anvil's blueprint management component, not a Cardano on-chain error. **Solution**: 1. **Contact support**: Reach out to Anvil support with details about your account, the blueprint ID/name, and the specific script hashes 2. **Verify ownership**: Be prepared to verify your identity and project ownership as requested 3. **Blueprint resolution**: The Anvil team may clear or merge blueprint records so you can upload your blueprint again [PreviousFAQ](https://dev.ada-anvil.io/anvil-api/faq) [NextSupport](https://dev.ada-anvil.io/anvil-api/support) Last updated 11 days ago Was this helpful? --- # FAQ | Anvil Development Agency [](https://dev.ada-anvil.io/anvil-api/faq#getting-started) Getting Started ------------------------------------------------------------------------------- **How to test the Anvil API?**[](https://dev.ada-anvil.io/anvil-api/faq#how-to-test-the-anvil-api) To get started with the Anvil API: 1. **Get Your API Keys**: Visit our self-serve portal at [ada-anvil.io/api-keys](https://ada-anvil.io/api-keys) to generate your keys instantly. If the portal is unavailable, please email us at `[[email protected]](https://dev.ada-anvil.io/cdn-cgi/l/email-protection) `. * A Preprod key for development and testing * A Mainnet key with lower rate limits for basic production use 2. **Make Your First API Call**: Include your API key in the X-Api-Key header: Copy curl -X GET \ -H "Content-Type: application/json" \ -H "X-Api-Key: YOUR_API_KEY_HERE" \ https://preprod.api.ada-anvil.app/v2/services/health 3. **Explore the Documentation**: See our [Build Your First Transaction](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction) guide or [Cardano Basics](https://dev.ada-anvil.io/anvil-api/cardano-basics) if you're new to Cardano. For more details about authentication, see our [Authentication](https://dev.ada-anvil.io/anvil-api/authentication) documentation. **What do I need to test on different Cardano environments?**[](https://dev.ada-anvil.io/anvil-api/faq#what-do-i-need-to-test-on-different-cardano-environments) > You need two types of API keys: one for testnet environments and one for mainnet, along with the correct endpoint for each network. The correct endpoints are: * Preprod: `https://preprod.api.ada-anvil.app/v2/services` * Preview: `https://preview.api.ada-anvil.app/v2/services` * Mainnet: `https://prod.api.ada-anvil.app/v2/services` **Is there a difference between testnet and pre-prod, or are they the same thing?**[](https://dev.ada-anvil.io/anvil-api/faq#is-there-a-difference-between-testnet-and-pre-prod-or-are-they-the-same-thing) They are related but distinct: * **Testnet** refers to any non-production Cardano network. Your testnet API key works across all testnet environments (both Preprod and Preview). * **Preprod** is a specific testnet environment that closely mirrors mainnet's parameters. * **Preview** is another testnet environment focused on testing future protocol changes. Both Preprod and Preview use test ADA (not real value), but they may be running different protocol versions. For most testing purposes, Preprod is recommended. **Is my tech compatible with the API?**[](https://dev.ada-anvil.io/anvil-api/faq#is-my-tech-compatible-with-the-api) The Anvil API is designed for maximum flexibility and compatibility across technology stacks: * **Language Agnostic**: Works with any programming language that can make HTTP requests (JavaScript, Python, Ruby, Go, Java, etc.) * **Multiple Interface Options**: Supports both REST API and tRPC for type-safe operations * **Standard JSON Format**: Uses standard JSON for request and response bodies * **Simple Authentication**: Uses straightforward API key authentication via HTTP headers This means you can easily integrate the Anvil API with: * Web applications (React, Vue, Angular, etc.) * Mobile apps (React Native, Flutter, native iOS/Android) * Backend servers (Node.js, Python, PHP, Ruby, Java, Rust, Go, C#, etc.) * Command-line tools and scripts [](https://dev.ada-anvil.io/anvil-api/faq#transaction-building) Transaction Building ----------------------------------------------------------------------------------------- **Can I build and send Cardano transactions entirely from the client-side (browser)?**[](https://dev.ada-anvil.io/anvil-api/faq#can-i-build-and-send-cardano-transactions-entirely-from-the-client-side-browser) Yes, but with important security considerations: * **Never expose your API key in client-side code** - API keys should always be kept on your server side * **Be aware of transaction privacy** - Direct client-side communication can expose transaction details to network observers or browser extensions The recommended approach is a hybrid architecture: 1. **Frontend (Browser)**: Use [Weld](https://github.com/Cardano-Forge/weld) to connect wallets and handle transaction signing Copy const wallet = useWallet(); if (wallet.isConnected) { // Frontend only handles wallet connection and signing const signature = await wallet.handler.signTx(txCbor); } 2. **Backend (Your Server)**: Create a secure backend service that: * Holds your Anvil API key securely * Communicates with Anvil API to build transactions * Sends the transaction CBOR to your frontend for signing * Receives the signature and submits the completed transaction This architecture ensures your API key remains secure while still leveraging client-side wallet capabilities. For testing purposes only, you can use the browser console to interact directly with a wallet: Copy const w = await window.cardano.eternl.enable(); await w.signTx("Your transaction CBOR", true); **How do I sign a transaction to send ADA or NFTs?**[](https://dev.ada-anvil.io/anvil-api/faq#how-do-i-sign-a-transaction-to-send-ada-or-nfts) For signing transactions: 1. **Browser/Frontend**: Use Weld to connect to the user's wallet and request signatures Copy // First connect to the wallet using Weld const wallet = useWallet(); if (wallet.isConnected) { // Get the wallet to sign your transaction CBOR const signature = await wallet.handler.signTx(txCbor); // Submit the signed transaction // ... } For detailed backend signing examples in multiple languages, see our comprehensive [Sign Transaction guide](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/transaction/signing-transaction.md) . **Do I need to provide UTXOs when building transactions?**[](https://dev.ada-anvil.io/anvil-api/faq#do-i-need-to-provide-utxos-when-building-transactions) Yes, you need to provide UTXOs when building transactions. * **For Testing ONLY (Preprod/Preview)**: You can optionally let Anvil handle UTXO selection automatically. Copy // Just provide a change address in test environments const response = await fetch('https://preprod.api.ada-anvil.app/v2/services/transactions/build', { // ... body: JSON.stringify({ changeAddress: 'addr_test...', // No UTXOs needed for testing! outputs: [/* ... */] }) }); * **For Production (Mainnet)**: You must explicitly provide UTXOs Copy // UTXOs are required in production const response = await fetch('https://prod.api.ada-anvil.app/v2/services/transactions/build', { // ... body: JSON.stringify({ utxos: ["8282...", "8282..."], changeAddress: 'addr_...', outputs: [/* ... */] }) }); See [Selecting UTXOs for Transactions](https://dev.ada-anvil.io/guides/transaction/selecting-utxos) for more details. [](https://dev.ada-anvil.io/anvil-api/faq#development-workflow) Development Workflow ----------------------------------------------------------------------------------------- **What's the recommended workflow for developing with Anvil API?**[](https://dev.ada-anvil.io/anvil-api/faq#whats-the-recommended-workflow-for-developing-with-anvil-api) 1. **Start with Preprod**: Use test ADA from the faucet to develop and test your application 2. **Test edge cases**: Ensure your code handles various scenarios correctly 3. **Optional: Test on Preview**: If you need to test with upcoming protocol features 4. **Move to Mainnet**: Once thoroughly tested, switch to Mainnet endpoints and API key Remember to keep your environments and API keys separate to avoid accidentally using real ADA during testing. **How do I handle wallet integration in my application?**[](https://dev.ada-anvil.io/anvil-api/faq#how-do-i-handle-wallet-integration-in-my-application) We strongly recommend using [Weld](https://github.com/Cardano-Forge/weld) for wallet integration. It provides: * A unified API for multiple Cardano wallets (Eternl, Lace, Flint, etc.) * Simple hooks for React applications * Comprehensive wallet state management * Utilities for common wallet operations Check our [Transaction Overview](https://dev.ada-anvil.io/guides/transaction) for a complete flow using Weld and Anvil API. [](https://dev.ada-anvil.io/anvil-api/faq#smart-contracts-and-plutus-scripts) Smart Contracts and Plutus Scripts --------------------------------------------------------------------------------------------------------------------- **Does the API support smart contract interaction and Plutus scripts?**[](https://dev.ada-anvil.io/anvil-api/faq#does-the-api-support-smart-contract-interaction-and-plutus-scripts) Yes, Anvil API fully supports Plutus smart contract interactions: 1. **Blueprint Management (CIP-57)**: Upload your compiled Plutus scripts as blueprints: Copy # Example: Create a blueprint from an Aiken compiler output curl -X POST \ -H "Content-Type: application/json" \ -H "X-Api-Key: YOUR_API_KEY" \ https://preprod.api.ada-anvil.app/v2/services/blueprints \ -d '{"blueprint": {...}}' # Your Plutus script blueprint 2. **Script Interactions**: Once uploaded, you can interact with the contract in transactions by referencing the blueprint and providing required datum/redeemer data. See our guides on [Blueprint Management](https://dev.ada-anvil.io/guides/smart-contract/blueprint-management) and example smart contracts like our [Simple Mint Smart Contract](https://dev.ada-anvil.io/guides/smart-contract/mint-smart-contract) for practical demonstrations. [](https://dev.ada-anvil.io/anvil-api/faq#api-technical-details) API Technical Details ------------------------------------------------------------------------------------------- **Are there rate limits or usage quotas?**[](https://dev.ada-anvil.io/anvil-api/faq#are-there-rate-limits-or-usage-quotas) Yes, every API key comes with defined usage limits that protect both you and the service: * **Daily Request Limits**: Maximum number of requests per 24-hour period, 300k requests per day per key * **Requests Per Second (RPS)**: Maximum throughput rate, 100 requests per second per key These limits provide important security and stability benefits: 1. **Preventing Unauthorized Use**: If your API key is compromised, rate limits minimize potential abuse by capping usage 2. **Cost Control**: Helps avoid unexpected charges by setting clear boundaries on API consumption 3. **Service Reliability**: Ensures no single client can overload the infrastructure, maintaining high availability for everyone 4. **Attack Mitigation**: Protects against DDoS and brute force attacks by throttling excessive requests If you exceed these limits, the API will respond with `Throttled, please try again later.`. Your exact quotas are configured when your API key is issued and can be tailored to your application's specific needs. For higher limits, please contact [\[email protected\]](https://dev.ada-anvil.io/cdn-cgi/l/email-protection) . **How is the transaction builder versioned?**[](https://dev.ada-anvil.io/anvil-api/faq#how-is-the-transaction-builder-versioned) The Anvil API transaction builder uses request payload versioning to maintain backward compatibility: Copy { "version": 1, // Other request parameters } Endpoints that support versioning accept a `version` field in the request body. This allows endpoints to evolve their functionality while maintaining backward compatibility with existing integrations. **How does Anvil handle blockchain upgrades and protocol changes?**[](https://dev.ada-anvil.io/anvil-api/faq#how-does-anvil-handle-blockchain-upgrades-and-protocol-changes) Anvil's infrastructure is updated automatically to support every Cardano protocol upgrade (hard forks, new ledger rules, Plutus enhancements) with zero downtime. You don't need to change your integration—your existing endpoints continue working as the underlying node software is upgraded behind the scenes. For testing upcoming features, you can point to the Preview endpoints, which run the latest protocol version before it hits Mainnet, so you can verify compatibility in advance. **How do I query blockchain data like transaction history or UTXOs?**[](https://dev.ada-anvil.io/anvil-api/faq#how-do-i-query-blockchain-data-like-transaction-history-or-utxos) The Anvil API focuses primarily on transaction building and submission rather than data querying. * **UTXO Management**: In test environments, automatic UTXO selection is available * **Production Requirements**: For mainnet, you must explicitly provide UTXOs * **Data Querying**: For blockchain exploration (transaction history, balances), we recommend using dedicated blockchain explorers or data APIs like BlockFrost Note: While not part of our standard API, we do provide specialized blockchain querying and indexing services for enterprise customers. If you need comprehensive blockchain data access for your application, please contact [\[email protected\]](https://dev.ada-anvil.io/cdn-cgi/l/email-protection) to discuss your requirements. See [Selecting UTXOs for Transactions](https://dev.ada-anvil.io/guides/transaction/selecting-utxos) for more information about working with UTXOs in Anvil API. [](https://dev.ada-anvil.io/anvil-api/faq#support-and-feedback) Support and Feedback ----------------------------------------------------------------------------------------- **I need help, I have a question or feedback**[](https://dev.ada-anvil.io/anvil-api/faq#i-need-help-i-have-a-question-or-feedback) Send an email to: [\[email protected\]](https://dev.ada-anvil.io/cdn-cgi/l/email-protection) Join the conversation in Discourse: (coming soon) [PreviousFees](https://dev.ada-anvil.io/anvil-api/fees) [NextTroubleshooting](https://dev.ada-anvil.io/anvil-api/troubleshooting) Last updated 16 days ago Was this helpful? --- # Sign Transaction | Anvil Development Agency Signing a transaction is a critical step that authorizes the movement of funds or interaction with smart contracts on the Cardano blockchain. This guide covers the primary methods for signing: on the frontend using a browser-based wallet, and on the backend using a server-side script. [](https://dev.ada-anvil.io/guides/transaction/signing-transaction#frontend-client-side-signing) Frontend (Client-Side) Signing ------------------------------------------------------------------------------------------------------------------------------------ Frontend signing is the most common method for user-centric applications. It leverages the user's installed browser wallet (like Eternl or Lace) to request a signature, ensuring that the user's private keys never leave their secure environment. ### [](https://dev.ada-anvil.io/guides/transaction/signing-transaction#using-weld) Using Weld We recommend using our [Weld](https://github.com/Cardano-Forge/weld) library to simplify interaction with browser wallets. Weld provides a unified API to connect to various wallets and request transaction signatures. ### [](https://dev.ada-anvil.io/guides/transaction/signing-transaction#using-the-browser-developer-console) Using the Browser Developer Console For quick testing, you can directly use the CIP-30 wallet API exposed in the browser's developer console. 1. Go to any website with a connected Cardano wallet (e.g., `https://ada-anvil.io`). 2. Open the Developer Console. 3. Execute the following JavaScript to enable the wallet and request a signature: Copy // Example using Eternl wallet const w = await window.cardano.eternl.enable(); // Replace with your actual transaction CBOR const signedTx = await w.signTx("Your unsigned transaction CBOR", true); console.log(signedTx); 4. Some wallets will prompt you to authorize your dApp to interact with the wallet. 5. Your wallet will then prompt you to sign the transaction. 6. The signed transaction CBOR, including the witness set, will be printed to the console. [PreviousSelect UTXOs](https://dev.ada-anvil.io/guides/transaction/selecting-utxos) [NextSubmit Transaction](https://dev.ada-anvil.io/guides/transaction/submit-transaction) Last updated 11 days ago Was this helpful? --- # Select Cardano Environment | Anvil Development Agency [](https://dev.ada-anvil.io/guides/select-cardano-environment#overview) Overview ------------------------------------------------------------------------------------- The Anvil API works across multiple Cardano environments—each serving a unique purpose in your development lifecycle. Whether you’re testing new features with fake ADA or running a full production dApp with real ADA, you’ll point your calls to different base URLs. The API behavior remains consistent, but the domain changes to reflect which network you’re on. [](https://dev.ada-anvil.io/guides/select-cardano-environment#key-environments) Key Environments: ------------------------------------------------------------------------------------------------------ * **Mainnet**: Real ADA, real economic value, live user interactions. * **Preprod**: A Cardano testnet for safe experimentation (e.g., CIP-68 tokens, CIP-25 minting) without risking real funds. * **Preview**: Another testnet, often used to test upcoming features or protocol changes. [](https://dev.ada-anvil.io/guides/select-cardano-environment#choosing-an-environment) Choosing an Environment ------------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/select-cardano-environment#mainnet) **Mainnet** * **Base URL**: `https://prod.api.ada-anvil.app` * **Use Case**: Production-ready calls for real Cardano usage (minting real tokens, CIP-25/68 assets for live dApps). * **Risks**: Mistakes cost actual ADA. Validate thoroughly on a testnet before going live. ### [](https://dev.ada-anvil.io/guides/select-cardano-environment#preprod) **Preprod** * **Base URL**: `https://preprod.api.ada-anvil.app` * **Use Case**: Primary test environment. Ideal for verifying transaction building, new CIP-25 or CIP-68 scripts, multi-sig flows, etc. * **Safe**: Uses test ADA from a faucet. Great for repeated trials or when you’re unsure about advanced features. ### [](https://dev.ada-anvil.io/guides/select-cardano-environment#preview) **Preview** * **Base URL**: `https://preview.api.ada-anvil.app` * **Use Case**: Testing future Cardano protocol changes, ensuring your code stays compatible with upcoming releases. * **Similar to Preprod**: Uses test ADA but may differ in protocol versions or feature flags. * * * **Network IDs (CSL/Technical Details)** If you’re working at a lower level (for instance, with Cardano Serialization Library—CSL), you might see references to **network IDs**: * **id**: `0` means Testnet (applies to Preprod, Preview, or private networks). * **id**: `1` means Mainnet. Anvil’s high-level APIs typically handle network IDs for you. However, if you’re constructing transactions yourself or using raw CSL calls, you may need to specify the correct ID to ensure your transactions are valid. **Note**: We’re continually refining Anvil’s tools to **abstract away** these complexities, so you can focus on your application logic rather than the intricacies of Cardano’s underlying protocol. * * * **Getting an Environment-Specific API Key** Each environment requires its own API Key. * **Mainnet Key**: For real ADA transactions. * **Preprod Key**: For test ADA in the pre-production environment. * **Preview Key**: For experimenting with upcoming protocol changes. **Why separate keys?** This ensures you won’t accidentally spend real funds when you intended to test, and it keeps usage metrics and rate limits distinct across different project stages. **Steps to acquire keys:** 1. **Email** `[[email protected]](https://dev.ada-anvil.io/cdn-cgi/l/email-protection) ` to request keys for each environment. 2. You’ll receive separate credentials and rate-limit policies for each. 3. **Include the correct** `**X-Api-Key**` **in your headers** when sending requests (see Authentication guide for details). * * * **Development Workflow** 1. **Local Development** * Start with the Preprod environment to mint tokens, test staking or delegate, and ensure your code works as expected. * Use tADA from the faucet to simulate transactions. 1. **Integration Testing** * Move to the Preview environment if you need to test features not yet live on Mainnet. * Validate that your application still performs correctly under new protocol rules. 1. **Production Deployment** * Once everything is stable and thoroughly tested, switch to the **Mainnet** domain and use your Mainnet key. * Ensure your contracts, UTXO handling, and address generation are thoroughly tested on the testnets first. * * * **Code Example: Switching Environments Dynamically** Here’s a Node.js snippet showing how you might toggle between environments based on a simple config variable: Copy // Adjust this to 'preprod', 'preview', or 'mainnet' const environment = process.env.CARDANO_ENV || "preprod"; const baseUrls = { preprod: "https://preprod.api.ada-anvil.app/v2/services", preview: "https://preview.api.ada-anvil.app/v2/services", mainnet: "https://prod.api.ada-anvil.app/v2/services", }; // Suppose each environment requires a distinct API key const apiKeys = { preprod: process.env.PREPROD_API_KEY, preview: process.env.PREVIEW_API_KEY, mainnet: process.env.MAINNET_API_KEY, }; async function getHealth() { const url = `${baseUrls[environment]}/health`; const apiKey = apiKeys[environment]; try { const response = await fetch(url, { headers: { "X-Api-Key": apiKey }, }); console.log(`[${environment.toUpperCase()}] Health:`, await response.json()); } catch (error) { console.error("Error:", error); } } (async () => { await getHealth(); })(); **Explanation**: * We store **domain URLs** and **API Keys** for each environment. * We pick the environment via a config variable (`CARDANO_ENV`). * If we wanted to run a test suite for both environments, we can easily toggle by changing `CARDANO_ENV`. * * * **FAQ and Troubleshooting** 1. **Do I need different code for each environment?** * Generally **no**—only your **base URL** and **API Key** change. The rest of your logic remains the same. 1. **Why am I getting** `**401**` **errors on the testnet but not on Mainnet?** * You might be using your Mainnet key on a testnet endpoint (or vice versa). Ensure you match the key to the environment. 1. **What if I need a private cluster?** * Contact us to discuss setting up a **private** or **enterprise** environment. We can tailor configurations (network IDs, access controls, node settings) to your needs. 1. **Is there any difference in UTXO structure between testnet and Mainnet?** * No—Cardano’s UTXO model works identically across environments. Only the tokens and network ID differ. * * * **Next Steps** * **Dive into UTXOs vs. Change Address** * Learn how Cardano’s transaction model differs from account-based blockchains. * **Check Out Our Utilities** * See how Anvil simplifies common Cardano functions (address derivation, transaction serialization, etc.). * **Ready to Build** * Head over to our Transaction or NFT & FT guides to start coding real (or test) transactions on Cardano. [PreviousSupport](https://dev.ada-anvil.io/anvil-api/support) [NextTransaction](https://dev.ada-anvil.io/guides/transaction) Last updated 3 months ago Was this helpful? --- # Deno & Fetch | Anvil Development Agency **Quick-Start Example** This streamlined tutorial shows how to mint a **CIP-68 NFT** on Cardano using **Deno + Fetch** and the **Anvil API**. This guide demonstrates the **Native Script Validation** approach using a Metadata-Manager Wallet. It's a straightforward way to create updatable NFTs without deploying a full smart contract. See [the reasons why](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-68#validation-approaches-native-scripts-vs-smart-contracts) for more details on the pros and cons of this approach. We’ll: 1. Load wallets. 2. Create a native script using our [Native Script utilities](https://dev.ada-anvil.io/developer-tools/utility-functions#native-script-utilities) . 3. Build a mint payload that creates both tokens (`100` + `222`). 4. Call `transactions/build`. 5. Sign with the Policy & Customer keys. 6. Submit the transaction. 7. Verify on-chain that the assets are minted. * The reference token is sent to the metadata manager wallet. * The user token is sent to the customer wallet. * * * [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-68/deno-and-fetch#prerequisites) Prerequisites -------------------------------------------------------------------------------------------------------------- **Three Wallets** – Three wallets (Customer, Policy, Metadata Manager). Create them quickly with our [Wallet CLI](https://github.com/Cardano-Forge/cardano-wallet-cli) . Make sure the customer wallet has ADA to pay for fees. **Anvil API Key** – A valid Anvil API key. See [Authentication](https://dev.ada-anvil.io/anvil-api/authentication) . **Utility Helpers** – Import helpers from the [utilities-functions](https://dev.ada-anvil.io/developer-tools/utility-functions) guide to keep this file short. * * * [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-68/deno-and-fetch#quick-start-script) Quick-Start Script ------------------------------------------------------------------------------------------------------------------------ Copy import { Buffer } from "node:buffer"; import { FixedTransaction, PrivateKey, } from "npm:@emurgo/[email protected]"; import { dateToSlot, getKeyhash, createNativeScript, } from "../utils/shared.ts"; import { API_URL, HEADERS } from "../utils/constant.ts"; // Load wallets const customerWallet = JSON.parse(Deno.readTextFileSync("wallet-customer.json")); const policyWallet = JSON.parse(Deno.readTextFileSync("wallet-policy.json")); const metaManagerWallet = JSON.parse(Deno.readTextFileSync("wallet-meta-manager.json")); // Create native script const slot = await dateToSlot(new Date("2026-01-01")); const keyhash = await getKeyhash(policyWallet.base_address_preprod); const nativeScript = await createNativeScript(keyhash!, slot); const counter = Date.now(); const assetName = `anvilapicip68_${counter}`; // Build mint payload const buildBody = { changeAddress: customerWallet.base_address_preprod, mint: [\ {\ // Reference token - label 100, sent to Metadata-Manager wallet\ version: "cip68",\ assetName: { name: assetName, format: "utf8", label: 100 },\ metadata: {\ name: `anvil-api-${counter}`,\ image: "ipfs://YOUR_IPFS_HASH_HERE",\ mediaType: "image/png",\ description: "Anvil API CIP-68 Mint Example",\ },\ policyId: nativeScript.hash,\ quantity: 1,\ destAddress: metaManagerWallet.base_address_preprod,\ },\ {\ // User token - label 222, sent to Customer wallet\ version: "cip68",\ assetName: { name: assetName, format: "utf8", label: 222 },\ policyId: nativeScript.hash,\ quantity: 1,\ destAddress: customerWallet.base_address_preprod,\ },\ ], preloadedScripts: [nativeScript], }; // Build transaction const buildResult = await fetch(`${API_URL}/transactions/build`, { method: "POST", headers: HEADERS, body: JSON.stringify(buildBody), }); const buildJson = await buildResult.json(); if (!buildResult.ok) { throw new Error(buildJson.message); } // Sign transaction const tx = FixedTransaction.from_bytes(Buffer.from(buildJson.complete, "hex")); tx.sign_and_add_vkey_signature(PrivateKey.from_bech32(policyWallet.skey)); // Sign with customer wallet. Normally this would come from the clientside via your customers browser extenstion // This is here for simplicity. See full minting examples below. tx.sign_and_add_vkey_signature(PrivateKey.from_bech32(customerWallet.skey)); // Submit transaction const submitResult = await fetch(`${API_URL}/transactions/submit`, { method: "POST", headers: HEADERS, body: JSON.stringify({ transaction: tx.to_hex() }), }); const submitJson = await submitResult.json(); if (!submitResult.ok) { throw new Error(submitJson.message); } console.log("Submitted Transaction Hash:", submitJson.txHash); * * * [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-68/deno-and-fetch#full-example-script) Full Example Script -------------------------------------------------------------------------------------------------------------------------- For complete scripts, see the examples repository: [CIP-68 Example](https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/cip68.ts) [CIP-68 Treasury Pays for Fees Example](https://github.com/Cardano-Forge/anvil-api-examples/blob/main/cip68-treasury-pays.ts) [PreviousMint NFTs (CIP-68)](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-68) [NextMint NFTs (CIP-25)](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25) Last updated 11 days ago Was this helpful? --- # Support | Anvil Development Agency [](https://dev.ada-anvil.io/anvil-api/support#self-help-resources) Self-Help Resources ------------------------------------------------------------------------------------------- Before contacting our support team, please check if your question or issue is already addressed in our documentation: * Check our [FAQ](https://dev.ada-anvil.io/anvil-api/faq) for answers to common questions * Review the [Troubleshooting](https://dev.ada-anvil.io/anvil-api/troubleshooting) guide for solutions to known errors * Explore our developer guides for detailed implementation instructions [FAQ](https://dev.ada-anvil.io/anvil-api/faq) [Troubleshooting](https://dev.ada-anvil.io/anvil-api/troubleshooting) [](https://dev.ada-anvil.io/anvil-api/support#contact-support) Contact Support ----------------------------------------------------------------------------------- If you couldn't find a solution in our documentation, please reach out to us through one of the following channels: ### [](https://dev.ada-anvil.io/anvil-api/support#email-support) Email Support For technical issues or account-related questions: * Email: [\[email protected\]](https://dev.ada-anvil.io/cdn-cgi/l/email-protection) * Please include detailed information about your issue, including error messages and steps to reproduce [](https://dev.ada-anvil.io/anvil-api/support#support-hours) Support Hours ------------------------------------------------------------------------------- Our team is available to respond to inquiries 24/7. However depending on your API tier, you may experience longer response times. [](https://dev.ada-anvil.io/anvil-api/support#when-contacting-support) When Contacting Support --------------------------------------------------------------------------------------------------- To help us resolve your issue more quickly, please include: 1. Your API key identifier or email address associated with your API key 2. Environment (mainnet, preprod, preview, etc.) 3. Context of what you are building. (e.g. a Minting dApp, a DeFi application, etc.) 4. Detailed description of the issue 5. Relevant error messages and timestamps 6. Steps to reproduce the problem 7. Any code samples (with sensitive information removed) [PreviousTroubleshooting](https://dev.ada-anvil.io/anvil-api/troubleshooting) [NextSelect Cardano Environment](https://dev.ada-anvil.io/guides/select-cardano-environment) Last updated 16 days ago Was this helpful? --- # Submit Transaction | Anvil Development Agency [](https://dev.ada-anvil.io/guides/transaction/submit-transaction#introduction) Introduction ------------------------------------------------------------------------------------------------- Once you have a signed transaction. You are ready to submit it to the Cardano network [](https://dev.ada-anvil.io/guides/transaction/submit-transaction#usage) Usage ----------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/transaction/submit-transaction#payload) Payload In order to submit the transaction, you need at least one valid signature with the provided transaction. This approach supports multi signature (_multi sig_) as well. Copy { "transaction": "84a4... *The complete transaction*", "signatures": ["a100...", "a100..."] } ### [](https://dev.ada-anvil.io/guides/transaction/submit-transaction#submit-the-transaction) Submit the Transaction **You can refer to this page to see how to create a transaction:** [Create Basic Transaction](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction) Once **signed**, you can submit it using the following endpoint: #### [](https://dev.ada-anvil.io/guides/transaction/submit-transaction#with-bash-and-curl) With Bash and cURL #### [](https://dev.ada-anvil.io/guides/transaction/submit-transaction#with-fetch) With Fetch **Cardano Explorer** You can get your transaction on explorer: [https://preprod.cexplorer.io/tx/b7969bf21b1125938bf112df7d932b66a519b32f0e94e36eb7d3e98cc4ef0c98](https://preprod.cexplorer.io/tx/b7969bf21b1125938bf112df7d932b66a519b32f0e94e36eb7d3e98cc4ef0c98) [PreviousSign Transaction](https://dev.ada-anvil.io/guides/transaction/signing-transaction) [NextCreate Custom Transaction](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction) Last updated 6 months ago Was this helpful? --- # Create Basic Transaction | Anvil Development Agency [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction#introduction) Introduction ------------------------------------------------------------------------------------------------------- This guide demonstrates how to create a **basic transaction** to send ADA from one address to another. The basic transaction is the simplest form of Cardano transaction, focused solely on transferring the native currency (ADA/Lovelace) **without** additional assets or metadata. With the Anvil API, you can easily create, sign, and submit these transactions with minimal configuration. For more information on **transactions**, please refer to the [Transaction Overview](https://dev.ada-anvil.io/guides/transaction) . [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction#key-features) Key Features ------------------------------------------------------------------------------------------------------- * Simple ADA transfers between addresses * Minimal configuration required * Clean and straightforward API payload [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction#requirements) Requirements ------------------------------------------------------------------------------------------------------- * A Cardano wallet with sufficient ADA * Recipient wallet address * Valid API key for authentication [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction#full-examples) Full Examples --------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction#using-next.js-and-weld) Using Next.js and Weld [Next.js & Weld](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld) ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction#using-bash-and-curl) Using Bash and cURL [Bash & cURL](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/bash-and-curl) ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction#using-ts-js-fetch) Using TS/JS Fetch [Deno & Fetch](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/node-and-fetch) * * * [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction#specifications) **Specifications** --------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction#api-endpoint) API Endpoint Copy POST /transactions/build ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction#request-structure) Request Structure Copy { "changeAddress": "", "utxos": ['8282...', '8282...', '8282...'], "outputs": [\ {\ "address": "",\ "lovelace": \ }\ ] } [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction#best-practices) Best Practices ----------------------------------------------------------------------------------------------------------- * Always verify the recipient address before submitting transactions * Remember that 1 ADA = 1,000,000 Lovelace * Always provide UTXOs in production See [Selecting UTXOs for Transactions](https://dev.ada-anvil.io/guides/transaction/selecting-utxos) * Test transactions on testnet before moving to mainnet * Consider transaction fees when planning amounts to send * Keep your API key secure and never expose it in client-side code [PreviousTransaction](https://dev.ada-anvil.io/guides/transaction) [NextBash & cURL](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/bash-and-curl) Last updated 16 days ago Was this helpful? --- # Bash & cURL | Anvil Development Agency ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/bash-and-curl#introduction) Introduction There are two ways to create a transaction, the first one is by passing the Cardano wallet address for the utxos (as the changeAddress), and the second one usually used with a frontend (wallet extension), requires to get a list of all cbor utxos to be used for the transaction. The following examples are covering both approaches. ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/bash-and-curl#objectives) Objectives This example sends 10ADA from one wallet to another. ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/bash-and-curl#requirements) Requirements * A Cardano wallet with ADA * A wallet to send ADA to * A wallet extension to sign the transaction * An API key The wallets are on Preprod network and using Eternl (So the signature is done in the browser) See [Signing Transactions](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/transaction/signing-transaction.md) for more details. ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/bash-and-curl#using-the-address-to-fetch-utxos) Using the Address to fetch Utxos **Payload (Using an address only)** > Important: This demo is meant for educational and testing purposes only. In production, please pass the UTXO list for your wallet. We recommend using the [@ada-anvil/weld](https://www.npmjs.com/package/@ada-anvil/weld) > wallet connector to easily retrieve UTXOs from connected wallets. In this example, the API will fetch the UTXO list for you and automatically determine the UTXO(s) to be used for the transaction. _You can use this payload when you cannot fetch the utxos, for example, when you don't have a wallet extension._ Copy { "changeAddress": "addr_sender...", "outputs": [\ {\ "address": "addr_recipient...",\ "lovelace": 10_000_000\ }\ ] } **Example (Using cURL)** _Using a preprod wallet._ Copy SENDER_ADDRESS="addr_test1qrvx8wgdndrk98qf62vka3q4fglchk7h940vepdtgcv9fuu0e0aeuac6j2xhz77esaaudku68ha89qesqvd29pmuzw6qk8xkcn" RECEIVER_ADDRESS="addr_test1qztayr885vqrx6w0j946lvtxl622flxx4asj2z4ludm3y2rewu7hmazv8tm78tvphzlream22pp6zhk0rrsa84nf6qxsrua9nh" # 10 ADA => 1ADA = 1'000'000LOVELACE LOVELACE_AMOUNT=10_000_000 # See Authentication page for API key details. X_API_KEY="testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9" Copy curl -X POST \ -H "Content-Type: application/json" \ -H "X-Api-Key: ${X_API_KEY}" \ -d '{ "changeAddress": "'${SENDER_ADDRESS}'", "outputs": [\ {\ "address": "'${RECEIVER_ADDRESS}'",\ "lovelace": 10000000\ }\ ] }' \ https://preprod.api.ada-anvil.app/v2/services/transactions/build **Output** Copy { "complete": "84a400d901028182582059f796dba33ec94cd07dd609d175a6ef239a2590e974b73ec052eccb8daf53f30101828258390097d20ce7a3003369cf916bafb166fe94a4fcc6af61250abfe377122879773d7df44c3af7e3ad81b8be3cf76a5043a15ecf18e1d3d669d00d1a0098968082583900d863b90d9b47629c09d2996ec4154a3f8bdbd72d5ecc85ab461854f38fcbfb9e771a928d717bd9877bc6db9a3dfa728330031aa2877c13b41a1de51e21021a0003cb05031a04e05deea0f5f6", "stripped": "84a400d901028182582059f796dba33ec94cd07dd609d175a6ef239a2590e974b73ec052eccb8daf53f30101828258390097d20ce7a3003369cf916bafb166fe94a4fcc6af61250abfe377122879773d7df44c3af7e3ad81b8be3cf76a5043a15ecf18e1d3d669d00d1a0098968082583900d863b90d9b47629c09d2996ec4154a3f8bdbd72d5ecc85ab461854f38fcbfb9e771a928d717bd9877bc6db9a3dfa728330031aa2877c13b41a1de51e21021a0003cb05031a04e05deea0f5f6", "witnessSet": "a0" } ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/bash-and-curl#using-a-list-of-utxos) Using a list of UTXOs **Payload** _You can use this payload when you can fetch the utxos, for example, when you have a wallet extension._ Copy { "utxos": ["8282...", "8282...", "..."], "changeAddress": "addr_sender...", "outputs": [\ {\ "address": "addr_recipient...",\ "lovelace": 10000000,\ }\ ] } TBD, do the example as above using an UTXOs list [PreviousCreate Basic Transaction](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction) [NextDeno & Fetch](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/node-and-fetch) Last updated 16 days ago Was this helpful? --- # Next.js & Weld | Anvil Development Agency [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld#overview) Overview ---------------------------------------------------------------------------------------------------------------- In this tutorial, you'll build a modern Next.js application designed for interacting with the Cardano blockchain through the Anvil API. Users will be able to connect their wallets, check balances, and easily create, sign, and submit ADA transactions—all powered by the robust Anvil API and Weld wallet connector. ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld#what-youll-create) What You'll Create A complete transaction application that features: * Server-side rendering (SSR) and [API Routes](https://nextjs.org/docs/pages/building-your-application/routing/api-routes) using Next.js * Smooth wallet integration using [Weld](https://github.com/Cardano-Forge/weld) * Robust transaction workflows with Anvil API (build, sign, and submit) * Clear error handling and user feedback powered by Anvil API responses * A clean, responsive user interface ![Completed Cardano Transaction App with Next.js and Weld](https://dev.ada-anvil.io/~gitbook/image?url=https%3A%2F%2F2837083171-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fi5SfFhLFp4rKhwQyZN9d%252Fuploads%252Fgit-blob-1fee610f3235816585f06f64dd19b02913e7ccd7%252Fnextjs-basic-transaction-finished.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=f1d4cbc1&sv=2) Completed transaction application with wallet connection and ADA transfer functionality [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld#guide-breakdown) Guide Breakdown ------------------------------------------------------------------------------------------------------------------------------ To make your journey easier, this guide is structured in three clear sections: ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld#part-1-project-setup) [Part 1: Project Setup](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-setup) In the first part, you'll set up your development environment and create the basic Next.js application: * Creating a new Next.js project * Installing dependencies (Weld, Tailwind CSS, etc.) * Configuring environment variables (Anvil API key, network) * Setting up the project structure By the end of Part 1, you'll have a working Next.js application ready for wallet integration. ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld#part-2-wallet-integration) [Part 2: Wallet Integration](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-wallet) The second part focuses on integrating Cardano wallets using Weld: * Creating a Weld provider with SSR support * Building a wallet connector component * Handling wallet state management * Testing wallet connection functionality When you complete Part 2, your application will be able to connect to Cardano wallets, display wallet information, and maintain connection state. ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld#part-3-building-transactions) [Part 3: Building Transactions](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-transactions) In the final part, you'll implement transaction functionality using the Anvil API: * Creating server-side functions to communicate with Anvil API endpoints * Building API routes that leverage Anvil API for transaction building and submission * Implementing the Anvil transaction flow (build → sign → submit) * Handling Anvil API responses and error conditions After finishing Part 3, you'll have a complete Cardano transaction application that uses Anvil API to build, sign, and submit transactions to the Cardano network. [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld#prerequisites) Prerequisites -------------------------------------------------------------------------------------------------------------------------- Before starting this guide, ensure you have: * Node.js 18+ installed * Basic knowledge of React and Next.js * Basic knowledge of Cardano, Cardano Networks,and wallets. See the Anvil API Getting Started guide * An Anvil API key (sign up at TODO: Add public URL) * A Cardano wallet extension installed in your browser (Eternl, Lace, etc.) [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld#getting-started) Getting Started ------------------------------------------------------------------------------------------------------------------------------ Ready to build your Cardano transaction application? Begin with [Part 1: Project Setup](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-setup) . [PreviousDeno & Fetch](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/node-and-fetch) [NextPart 1: Project Setup](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-setup) Last updated 4 months ago Was this helpful? --- # Create Custom Transaction | Anvil Development Agency [](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction#introduction) Introduction -------------------------------------------------------------------------------------------------------- This guide demonstrates how to create a **custom transaction** that sends ADA and assets to multiple recipient addresses in a single transaction. This powerful feature allows you to efficiently distribute funds and tokens to several wallets at once, reducing transaction costs and improving throughput. Simply include **multiple outputs** in the payload to each recipient, and define what ADA and assets to send, and Anvil API will handle building the transaction for you. For more information on **transactions**, please refer to the [Transaction Overview](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/transaction/transaction/README.md) . [](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction#key-features) Key Features -------------------------------------------------------------------------------------------------------- * Send different amounts of ADA to multiple recipients * Distribute different native assets to different addresses * Customize each output independently * Handle complex transactions in a single API call [](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction#requirements) Requirements -------------------------------------------------------------------------------------------------------- * A Cardano wallet with sufficient ADA * Native assets in your wallet (if sending assets) * Recipient wallet addresses * Valid API key for authentication [](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction#full-examples) Full Examples ---------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction#using-bash-and-curl) Using Bash and cURL [Bash & cURL](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction/bash-and-curl) ### [](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction#using-ts-js-fetch) Using TS/JS Fetch [Deno & Fetch](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction/node-and-fetch) * * * [](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction#specifications) **Specifications** ---------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction#api-endpoint) API Endpoint Copy POST /transactions/build ### [](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction#request-structure) Request Structure Copy { "changeAddress": "", "outputs": [\ {\ "address": "",\ "lovelace": ,\ "assets": [\ {\ "policyId": "",\ "assetName": "",\ "quantity": \ }\ ]\ },\ {\ "address": "",\ "lovelace": ,\ "assets": [\ {\ "policyId": "",\ "assetName": "",\ "quantity": \ }\ ]\ }\ // Add as many outputs as needed\ ], // Optional bounds that determine when the transaction is valid. // False can be used to disable a bound. // If transactions are submitted outside of the validity interval, they will be rejected. "validityInterval": { "start": , // Optional, default: current slot "end": // Optional, default: current slot + 2 hours } } [](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction#best-practices) Best Practices ------------------------------------------------------------------------------------------------------------ * Always verify recipient addresses before submitting transactions * Include sufficient ADA in each output (minimum 2 ADA if sending native assets) * Test transactions on testnet before moving to mainnet * Consider transaction fees when planning outputs * Keep your API key secure and never expose it in client-side code [PreviousSubmit Transaction](https://dev.ada-anvil.io/guides/transaction/submit-transaction) [NextBash & cURL](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction/bash-and-curl) Last updated 1 month ago Was this helpful? --- # Deno & Fetch | Anvil Development Agency **Quick-Start Example** This streamlined tutorial shows how to mint a **CIP-25 NFT** on Cardano using **Deno + Fetch** and the **Anvil API**. Everything lives in a single file (`index.ts`) so you can copy-paste and run. We will: 1. Load wallets. 2. Create a native script using our [shared utils](https://dev.ada-anvil.io/developer-tools/utility-functions) . 3. Build a mint payload for one asset. 4. Call `transactions/build`. 5. Sign with the Policy & Customer keys. 6. Submit the transaction. 7. Verify the NFT on-chain. * * * ### [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25/deno-and-fetch#prerequisites) Prerequisites • **Anvil API key** – Get one [here](https://dev.ada-anvil.io/anvil-api/authentication) . • **Two funded wallets** – – **Customer wallet** – Pays fees and receives the NFT. – **Policy wallet** – Must sign the transaction to authorize the mint. Via the rules defined in your [native script](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts) . Use our [wallet CLI](https://dev.ada-anvil.io/developer-tools/wallet-cli) to create them. • **NFT metadata** – Name, description, image URL etc. formatted per CIP-25. • **Utility Helpers** – Import helpers from the [utilities-functions](https://dev.ada-anvil.io/developer-tools/utility-functions) guide to keep this file short. * * * ### [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25/deno-and-fetch#step-by-step-walk-through) Step-by-Step Walk-through Copy import { Buffer } from "node:buffer"; import { FixedTransaction, PrivateKey, } from "npm:@emurgo/[email protected]"; import { createNativeScript, dateToSlot, getKeyhash, } from "../utils/shared.ts"; import { API_URL, HEADERS } from "../utils/constant.ts"; // Load wallets const customerWallet = JSON.parse(Deno.readTextFileSync("wallet-customer.json")); const policyWallet = JSON.parse(Deno.readTextFileSync("wallet-policy.json")); // Create native script const slot = await dateToSlot(new Date("2026-01-01")); const keyhash = await getKeyhash(policyWallet.base_address_preprod); const nativeScript = await createNativeScript(keyhash!, slot); // Prepare mint payload const counter = Date.now(); const assetName = `anvilapicip25_${counter}`; // Build transaction const buildBody = { changeAddress: customerWallet.base_address_preprod, mint: [\ {\ // CIP-25 asset with Metadata\ version: "cip25",\ assetName: { name: assetName, format: "utf8" },\ metadata: {\ name: `anvil-api-${counter}`,\ image: `ipfs://YOUR_IPFS_HASH_HERE`,\ mediaType: "image/png",\ description: "Anvil API CIP-25 Mint Example",\ },\ policyId: nativeScript.hash,\ quantity: 1,\ },\ ], preloadedScripts: [nativeScript], }; const buildResult = await fetch(`${API_URL}/transactions/build`, { method: "POST", headers: HEADERS, body: JSON.stringify(buildBody), }); const buildJson = await buildResult.json(); if (!buildResult.ok) { throw new Error(buildJson.message); } // Sign transaction const tx = FixedTransaction.from_bytes(Buffer.from(buildJson.complete, "hex")); // Sign with policy wallet tx.sign_and_add_vkey_signature(PrivateKey.from_bech32(policyWallet.skey)); // Sign with customer wallet. Normally this would come from the clientside via your customers browser extenstion // This is here for simplicity. See full minting examples below. tx.sign_and_add_vkey_signature(PrivateKey.from_bech32(customerWallet.skey)); // Submit transaction const submitResult = await fetch(`${API_URL}/transactions/submit`, { method: "POST", headers: HEADERS, body: JSON.stringify({ transaction: tx.to_hex() }) }); const submitJson = await submitResult.json(); if (!submitResult.ok) { throw new Error(submitJson.message); } console.log("Submitted Transaction Hash: ", submitJson.txHash); * * * ### [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25/deno-and-fetch#full-example-script) Full Example Script For complete, fully-annotated examples see the examples repository: [Deno Example](https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/cip25.ts) [HTMX + Hono + Weld Example](https://github.com/Cardano-Forge/anvil-api-examples/tree/main/minting-platform-cip25) [Next.js + Weld Example](https://github.com/Cardano-Forge/anvil-api-examples/tree/main/nextjs-minting-platform-cip25) [PreviousCLI Tool to Mint Assets](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25/cli-tool-to-mint-assets) [NextDelegations](https://dev.ada-anvil.io/guides/delegations) Last updated 11 days ago Was this helpful? --- # Create Transaction with Metadata (CIP-20) | Anvil Development Agency [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20#overview) Overview -------------------------------------------------------------------------------------------------------------- Sometimes you want to attach a message, comment, or memo to a Cardano transaction—whether it's a receipt reference, a note for future auditing, or just user-friendly text. **CIP-20** standardizes how these messages appear on-chain by using **label 674** in transaction metadata. With the Anvil API, you can easily attach messages to your transactions with minimal configuration. The Anvil API lets you supply a simple message or array of messages. Under the hood, Anvil auto-formats them for CIP-20 compliance: * **Single string** → `["Your message"]` * **Array of strings** → `["Line 1", "Line 2"]` * **Long string** → Automatically split across multiple array entries if it exceeds **64 bytes** For more information on **transactions**, please refer to the [Transaction Overview](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/transaction/transaction/README.md) . [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20#key-features) Key Features ---------------------------------------------------------------------------------------------------------------------- * Simple message attachment to transactions * Automatic CIP-20 compliance formatting * Support for single strings or arrays of strings * Automatic handling of messages exceeding 64 bytes [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20#requirements) Requirements ---------------------------------------------------------------------------------------------------------------------- * A Cardano wallet with sufficient ADA * Valid API key for authentication [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20#full-examples) Full Examples ------------------------------------------------------------------------------------------------------------------------ ### [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20#using-bash-and-curl) Using Bash and cURL [Bash & cURL](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/bash-and-curl) ### [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20#using-ts-js-fetch) Using TS/JS Fetch [Deno & Fetch](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/node-and-fetch) * * * [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20#specifications) **Specifications** ------------------------------------------------------------------------------------------------------------------------------ ### [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20#api-endpoint) API Endpoint Copy POST /transactions/build ### [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20#request-structure) Request Structure Copy { "changeAddress": "", "message": "Your message" // String or array of strings } ### [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20#cip-20-formatting) CIP-20 Formatting The Anvil API automatically formats your messages for CIP-20 compliance: * **Single string** → `["Your message"]` * **Array of strings** → `["Line 1", "Line 2"]` * **Long string** → Automatically split across multiple array entries if it exceeds **64 bytes** [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20#best-practices) Best Practices -------------------------------------------------------------------------------------------------------------------------- * Keep messages concise and relevant * Consider privacy implications of on-chain messages * Test transactions on testnet before moving to mainnet * Remember that all on-chain metadata is publicly visible * Keep your API key secure and never expose it in client-side code [PreviousDeno & Fetch](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction/node-and-fetch) [NextBash & cURL](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/bash-and-curl) Last updated 4 months ago Was this helpful? --- # Transaction | Anvil Development Agency [](https://dev.ada-anvil.io/guides/transaction#introduction) Introduction ------------------------------------------------------------------------------ Cardano **transactions** are the fundamental way to move value and interact with the blockchain, whether it's moving ADA, native tokens, and NFTs. Or it's interacting with smart contracts. The **Anvil API** simplifies transaction creation with a flexible interface that supports: * **Simple to complex outputs** - from basic ADA transfers to multi-asset transactions with multiple recipients * **Input methods** - using `changeAddress` and `UTXOs` to source funds * **Complete transaction lifecycle** - building, signing, and submitting in a streamlined workflow This documentation covers the various **transaction types**, **payload structures**, and **best practices** for creating transactions with the Anvil API. [](https://dev.ada-anvil.io/guides/transaction#the-basic-flow) The Basic Flow ---------------------------------------------------------------------------------- 1. **BUILD**: We structure a simple payload with `changeAddress`, `utxos`, and `outputs` 2. **SIGN**: We sign the transaction with your preferred signing method (See [Signing Transactions](https://dev.ada-anvil.io/guides/transaction/signing-transaction) ) 3. **SUBMIT**: We send the signed transaction to the Anvil API's transaction submit endpoint 4. **CONFIRM**: We handle the response from the submit endpoint [](https://dev.ada-anvil.io/guides/transaction#transaction-flow-overview) Transaction Flow Overview -------------------------------------------------------------------------------------------------------- Below is a sequence diagram that illustrates the basic flow of a transaction between your application and the Anvil API. [](https://dev.ada-anvil.io/guides/transaction#breakdown-of-the-basic-flow) Breakdown of the Basic Flow ------------------------------------------------------------------------------------------------------------ The above flow diagram illustrates the basic flow of a transaction. The following sections will break down each step in detail. As you can see there are two API calls that are involved in the basic flow: **Build** and **Submit**. The **Build** API call is used to build a transaction, and the **Submit** API call is used to submit a transaction after the user or application has **signed** it. ### [](https://dev.ada-anvil.io/guides/transaction#id-1.-building-transaction-defining-addresses-utxos-and-outputs) 1\. Building Transaction: Defining Addresses, UTXOs, and Outputs The **Build** (`POST /transaction/build`)API call is used to build a transaction. This call requires a payload that includes the sender address for change, UTXOs for inputs, and outputs. All transactions need UTXOs for inputs. See: [Selecting UTXOs](https://dev.ada-anvil.io/guides/transaction/selecting-utxos) for more information on how to do this for production applications. Copy // Structure for the transaction build request const buildBody = { // Sender address for change changeAddress: "addr_sender...", // UTXOs for inputs (either address or array of CBOR-encoded UTXOs) // For CIP-30 wallets: await wallet.getUtxos() utxos: ["8282...", "8282..."], // Optional: Force the inclusion of specific UTXOs, often required for smart contract interactions. // This is not required, but is useful if you need to force the spending of a specific UTXO. requiredInputs: ["8282...", "8282..."], // Where to send the ADA, assets, and amounts outputs: [\ {\ address: "addr_recipient...",\ lovelace: 10_000_000, // 10 ADA\ },\ ], // Optional bounds that determine when the transaction is valid. // False can be used to disable a bound. // If transactions are submitted outside of the validity interval, they will be rejected. validityInterval: { start: 1234567890, // POSIX timestamp or slot number (optional) end: 1234598765 // POSIX timestamp or slot number (optional) } //Add additional parameters here. }; // Send the build request const buildResponse = await fetch( "https://preprod.api.ada-anvil.app/v2/services/transactions/build", { method: "POST", headers: { "Content-Type": "application/json", "X-Api-Key": "YOUR_API_KEY", }, body: JSON.stringify(buildBody), } ); const buildData = await buildResponse.json(); ### [](https://dev.ada-anvil.io/guides/transaction#build-response-structure) Build Response Structure Copy // After successful build { "hash": "TRANSACTION_HASH", // Complete transaction is preferred for signing most transactions. "complete": "TRANSACTION_CBOR", // Stripped transaction is preferred for minting operations (NFTs/tokens). // This is because we want to hide metadata from the client. "stripped": "STRIPPED_CBOR", // The witness data component "witnessSet": "WITNESS_SET_CBOR" } > **Important Warning:** > > * Use the `complete` format when you don't care about hiding metadata from the frontend client. > > * For minting operations (NFTs/tokens), only exposing the `stripped` format of your `build` response helps hide metadata from the frontend client. The `complete` format will include those attributes allowing users to see the metadata before the transaction is submitted. > ### [](https://dev.ada-anvil.io/guides/transaction#id-2.-sign-the-transaction) 2\. Sign the Transaction **Sign** the transaction with your preferred signing method (See [Signing Transactions](https://dev.ada-anvil.io/guides/transaction/signing-transaction) ). This can be done using a CIP-30 compatible wallet, or by using a private key in your application, or by using a hardware wallet. We recommend using the [@ada-anvil/weld](https://www.npmjs.com/package/@ada-anvil/weld) wallet connector for a seamless experience handling interactions with Cardano wallets. ### [](https://dev.ada-anvil.io/guides/transaction#id-3.-submit-the-signed-transaction) 3\. Submit the Signed Transaction Once you have the signatures, we can **submit** `POST /transaction/submit` the transaction in two ways: 1. **Embed signatures in transaction**: You can embed the signatures in the transaction. This is helpful when you are signing a transaction in a backend server (using CSL). Otherwise, option 2 is recommended. 2. **Separate transaction and signatures**: You can separate the transaction and signatures and send them to the API separately. Copy // Option 1: Submit with signatures embedded in transaction. const submitBody = { transaction: "SIGNED_TRANSACTION_CBOR" }; // Option 2: Submit with separate transaction and signatures const submitBody = { transaction: "UNSIGNED_TRANSACTION_CBOR", signatures: ["SIGNATURE_1", "SIGNATURE_2"] // Array of signatures }; ### [](https://dev.ada-anvil.io/guides/transaction#submit-response-structure) Submit Response Structure Copy // After successful submit { "hash": "TRANSACTION_HASH" // The transaction ID on the blockchain } > **Cardano is Deterministic:** The transaction hash returned by the build endpoint is the same hash that will appear on-chain. Unlike some other blockchains, Cardano's eUTXO model allows for deterministic transaction creation - the transaction ID is calculated before submission and remains unchanged when confirmed on the blockchain. This feature enables reliable transaction tracking from creation through confirmation. * * * [](https://dev.ada-anvil.io/guides/transaction#best-practices) Best Practices ---------------------------------------------------------------------------------- * **Test on Preprod** first to avoid risking real ADA. * **Handle Errors gracefully**: watch for `400 Bad Request`, `401 Unauthorized`, or `429 Throttled, please try again later.` responses. * **Use CIP-30 for a user-friendly Web3 experience**: fetch `utxos` and `changeAddress` directly from the user's connected wallet. * **Validate JSON**: Ensure your payload is correctly formed—typos in keys like lovelace can cause failures. * **Include sufficient ADA**: When sending native assets, include at least 2 ADA per output (due to min-UTXO requirements). * **Verify addresses**: Always double-check recipient addresses before submitting transactions. * **Keep API keys secure**: Never expose your API keys in client-side code. * * * ### [](https://dev.ada-anvil.io/guides/transaction#transaction-builder-examples-and-next-steps) Transaction Builder Examples and Next Steps Now that you have a basic understanding of how to send a transaction, explore how the Anvil API is flexible and can handle many types of transactions. Wether you are sending ADA and/or assets to multiple recipients, adding metadata to transactions, or handling multi-signature transactions, the Anvil API takes advantage of the full power of transactions on Cardano. Review our guides below for more details. They provide practical examples of how to use the Anvil API transaction builder: [Create Basic Transaction](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction) [Create Custom Transaction](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction) [Create Transaction with Metadata (CIP-20)](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20) [PreviousSelect Cardano Environment](https://dev.ada-anvil.io/guides/select-cardano-environment) [NextCreate Basic Transaction](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction) Last updated 11 days ago Was this helpful? --- # Part 1: Project Setup | Anvil Development Agency [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-setup#introduction) Introduction ----------------------------------------------------------------------------------------------------------------------------------------------- In this first part, we'll create a Next.js application and configure it to work with Anvil API. By the end of this section, you'll have a working development environment ready for wallet integration. [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-setup#prerequisites) Prerequisites ------------------------------------------------------------------------------------------------------------------------------------------------- Before you begin, ensure you have: * Node.js 18+ installed * Basic familiarity with React and Next.js * An Anvil API key (sign up at TODO: Add public URL) [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-setup#project-creation) Project Creation ------------------------------------------------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-setup#id-1.-create-a-new-next.js-project) 1\. Create a new Next.js project Start by creating a new Next.js application using the create-next-app tool: Copy npx create-next-app@latest my-basic-transaction-app cd my-basic-transaction-app When prompted for options, select the following: * TypeScript: Yes * ESLint: Yes * Tailwind CSS: Yes * `src/` directory: Yes * App Router: Yes * Use TurboPack: (Optional) * Custom Import Alias (@/\*): No ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-setup#id-2.-install-required-dependencies) 2\. Install Required Dependencies Next, install Weld and other required dependencies: Copy npm install @ada-anvil/weld This single package includes everything needed for the client-side wallet integration. Other dependencies (e.g., Next.js, Tailwind CSS) should already be installed as part of the project creation. See the [Weld Documentation](https://github.com/Cardano-Forge/weld) for more information. The `@ada-anvil/weld` package includes all the necessary functionality for wallet integration. The React-specific hooks are exported from the package via the `/react` subpath: `@ada-anvil/weld/react`. ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-setup#id-3.-environment-configuration) 3\. Environment Configuration Create a `.env.example` file to document the required environment variables for other developers: Copy NEXT_PUBLIC_ANVIL_API_URL=https://preprod.api.ada-anvil.app/v2/services ANVIL_API_KEY=testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9 NEXT_PUBLIC_NETWORK=preprod This file can be safely committed to your repository. Next, create a `.env.local` file at the root of your project with your actual values Never commit your `.env.local` file to version control. Add it to your `.gitignore` file to keep your API key secure. ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-setup#id-4.-project-structure) 4\. Project Structure The project will be built incrementally across all three parts of this guide. Here's a preview of the complete project structure you'll create: Copy my-basic-transaction-app/ ├── src/ │ ├── app/ │ │ ├── api/ # API routes │ │ │ └── transaction/ │ │ │ ├── build/ │ │ │ │ └── route.ts # Transaction Build │ │ │ └── submit/ │ │ │ └── route.ts # Transaction Submit │ │ ├── globals.css # Global styles │ │ ├── layout.tsx # Main Layout │ │ └── page.tsx # Main Page │ ├── components/ # Component files │ │ ├── WalletConnector.tsx # Wallet Connector │ │ ├── TransactionForm.tsx # Transaction Form │ │ └── WeldProvider.tsx # Weld Provider │ ├── hooks/ # Custom React hooks │ │ └── useTransactionSubmission.ts # Transaction Submission Hook │ └── utils/ # Utility functions │ └── anvil-api.ts # Anvil API ├── public/ # Static assets ├── .env.local # Environment variables ├── .env.example # Environment variables ├── package.json # Package configuration └── other project config files # Project configuration In Part 1 (this guide), we'll focus on setting up the basic Next.js application structure, configuring environment variables, and adding styles. The implementation of specific components and API routes will come in Parts 2 and 3. ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-setup#id-5.-basic-styling) 5\. Basic Styling Create a consistent look and feel by adding basic styles to your `src/app/globals.css` file: We are using custom styles for the paper, button, and custom-rounded classes. You can modify these styles to match your desired design. Or you can use Tailwind's built-in utilities to create your own styles. For now, copy the following code into your `src/app/globals.css` file: Copy /* src/app/globals.css */ @tailwind base; @tailwind components; @tailwind utilities; :root { --bg-primary: #333; --bg-secondary: #ebebeb; --bg-accent: #212121; --fg-primary: #fff; --fg-secondary: #000; --fg-variant: #333; --white: #fff; --black: #000; --font: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; } .paper { --bg-primary: #000; --bg-secondary: #dfd0b9; --bg-accent: #000; --fg-primary: #fff; --fg-secondary: #000; --fg-variant: #edede7; --white: #edede7; --black: #000; } @layer base { body { font-family: var(--font); background-color: var(--white); color: var(--black); } p, span { color: var(--fg-secondary); } } @layer components { .w-300 { max-width: 18.75rem; min-width: 18.75rem; width: 18.75rem; } .break { word-break: break-word; } .custom-border { border: 0.125rem solid var(--fg-secondary); } .custom-rounded { border-radius: 1rem; } section, article { box-shadow: 0.25rem 0.25rem var(--black); padding: 1rem; border: 0.125rem solid var(--fg-secondary); border-radius: 1rem; margin-bottom: 1rem; } .btn { border: 0.125rem solid var(--fg-secondary); border-radius: 1rem; box-shadow: 0.25rem 0.25rem; background-color: var(--white); color: var(--fg-secondary); padding: 1rem; font-family: var(--font); font-weight: bold; font-size: medium; transition: 0.1s; cursor: pointer; } .btn:hover { translate: 0.25rem 0.25rem; box-shadow: none; background-color: var(--bg-secondary); } .btn:disabled { opacity: 0.5; cursor: not-allowed; } input[type="text"], input[type="number"], select { width: 100%; padding: 10px; margin: 8px 0; display: block; border: 2px solid var(--black); background-color: var(--white); color: var(--black); font-size: 16px; box-sizing: border-box; border-radius: 1rem; } input[type="text"]:focus, input[type="number"]:focus, select:focus { outline: none; border-color: var(--black); } .container-spacing { padding: 1rem; } .container-spacing > * { margin-bottom: 1rem; } .container-spacing > *:last-child { margin-bottom: 0; } } [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-setup#verify-your-setup) Verify Your Setup --------------------------------------------------------------------------------------------------------------------------------------------------------- Let's make sure your setup is working correctly: 1. Modify the existing home page at `src/app/page.tsx`. Next.js has already created this file during setup. Replace its contents with: Copy export default function Home() { return (

Cardano Transaction App

Welcome to the Cardano Transaction App!

); } 1. Start your development server: Copy npm run dev 1. Navigate to http://localhost:3000 in your browser. You should see the welcome message. Congratulations! You've completed Part 1 of the guide. You now have a Next.js project ready for Cardano wallet integration. [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-setup#whats-next) What's Next? --------------------------------------------------------------------------------------------------------------------------------------------- Now that you have a basic Next.js application set up, you're ready to integrate the Weld wallet connector. In [Part 2: Wallet Integration](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-wallet) , we'll implement the wallet connection functionality. [PreviousNext.js & Weld](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld) [NextPart 2: Wallet Integration](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-wallet) Last updated 3 months ago Was this helpful? --- # Part 2: Wallet Integration | Anvil Development Agency [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-wallet#weld-wallet-integration-overview) Weld Wallet Integration Overview ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Weld is a universal Cardano wallet connector library that simplifies wallet integration by providing: * A unified interface for multiple Cardano wallets * TypeScript support with full type safety * React hooks for state management * Server-side rendering compatibility See the [Weld documentation](https://github.com/Cardano-Forge/weld) for more information. [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-wallet#implementation-steps) Implementation Steps ---------------------------------------------------------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-wallet#id-1.-create-the-weld-provider-component) 1\. Create the Weld Provider Component First, we need to create a provider component that will make Weld available throughout your application. This component will handle wallet connectivity state and server/client hydration: Copy // src/components/WeldProvider.tsx "use client"; import { WeldProvider, type WeldProviderProps } from "@ada-anvil/weld/react"; export function ClientWeldProvider({ children, lastConnectedWallet, }: { children: React.ReactNode; lastConnectedWallet?: NonNullable< WeldProviderProps["wallet"] >["tryToReconnectTo"]; }) { return ( {children} ); } The `updateInterval` option (set to 30 seconds) helps maintain active wallet connections during longer user sessions. ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-wallet#id-2.-integrate-the-provider-in-your-layout) 2\. Integrate the Provider in Your Layout Next, we'll update the app layout to use our `ClientWeldProvider`. Notice that we include the `lastConnectedWallet` prop to restore wallet connection state between server and client. This is important for maintaining wallet connectivity during page navigation, and preventing React hydration errors due to server-side rendering being inconsistent with client-side rendering. See the [Server-Side Rendering (SSR) section in the Weld documentation](https://github.com/Cardano-Forge/weld?tab=readme-ov-file#server-side-rendering) for more information. Copy // src/app/layout.tsx import type { Metadata } from "next"; import { Geist, Geist_Mono } from "next/font/google"; import "./globals.css"; // Add the three imports. import { ClientWeldProvider } from "@/components/WeldProvider"; import { cookies } from "next/headers"; import { STORAGE_KEYS } from "@ada-anvil/weld/server"; const geistSans = Geist({ variable: "--font-geist-sans", subsets: ["latin"], }); const geistMono = Geist_Mono({ variable: "--font-geist-mono", subsets: ["latin"], }); export const metadata: Metadata = { title: "Cardano Transaction App", description: "Send Cardano transactions using the Anvil API", }; export default async function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { // Add the cookie retrieval logic. const cookieStore = await cookies(); const wallet = cookieStore.get(STORAGE_KEYS.connectedWallet)?.value; const changeAddress = cookieStore.get(STORAGE_KEYS.connectedChange)?.value; const stakeAddress = cookieStore.get(STORAGE_KEYS.connectedStake)?.value; const lastConnectedWallet = wallet ? { wallet, changeAddress, stakeAddress } : undefined; return ( {/* Wrap the children with the ClientWeldProvider component. */} {children} ); } ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-wallet#id-3.-understanding-ssr-with-weld) 3\. Understanding SSR with Weld Our implementation addresses three critical aspects of wallet integration in Next.js with SSR: 1. **Hydration Consistency**: By retrieving wallet connection data from cookies during server-side rendering, we ensure the initial client-side state matches what was rendered on the server, preventing React hydration errors. 2. **Connection Persistence**: The `updateInterval` configuration keeps an active wallet connection stable through periodic state checks. 3. **Seamless Reconnection**: The `tryToReconnectTo` property provides a smooth user experience by maintaining wallet connections across page refreshes. ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-wallet#id-4.-create-a-wallet-connector-component) 4\. Create a Wallet Connector Component Now let's create a UI component that users will interact with to connect their wallets: Copy // src/components/WalletConnector.tsx "use client"; import { useState } from "react"; // Import Weld hooks to get wallet and user extensions. import { useWallet, useExtensions } from "@ada-anvil/weld/react"; // Import supported wallets from Weld. import { SUPPORTED_WALLETS } from "@ada-anvil/weld"; // Helper function to truncate address for display const truncateAddress = (address: string) => { if (!address) return ""; return `${address.slice(0, 8)}...${address.slice(-8)}`; }; // Component to display wallet info const WalletInfo = ({ label, value }: { label: string; value: string }) => (
{label} {value}
); export default function WalletConnector() { const wallet = useWallet(); const { supportedMap: installedWallets, isLoading } = useExtensions( "supportedMap", "isLoading", ); const availableWallets = SUPPORTED_WALLETS.filter((w) => installedWallets.has(w.key), ); const [selectedWallet, setSelectedWallet] = useState(""); // Reset connection state if wallet selection changes const handleWalletSelection = (value: string) => { if (wallet.isConnectingTo && value !== wallet.isConnectingTo) { // Cancel any pending connection wallet .disconnect() .catch((err) => console.error("Failed to disconnect wallet:", err)); } setSelectedWallet(value); }; const handleConnect = async (walletKey?: string) => { if (!walletKey) return; try { await wallet.connectAsync(walletKey); } catch (error) { console.error("Failed to connect wallet:", error); } }; return (

Wallet

{wallet.isConnected ? ( // Connected state - show wallet info and disconnect button <> ) : // Disconnected state - show wallet selector and connect button isLoading ? (
Detecting wallet extensions...
) : (
)}
); } ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-wallet#id-5.-update-the-home-page) 5\. Update the Home Page Now that we have created the component. Lets update your home page to include the wallet connector component: Copy // src/app/page.tsx import WalletConnector from "@/components/WalletConnector"; export default function Home() { return (

Cardano Transaction App

); } [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-wallet#testing-your-wallet-integration) Testing Your Wallet Integration -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Now let's test the wallet integration to ensure it's working correctly: 1. **Start your development server**: Copy npm run dev 1. **Navigate to your application** (usually at http://localhost:3000) 2. **Test the wallet connection flow**: * Verify that available wallets are correctly detected in the dropdown * Select a wallet and click "Connect Wallet" * Confirm that the wallet popup appears requesting connection * After approving, verify that wallet information is displayed: * Connected wallet name * Truncated wallet address * ADA balance 3. **Test disconnection**: * Click the "Disconnect" button * Verify that the UI returns to the wallet selection state If wallet connection fails, check your browser console for errors. Common issues include: * Wallet extension not properly installed * Wallet needs to be enabled for dApp interactions. * Wallet locked (needs to be unlocked first) * Incompatible wallet versions [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-wallet#troubleshooting) Troubleshooting ------------------------------------------------------------------------------------------------------------------------------------------------------ ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-wallet#no-wallets-detected) No Wallets Detected If no wallets are appearing in the dropdown list: 1. Make sure you have wallet extensions installed (Eternl, Lace, etc.) 2. Refresh the page after installing a new wallet extension 3. Check your browser console for any errors [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-wallet#whats-next) What's Next? ---------------------------------------------------------------------------------------------------------------------------------------------- Now that you have a working wallet integration, you're ready to implement transaction functionality. In [Part 3: Building Transactions](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-transactions) , we'll create the components and API routes needed to build and submit Cardano transactions. Congratulations! You've completed Part 2 of the guide. Your application can now detect wallets, connect to them, and display wallet information. [PreviousPart 1: Project Setup](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-setup) [NextPart 3: Building Transactions](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-transactions) Last updated 4 months ago Was this helpful? --- # Deno & Fetch | Anvil Development Agency [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/node-and-fetch#introduction) Introduction ---------------------------------------------------------------------------------------------------------------------- There are two ways to create a transaction, the first one is by passing the Cardano wallet address for the utxos (only available on testnet networks), and the second one (required for production) usually used with a frontend (wallet extension), requires to get a list of all cbor utxos to be used for the transaction. [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/node-and-fetch#objectives) Objectives ------------------------------------------------------------------------------------------------------------------ This example sends 10ADA from one wallet to another. The wallets are on Preprod network and using a CIP-30 compatible wallet (So the signature is done in the browser) [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/node-and-fetch#requirements) Requirements ---------------------------------------------------------------------------------------------------------------------- * A Cardano wallet with ADA * A wallet to send ADA to * A wallet extension to sign the transaction * An API key [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/node-and-fetch#api-request-structure) API Request Structure ---------------------------------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/node-and-fetch#payload-format-using-an-address-as-utxos) Payload Format (Using an address as utxos) > Important: This demo is meant for educational and testing purposes only. In production, please pass the UTXO list for your wallet. We recommend using the [@ada-anvil/weld](https://www.npmjs.com/package/@ada-anvil/weld) > wallet connector to easily retrieve UTXOs from connected wallets. In this example, the API will fetch the UTXO list for you and automatically determine the UTXO(s) to be used for the transaction. Copy { "changeAddress": "addr_sender...", "outputs": [\ {\ "address": "addr_recipient...",\ "lovelace": 10_000_000\ }\ ] } [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/node-and-fetch#implementation) Implementation -------------------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/node-and-fetch#configuration-and-parameters-using-deno-and-fetch) Configuration and Parameters (using Deno and Fetch) _Using a preprod wallet all value needed for a transaction_ Copy const SENDER_ADDRESS = "addr_test1qrydyk6uw6cehk5u3zspyz3dhnwzmhfls2fp42vv5dv9g2z3885pg4kpkn30ptezc855lu3w5ey93zcr5lrezjmwkftqg8xvge"; const RECEIVER_ADDRESS = "addr_test1qr0tkwvlln0v5fljdxceudmlpt5y6szc84vpj4skm836tgn4hsqaesgg97l8ppy5rsn0alj8pth6lqe20fdyydsdgw6sr74cyt"; // 10 ADA => 1ADA = 1'000'000LOVELACE const LOVELACE_AMOUNT = 10_000_000; // See Authentication page for API key details. const X_API_KEY = "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9"; ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/node-and-fetch#request-body) Request Body _Body Structure for creating a basic transaction using the previously collected values._ Copy const BODY = { changeAddress: SENDER_ADDRESS, outputs: [\ {\ address: RECEIVER_ADDRESS,\ lovelace: LOVELACE_AMOUNT,\ },\ ], }; ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/node-and-fetch#api-call-using-deno-and-fetch) API Call (using Deno and Fetch) Basic POST call with Fetch basic-transaction.ts Copy const response = await fetch( `https://preprod.api.ada-anvil.app/v2/services/transactions/build`, { method: "POST", headers: { "Content-Type": "application/json" "x-api-key": X_API_KEY, }, body: JSON.stringify(BODY), } ); console.log(await response.json()); ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/node-and-fetch#deno-command) Deno Command Copy deno run --allow-net basic-transaction.ts ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/node-and-fetch#output) Output Copy { "hash": "b1f159d88e9dece4d327352c03a6fbb9484533dee60207367a2689f632650c9c", "complete": "84a400d9010281825820c83c3a8285c7b08c2cec3abf0c91dcec5afee531d6a0eeef002dd6d75456927d01018282583900debb399ffcdeca27f269b19e377f0ae84d40583d58195616d9e3a5a275bc01dcc1082fbe7084941c26fefe470aefaf832a7a5a42360d43b51a0098968082583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a43c6a5fc021a0003cb05031a04ebdd50a0f5f6", "stripped": "84a400d9010281825820c83c3a8285c7b08c2cec3abf0c91dcec5afee531d6a0eeef002dd6d75456927d01018282583900debb399ffcdeca27f269b19e377f0ae84d40583d58195616d9e3a5a275bc01dcc1082fbe7084941c26fefe470aefaf832a7a5a42360d43b51a0098968082583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a43c6a5fc021a0003cb05031a04ebdd50a0f5f6", "witnessSet": "a0" } [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/node-and-fetch#the-whole-file-deno-version) The Whole File (Deno Version) ------------------------------------------------------------------------------------------------------------------------------------------------------ basic-transaction.ts[](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/node-and-fetch#basic-transaction.ts) Copy const SENDER_ADDRESS = "addr_test1qrydyk6uw6cehk5u3zspyz3dhnwzmhfls2fp42vv5dv9g2z3885pg4kpkn30ptezc855lu3w5ey93zcr5lrezjmwkftqg8xvge"; const RECEIVER_ADDRESS = "addr_test1qr0tkwvlln0v5fljdxceudmlpt5y6szc84vpj4skm836tgn4hsqaesgg97l8ppy5rsn0alj8pth6lqe20fdyydsdgw6sr74cyt"; // 10 ADA => 1ADA = 1'000'000LOVELACE const LOVELACE_AMOUNT = 10_000_000; // See Authentication page for API key details. const X_API_KEY = "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9"; const API_URL = "https://preprod.api.ada-anvil.app/v2/services"; const HEADERS = { "Content-Type": "application/json", "x-api-key": X_API_KEY }; const BODY = { changeAddress: SENDER_ADDRESS, outputs: [\ {\ address: RECEIVER_ADDRESS,\ lovelace: LOVELACE_AMOUNT,\ },\ ], }; const response = await fetch(`${API_URL}/transactions/build`, { method: "POST", headers: HEADERS, body: JSON.stringify(BODY), }); console.log(await response.json()); export {}; ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/node-and-fetch#using-fetched-utxos) Using Fetched Utxos **Payload (Using a list of utxos)** _You can use this payload when you can fetch the utxos, for example, when you have a wallet extension_ Copy { "utxos": ["8282...", "8282...", "..."], "changeAddress": "addr_sender...", "outputs": [\ {\ "address": "addr_recipient...",\ "lovelace": 10_000_000\ }\ ] } _Add the required UTXOs for your transaction. (usually all of them)_ ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/node-and-fetch#get-utxo) Get UTXO _You can retrieve your UTXOs from your wallet using these functions in the browser console. (Example: Chrome console with Eternl)_ Copy const wallet = await window.cardano.eternl.enabled(); const utxos = await wallet.getUtxos(); console.log(utxos); _Then, copy your UTXO array and include it as a parameter in the request body, as shown below._ ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/node-and-fetch#params) Params Copy const UTXOS = [\ "828258200599b9572ee291d70e2ffe6ac876c1e814c952a1633e4c48ac58e39726123d9d0082583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a002dc6c0",\ "8282582020932c96eccc8ffdde138945a492a568942f7ef9181b1805eaae9a4bfdede28a0282583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a3b9a6a05",\ "8282582020932c96eccc8ffdde138945a492a568942f7ef9181b1805eaae9a4bfdede28a0382583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a3b9a6a05",\ "8282582020932c96eccc8ffdde138945a492a568942f7ef9181b1805eaae9a4bfdede28a0482583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a1dcd3507",\ "8282582020932c96eccc8ffdde138945a492a568942f7ef9181b1805eaae9a4bfdede28a0582583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a1dcd3502",\ "8282582020932c96eccc8ffdde138945a492a568942f7ef9181b1805eaae9a4bfdede28a0682583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a1dcd3502",\ "828258209856940b54a9048607a5b081e8718602571e57f59de745c562ce7b74776c89a90082583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb256821a0011f436a1581c1af660e4c58514a2f0ea167deca340381e55bed4aea60bc09c211417a14d416e76696c546573743133353601",\ "82825820a2cf6441b06a025f92d32d3e5faa17227d8f1d2310c0a1081ceaaa72d2dd6aa90182583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a1dca3e37",\ "82825820e1cd7eee71dcf9de2ea133f6932552d922ff6cc0a34908cdec8c0eae8c9664d80582583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a000f4240",\ "82825820c83c3a8285c7b08c2cec3abf0c91dcec5afee531d6a0eeef002dd6d75456927d0182583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a44630781",\ "828258203aeb86b1388189192281b6b2c114235cec91b4a1fc3aeb4d73b1721855b93f3c0082583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561b00000002540be400",\ ]; const SENDER_ADDRESS = "addr_test1qrydyk6uw6cehk5u3zspyz3dhnwzmhfls2fp42vv5dv9g2z3885pg4kpkn30ptezc855lu3w5ey93zcr5lrezjmwkftqg8xvge"; const RECEIVER_ADDRESS = "addr_test1qr0tkwvlln0v5fljdxceudmlpt5y6szc84vpj4skm836tgn4hsqaesgg97l8ppy5rsn0alj8pth6lqe20fdyydsdgw6sr74cyt"; // 10 ADA => 1ADA = 1'000'000LOVELACE const LOVELACE_AMOUNT = 10_000_000; // See Authentication page for API key details. const X_API_KEY = "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9"; #### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/node-and-fetch#api-post-request-body) API POST Request Body _Body Structure for creating a basic transaction using the previously collected values._ Copy const BODY = { utxos: UTXOS, changeAddress: SENDER_ADDRESS, outputs: [\ {\ address: RECEIVER_ADDRESS,\ lovelace: LOVELACE_AMOUNT,\ },\ ], }; #### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/node-and-fetch#fetch-command-with-deno) Fetch Command with Deno Basic POST call with Fetch Copy const response = await fetch( `${API_URL}/transactions/build`, { method: "POST", headers: { "Content-Type": "application/json", "x-api-key": X_API_KEY, }, body: JSON.stringify(BODY), } ); console.log(await response.json()); ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/node-and-fetch#deno-command-1) Deno Command Copy deno run --allow-net basic-transaction.ts ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/node-and-fetch#output-1) Output Copy { hash: "b1f159d88e9dece4d327352c03a6fbb9484533dee60207367a2689f632650c9c", complete: "84a400d9010281825820c83c3a8285c7b08c2cec3abf0c91dcec5afee531d6a0eeef002dd6d75456927d01018282583900debb399ffcdeca27f269b19e377f0ae84d40583d58195616d9e3a5a275bc01dcc1082fbe7084941c26fefe470aefaf832a7a5a42360d43b51a0098968082583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a43c6a5fc021a0003cb05031a04ebdd50a0f5f6", stripped: "84a400d9010281825820c83c3a8285c7b08c2cec3abf0c91dcec5afee531d6a0eeef002dd6d75456927d01018282583900debb399ffcdeca27f269b19e377f0ae84d40583d58195616d9e3a5a275bc01dcc1082fbe7084941c26fefe470aefaf832a7a5a42360d43b51a0098968082583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a43c6a5fc021a0003cb05031a04ebdd50a0f5f6", witnessSet: "a0" } * * * ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/node-and-fetch#sign-and-submit-the-transaction) Sign and Submit the Transaction The **transaction** can be **sign** and **submitted.** [https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/transaction/signing-transaction.md](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/transaction/signing-transaction.md) [Submit Transaction](https://dev.ada-anvil.io/guides/transaction/submit-transaction) [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/node-and-fetch#the-whole-file-deno-version-1) The Whole File (Deno Version) -------------------------------------------------------------------------------------------------------------------------------------------------------- basic-transaction.ts[](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/node-and-fetch#basic-transaction.ts-1) Copy const UTXOS = [\ "828258200599b9572ee291d70e2ffe6ac876c1e814c952a1633e4c48ac58e39726123d9d0082583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a002dc6c0",\ "8282582020932c96eccc8ffdde138945a492a568942f7ef9181b1805eaae9a4bfdede28a0282583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a3b9a6a05",\ "8282582020932c96eccc8ffdde138945a492a568942f7ef9181b1805eaae9a4bfdede28a0382583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a3b9a6a05",\ "8282582020932c96eccc8ffdde138945a492a568942f7ef9181b1805eaae9a4bfdede28a0482583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a1dcd3507",\ "8282582020932c96eccc8ffdde138945a492a568942f7ef9181b1805eaae9a4bfdede28a0582583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a1dcd3502",\ "8282582020932c96eccc8ffdde138945a492a568942f7ef9181b1805eaae9a4bfdede28a0682583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a1dcd3502",\ "828258209856940b54a9048607a5b081e8718602571e57f59de745c562ce7b74776c89a90082583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb256821a0011f436a1581c1af660e4c58514a2f0ea167deca340381e55bed4aea60bc09c211417a14d416e76696c546573743133353601",\ "82825820a2cf6441b06a025f92d32d3e5faa17227d8f1d2310c0a1081ceaaa72d2dd6aa90182583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a1dca3e37",\ "82825820e1cd7eee71dcf9de2ea133f6932552d922ff6cc0a34908cdec8c0eae8c9664d80582583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a000f4240",\ "82825820c83c3a8285c7b08c2cec3abf0c91dcec5afee531d6a0eeef002dd6d75456927d0182583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561a44630781",\ "828258203aeb86b1388189192281b6b2c114235cec91b4a1fc3aeb4d73b1721855b93f3c0082583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561b00000002540be400",\ ]; const SENDER_ADDRESS = "addr_test1qrydyk6uw6cehk5u3zspyz3dhnwzmhfls2fp42vv5dv9g2z3885pg4kpkn30ptezc855lu3w5ey93zcr5lrezjmwkftqg8xvge"; const RECEIVER_ADDRESS = "addr_test1qr0tkwvlln0v5fljdxceudmlpt5y6szc84vpj4skm836tgn4hsqaesgg97l8ppy5rsn0alj8pth6lqe20fdyydsdgw6sr74cyt"; // 10 ADA => 1ADA = 1'000'000LOVELACE const LOVELACE_AMOUNT = 10_000_000; // See Authentication page for API key details. const X_API_KEY = "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9"; const API_URL = "https://preprod.api.ada-anvil.app/v2/services"; const BODY = { utxos: UTXOS, changeAddress: SENDER_ADDRESS, outputs: [\ {\ address: RECEIVER_ADDRESS,\ lovelace: LOVELACE_AMOUNT,\ },\ ], }; const response = await fetch( `${API_URL}/transactions/build`, { method: "POST", headers: { "Content-Type": "application/json", "x-api-key": X_API_KEY }, body: JSON.stringify(BODY), } ); console.log(await response.json()); export {}; [PreviousBash & cURL](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/bash-and-curl) [NextNext.js & Weld](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld) Last updated 16 days ago Was this helpful? --- # Bash & cURL | Anvil Development Agency [](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction/bash-and-curl#introduction) Introduction ---------------------------------------------------------------------------------------------------------------------- This guide will show you how to create a custom transaction using bash and cURL. [](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction/bash-and-curl#objectives) Objectives ------------------------------------------------------------------------------------------------------------------ This guide shows the robustness of the Anvil API transaction builder, by sending two assets and ADA to multiple recipients all within a single transaction. [](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction/bash-and-curl#requirements) Requirements ---------------------------------------------------------------------------------------------------------------------- * A Cardano wallet with 2 assets and at a minimum 20ADA * Two Cardano wallets to send assets and ADA to * A wallet extension to sign the transaction * An API key [](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction/bash-and-curl#payload) Payload ------------------------------------------------------------------------------------------------------------ **Send 10 ADA + 1 asset to address#1 and Send 5 ADA + 1 asset to address#2** Copy { "changeAddress": "", "outputs": [\ {\ "address": "",\ "lovelace": 10_000_000,\ "assets": [\ {\ "policyId": "",\ "assetName": "",\ "quantity": 1\ }\ ]\ },\ {\ "address": "",\ "lovelace": 5_000_000,\ "assets": [\ {\ "policyId": "",\ "assetName": "",\ "quantity": 1\ }\ ]\ }\ \ ] } [](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction/bash-and-curl#usage) Usage -------------------------------------------------------------------------------------------------------- Using Bash/cURL ### [](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction/bash-and-curl#parameters) Parameters Copy # Wallets SENDER_ADDRESS="addr_test1qq7fc3ke49nkcsfglltut7apa9t3gdul4utwhxt6j2hdrw7pg4vk6erdshyhdj5xeq0vh8qdy34cpdfstvc8l9su8hgq679eew" RECEIVER_ADDRESS_1="addr_test1qrydyk6uw6cehk5u3zspyz3dhnwzmhfls2fp42vv5dv9g2z3885pg4kpkn30ptezc855lu3w5ey93zcr5lrezjmwkftqg8xvge" RECEIVER_ADDRESS_2="addr_test1qr0tkwvlln0v5fljdxceudmlpt5y6szc84vpj4skm836tgn4hsqaesgg97l8ppy5rsn0alj8pth6lqe20fdyydsdgw6sr74cyt" # Resources POLICY_ID="360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13" ASSET_NAME_1="616e76696c61706963697032355f333837393837393739" ASSET_NAME_2="616e76696c61706963697032355f333837393732" # See Authentication page for API key details. X_API_KEY="testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9" API_URL="https://preprod.api.ada-anvil.app/v2/services" ### [](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction/bash-and-curl#code) Code Copy curl -X POST \ -H "Content-Type: application/json" \ -H "X-Api-Key: ${X_API_KEY}" \ -d '{ "changeAddress": "'${SENDER_ADDRESS}'", "outputs": [\ {\ "address": "'${RECEIVER_ADDRESS_1}'",\ "lovelace": 10000000,\ "assets": [{\ "assetName": "'${ASSET_NAME_1}'",\ "policyId": "'${POLICY_ID}'",\ "quantity": 1\ }]\ },\ {\ "address": "'${RECEIVER_ADDRESS_2}'",\ "lovelace": 5000000,\ "assets": [{\ "assetName": "'${ASSET_NAME_2}'",\ "policyId": "'${POLICY_ID}'",\ "quantity": 1\ }]\ }\ ] }' \ ${API_URL}/transactions/build ### [](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction/bash-and-curl#expected-response) Expected Response Copy { hash: "7befd78854418b5465d505fa853a0631c1e8165bb0557b15ec6f194fcacd19a9", complete: "84a600d90102838258205bf3681e7bfe3322c5e05cda80a5a784177d3239f1aae6d929c8a86be3474198018258206a6bb5dafa917de09a2b727d4050c785f59f39f1b819e43a34570c8a537cadf00825820e490036d4c93cca91ada4365a2ea99d6a6b030338d041c02ba0a77e91c417eac01018482583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca3585428513981456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb256821a00989680a1581c360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13a157616e76696c6170696369702355f3338373938373937390182583900debb399ffcdeca27f269b19e377f0ae84d40583d58195616d9e3a5a275bc01dcc1082fbe7084941c26fefe470aefaf832a7a5a42360d43b5821a00c4b40a1581c360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13a154616e76696c61706963697032355f33383739373201a300581d605d2018b32a3752c252a8f08f5d34af8ff8993da9c30d5bf94e26522011a00155cc0028201d8184a49616e76696c2d746167825839003c9c46d9a9676c4128ffd7c5fba1e95714379faf16eb997a92aed1bbc145596d646d85c96ca86c81ecb9c0d246b80b5305b307f961c3dd0821a1c5c8558a1581c360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13a3581b616e76696c61706963697032355f313743130333330373738303401581b616e76696c61706963697032355f3137343130333331383531353001581b616e76696c61706963697032355f3137343133363330393135333801021a000329d031a0522114b081a0521f52b0ed9010281581c5d2018b32a3752c252a8f08f5d34daf8ff8993da9c30d5bf94e26522a100d9010281825820292af64334964e4e37e09a65aa7b14bf91a26236817ccc13acc660ccd2f3b725840003c389bf132640f78158f180b14f057a9919924b79c5e4771a9bccb0c225cde1bf3f1e5da4aac0649d8d905aa6dbe8734fa9da0053af103b1c0e480bb25503f5f6", stripped:"84a600d90102838258205bf3681e7bfe3322c5e05cda80a5a784177d3239f1aae6d929c8a86be3474198018258206a6bb5dafa917de09a2b727d4050c785f59f39f1b819e43a34570c8a537cadf00825820e490036d4c93cca91ada4365a2ea99d6a6b030338d041c02ba0a77e91c417eac01018482583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca3585428513981456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb256821a00989680a1581c360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13a157616e76696c6170696369702355f3338373938373937390182583900debb399ffcdeca27f269b19e377f0ae84d40583d58195616d9e3a5a275bc01dcc1082fbe7084941c26fefe470aefaf832a7a5a42360d43b5821a00c4b40a1581c360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13a154616e76696c61706963697032355f33383739373201a300581d605d2018b32a3752c252a8f08f5d34af8ff8993da9c30d5bf94e26522011a00155cc0028201d8184a49616e76696c2d746167825839003c9c46d9a9676c4128ffd7c5fba1e95714379faf16eb997a92aed1bbc145596d646d85c96ca86c81ecb9c0d246b80b5305b307f961c3dd0821a1c5c8558a1581c360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13a3581b616e76696c61706963697032355f313743130333330373738303401581b616e76696c61706963697032355f3137343130333331383531353001581b616e76696c61706963697032355f3137343133363330393135333801021a000329d031a0522114b081a0521f52b0ed9010281581c5d2018b32a3752c252a8f08f5d34daf8ff8993da9c30d5bf94e26522a0f5f6", witnessSet:"a100d9010281825820292af64334964e4e37e09a65aa7b14bf91a286236817ccc13acc660ccd2f3b725840003c389bf132640f78158f180b14f057a9919924b79c5e4771a9bccb0c225cdebf3f1e5da4aac0649d8d905aa6dbe8734fa9da0053af103b1c0e480fbb25503" } [PreviousCreate Custom Transaction](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction) [NextDeno & Fetch](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction/node-and-fetch) Last updated 3 months ago Was this helpful? --- # Bash & cURL | Anvil Development Agency ### [](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-pool/bash-and-curl#prepare-pool-delegation-transaction) Prepare Pool Delegation Transaction **Payload** Copy { "changeAddress": "addr_test1...", "delegations": [\ {\ "type": "pool",\ "address": "addr_test1...",\ "keyHash": "pool1z5uq..."\ }\ ] } **Example** Copy POOL_ID="pool1n3sjq3qvu5vvcd6aud6ndcwq7r3ghmkafcg60gznlwfrk2ucxku" ADDR="addr_test1qzyttcj6czjltcs3tn3vls6yg90542lctrzwg5aagduqlfgupztxhczzmuakzfuwvrht542yrx7ll3fk29lcl2xl8axqh3pjdh" # See Authentication page for API key details. X_API_KEY="testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9" Copy curl -X POST \ -H "Content-Type: application/json" \ -H "X-Api-Key: ${X_API_KEY}" \ -d '{ "changeAddress": "'${ADDR}'", "delegations": [\ {\ "type": "pool",\ "address": "'${ADDR}'",\ "keyHash": "'${POOL_ID}'"\ }\ ] }' \ https://preprod.api.ada-anvil.app/v2/services/transactions/build **Output** Copy { "hash": "a4633c716755a8d72d07c058ffabe94c9b8b0f8273a8d7babb1f607050fb3c3b", "complete": "84a700d90102828258203376a58b03a1a26108d79e3db28207d1e3b7db51266bb9d07691b3d724d8198201825820f4bf2eb3572ec503081a24baf506ee26f5a2ee8d5d6c707830b75516db9bb9ed020182a300581d60355efb09c4a29c7e3b63ead47a220efcbbdef1fd5369c4efff947b4b011a001dc130028201d8184a49616e76696c2d7461678258390088b5e25ac0a5f5e2115ce2cfc344415f4aabf858c4e453bd43780fa51c08966be042df3b61278e60eeba554419bdffc536517f8fa8df3f4c1a38227e99021a00032095031a053f215904d901028282008200581c1c08966be042df3b61278e60eeba554419bdffc536517f8fa8df3f4c83028200581c1c08966be042df3b61278e60eeba554419bdffc536517f8fa8df3f4c581c9c6120440ce518cc375de37536e1c0f0e28beedd4e11a7a053fb923b081a053f05390ed9010281581c355efb09c4a29c7e3b63ead47a220efcbbdef1fd5369c4efff947b4ba100d90102818258204bc7618201307ab45cdb3793107822c47230f097e9771fb358473cdb82b2e644584020ecc8e8a0b6d2f4a2bd51d388ee20abebf4982d55ce738d9b1c223bc77152fa175c15729e96073873d262017ea113ff41a8ec26a16441029f00fbf004229b07f5f6", "stripped": "84a700d90102828258203376a58b03a1a26108d79e3db28207d1e3b7db51266bb9d07691b3d724d8198201825820f4bf2eb3572ec503081a24baf506ee26f5a2ee8d5d6c707830b75516db9bb9ed020182a300581d60355efb09c4a29c7e3b63ead47a220efcbbdef1fd5369c4efff947b4b011a001dc130028201d8184a49616e76696c2d7461678258390088b5e25ac0a5f5e2115ce2cfc344415f4aabf858c4e453bd43780fa51c08966be042df3b61278e60eeba554419bdffc536517f8fa8df3f4c1a38227e99021a00032095031a053f215904d901028282008200581c1c08966be042df3b61278e60eeba554419bdffc536517f8fa8df3f4c83028200581c1c08966be042df3b61278e60eeba554419bdffc536517f8fa8df3f4c581c9c6120440ce518cc375de37536e1c0f0e28beedd4e11a7a053fb923b081a053f05390ed9010281581c355efb09c4a29c7e3b63ead47a220efcbbdef1fd5369c4efff947b4ba0f5f6", "witnessSet": "a100d90102818258204bc7618201307ab45cdb3793107822c47230f097e9771fb358473cdb82b2e644584020ecc8e8a0b6d2f4a2bd51d388ee20abebf4982d55ce738d9b1c223bc77152fa175c15729e96073873d262017ea113ff41a8ec26a16441029f00fbf004229b07" } [PreviousDelegate to a Stake Pool](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-pool) [NextDeno & Fetch](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-pool/deno-and-fetch) Last updated 3 months ago Was this helpful? --- # Select UTXOs | Anvil Development Agency [](https://dev.ada-anvil.io/guides/transaction/selecting-utxos#overview) Overview -------------------------------------------------------------------------------------- Unspent Transaction Outputs (UTXOs) are a fundamental concept in Cardano's accounting model. Unlike an account-based model that tracks balances, the UTXO model tracks individual outputs of transactions that haven't been spent yet. When building a Cardano transaction with Anvil API, you have two options for specifying transaction inputs: 1. **Provide UTXO List** - Explicitly specify which UTXOs to use (required in production) 2. **Automatic Selection** - Let Anvil choose UTXOs for you (testing environments only) ### [](https://dev.ada-anvil.io/guides/transaction/selecting-utxos#automatic-utxo-selection-in-testing) Automatic UTXO Selection in Testing In test environments (preprod, preview), the `utxos` parameter in the transaction building API is **optional**. If you don't provide it, Anvil will: * Automatically fetch UTXOs associated with the `changeAddress` * Select appropriate UTXOs to cover the transaction * Handle all input management for you This simplifies development and testing by removing the need to manage UTXO selection logic until you're ready for production. If you are using a multi-account wallet (e.g., Eternl multi-account setting), you will need to provide the `utxos` parameter in the transaction building API. Anvil will not be able to automatically select UTXOs for you in this case. Copy // Example: Let Anvil handle UTXO selection (test environments only) const response = await fetch('https://preprod.api.ada-anvil.app/v2/services/transactions/build', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Api-Key': 'YOUR_API_KEY' }, body: JSON.stringify({ // Just provide a change address - no UTXOs needed! changeAddress: 'addr_test...', outputs: [\ {\ address: 'recipient_address',\ lovelace: 5000000 // 5 ADA\ }\ ] }) }); ### [](https://dev.ada-anvil.io/guides/transaction/selecting-utxos#production-requirement) Production Requirement For production environments, you **must explicitly provide** a list of available UTXOs using the `utxos` parameter. Additionally, the API provides an optional `requiredInputs` parameter to force the inclusion of specific UTXOs in the transaction. * `**utxos**` **(Required in Production)**: An array of available hex-CBOR encoded UTXOs (e.g., \[`8282...`, `8282...`\]) that the transaction builder can use to cover the cost of the transaction. The builder will intelligently select the best combination from this list. * `**requiredInputs**` **(Optional)**: An array of specific hex-CBOR encoded UTXOs (e.g., \[`8282...`, `8282...`\]) that **must** be included as inputs. This is useful for forcing the spending of a particular UTXO, such as one holding a smart contract script. Copy // Production example: Explicitly provide UTXOs and force a specific input const response = await fetch('https://prod.api.ada-anvil.app/v2/services/transactions/build', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Api-Key': 'YOUR_API_KEY' }, body: JSON.stringify({ changeAddress: 'addr...', // A list of available UTXOs for the builder to choose from (Required) utxos: ['8282...', '8282...'], // A specific UTXO that MUST be included in the transaction (Optional) requiredInputs: ['8282...'], outputs: [\ {\ address: 'recipient_address',\ lovelace: 5000000 // 5 ADA\ }\ ] }) }); [](https://dev.ada-anvil.io/guides/transaction/selecting-utxos#fetching-utxos-multiple-approaches) Fetching UTXOs: Multiple Approaches ------------------------------------------------------------------------------------------------------------------------------------------- Here are three efficient ways to retrieve UTXOs in your Cardano applications: ### [](https://dev.ada-anvil.io/guides/transaction/selecting-utxos#using-weld) Using Weld [Weld](https://github.com/Cardano-Forge/weld) is a bridge to browser-based wallets that implements the CIP-30 standard. It provides a unified interface for wallet interactions. Here's how to retrieve UTXOs from a connected wallet: Copy const wallet = useWallet("isConnected", "handler"); const interact = async () => { if (!wallet.isConnected) { return; } const utxos = await wallet.handler.getUtxos(); // Build Transaction using utxos // Sign Transaction const res = await wallet.handler.signTx("TX_CBOR_HERE"); }; This code snippet uses the Weld wallet connector to: 1. Check if a wallet is connected using the `isConnected` property 2. Retrieve the complete list of UTXOs associated with the wallet's addresses using `getUtxos()` 3. Demonstrate how these UTXOs could then be used in transaction signing with `signTx()` Weld simplifies wallet interactions by providing a consistent API regardless of which Cardano wallet the user has connected. ### [](https://dev.ada-anvil.io/guides/transaction/selecting-utxos#using-blockfrost) Using BlockFrost [BlockFrost](https://blockfrost.io/) is a popular API-as-a-service for Cardano that provides access to on-chain data. The following example demonstrates fetching UTXOs via the BlockFrost API and converting them to the format expected by `cardano-serialization-lib` (CSL): Copy import { Buffer } from "node:buffer"; import { TransactionUnspentOutput, TransactionInput, TransactionOutput, TransactionHash, TransactionUnspentOutputs, MultiAsset, Value, Assets, AssetName, BigNum, ScriptHash, Address, } from "@emurgo/cardano-serialization-lib-nodejs"; export function hex_to_uint8(input: string): Uint8Array { return new Uint8Array(Buffer.from(input, "hex")); } export function assets_to_value( multi_asset: MultiAsset, assets: Asset[], ): Value { const qt = assets.find((asset) => asset.unit === "lovelace")?.quantity; if (!qt) { throw new Error("No lovelace found in the provided utxo"); } const assets_to_add: Record = {}; for (const asset of assets) { if (asset.unit !== "lovelace") { const policy_hex = asset.unit.slice(0, 56); const asset_hex = hex_to_uint8(asset.unit.slice(56)); if (!assets_to_add[policy_hex]) { assets_to_add[policy_hex] = Assets.new(); } assets_to_add[policy_hex].insert( AssetName.new(asset_hex), BigNum.from_str(asset.quantity), ); } } for (const key of Object.keys(assets_to_add)) { multi_asset.insert(ScriptHash.from_hex(key), assets_to_add[key]); } return Value.new_with_assets(BigNum.from_str(qt), multi_asset); } export type Asset = { unit: string; quantity: string; }; export type Utxo = { address: string; tx_hash: string; output_index: number; amount: Asset[]; block?: string; data_hash?: string | null; inline_datum?: string | null; reference_script_hash?: string | null; }; export async function get_utxos_api( blockfrost_base_url: string, blockfrost_api_key: string, address: string, ): Promise { const get = async (data: object[] = [], page = 1) => { const res = await fetch( `${blockfrost_base_url}/addresses/${address}/utxos?page=${page}`, { method: "GET", headers: { "Content-Type": "application/json", Accept: "application/json", project_id: blockfrost_api_key, }, }, ); if (res.status < 199 || res.status > 200) { throw new Error("Unable to get utxos for specified address"); } const json = await res.json(); if (json.length > 0) { return await get([...data, ...json], page + 1); } return [...data, ...json]; }; return await get(); } const utxos = await get_utxos_api( "https://cardano-mainnet.blockfrost.io/api/v0", Deno.env.get("BLOCKFROST_PROJECT_ID"), "addr1q96gmvs93t96txjv43sw5gtf6pfzwmsu07t635pmlvwsucup2645cqyrknctcsd3s4e6wc23fxqsr3mywdlx9lnme6sshlyndu", ); console.log("UTXOs Found:", utxos.length); const parsedUtxo: TransactionUnspentOutputs = TransactionUnspentOutputs.new(); for (const utxo of utxos) { const multi_assets = MultiAsset.new(); const { tx_hash, output_index, amount, address } = utxo as Utxo; // If we have enough utxos, we can proceed. parsedUtxo.add( TransactionUnspentOutput.new( TransactionInput.new(TransactionHash.from_hex(tx_hash), output_index), TransactionOutput.new( Address.from_bech32(address), assets_to_value(multi_assets, amount), ), ), ); } This code performs several key operations: 1. Defines utility functions to convert between different data formats, including the critical `assets_to_value` function that transforms BlockFrost's asset representation to CSL's `Value` type 2. Implements a paginated API request to BlockFrost's `/addresses/{address}/utxos` endpoint with proper authentication 3. Constructs `TransactionUnspentOutput` objects from the returned data, which include: * Transaction inputs (with transaction hash and output index) * Transaction outputs (with address and value information) 4. Combines these into a `TransactionUnspentOutputs` collection that can be used for transaction building This approach is particularly useful for server-side applications where you need to query UTXOs without a connected wallet. ### [](https://dev.ada-anvil.io/guides/transaction/selecting-utxos#using-dolos) Using Dolos [Dolos](https://github.com/txpipe/dolos) is a specialized Cardano "Data Node" designed for efficient data access. It works with [UTXOrpc](https://utxorpc.org/) , a standardized protocol for querying UTXO-based blockchains. The example below shows how to use the UTXOrpc SDK to retrieve UTXOs: Copy import { CardanoQueryClient } from "@utxorpc/sdk"; import { Buffer } from "node:buffer"; import { TransactionUnspentOutput, TransactionInput, TransactionOutput, TransactionHash, TransactionUnspentOutputs, } from "@emurgo/cardano-serialization-lib-nodejs"; const queryClient = new CardanoQueryClient({ // Dolos Endpoint uri: Deno.env.get("DOLOS_ENDPOINT"), }); console.time("dolos"); const utxos = await queryClient.searchUtxosByAddress( Buffer.from( // Fetched from browser using wallet.getChangeAddress() "01748db2058acba59a4cac60ea2169d052276e1c7f97a8d03bfb1d0e638156ab4c0083b4f0bc41b18573a76151498101c764737e62fe7bcea1", "hex", ), ); console.log("UTXOs Found:", utxos.length); const parsedUtxo: TransactionUnspentOutputs = TransactionUnspentOutputs.new(); for (const utxo of utxos) { // UTXOs from dolos const bytes = utxo.nativeBytes; if (!bytes) { throw new Error("No native bytes"); } parsedUtxo.add( TransactionUnspentOutput.new( TransactionInput.new( TransactionHash.from_bytes(utxo.txoRef.hash), utxo.txoRef.index, ), TransactionOutput.from_bytes(bytes), ), ); } const utxoHexes: string[] = []; for (let i = 0; i < parsedUtxo.len(); i++) { utxoHexes.push(parsedUtxo.get(i).to_hex()); } console.log(utxoHexes); This example demonstrates: 1. Initializing a `CardanoQueryClient` from the UTXOrpc SDK, pointing to a Dolos endpoint 2. Querying UTXOs for a specific address (the address is provided in hex format) 3. Converting the returned UTXOs directly to `TransactionUnspentOutput` objects using the native byte format 4. Creating a collection of UTXOs that can be used for transaction building 5. Converting each UTXO to its hexadecimal representation for debugging or further processing Dolos with UTXOrpc offers a more lightweight and efficient approach to data retrieval compared to running a full Cardano node. It's particularly well-suited for applications that need high performance UTXO retrieval with lower resource requirements. [](https://dev.ada-anvil.io/guides/transaction/selecting-utxos#comparison-and-considerations) Comparison and Considerations -------------------------------------------------------------------------------------------------------------------------------- * **Weld**: Best for client-side/browser applications where a user's wallet is already connected. Simplest API for direct wallet interaction. * **BlockFrost**: Good for server-side applications or when no wallet is connected. Requires an API key and performs well for most use cases. * **Dolos**: Optimal for high-performance applications. Requires running a Dolos instance or connecting to a hosted one. Provides native format UTXOs that minimize conversion overhead. Choose the approach that best fits your application architecture, performance requirements, and deployment environment. ### [](https://dev.ada-anvil.io/guides/transaction/selecting-utxos#related-resources) Related Resources [https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/guides/transaction/README.md](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/guides/transaction/README.md) [Utility Functions](https://dev.ada-anvil.io/developer-tools/utility-functions) [PreviousPart 3: Building Transactions](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-transactions) [NextSign Transaction](https://dev.ada-anvil.io/guides/transaction/signing-transaction) Last updated 16 days ago Was this helpful? --- # Policy Units | Anvil Development Agency In Cardano, "unit" typically refers to the policy ID plus the asset name in a single hex string that identifies a native token or NFT. Because you can have thousands of different tokens, each must be uniquely identified by a combination of: * **Policy ID** (28 bytes, 56 hex characters). * **Asset Name** (up to 32 bytes, variable hex length). For CIP-25 or CIP-68 NFTs, you might see `unit` used in your minted assets or outputs. Similarly, when listing assets in an output, you specify: Copy "unit": "" For example, if your `policyId` is: Copy 1af660e4c58514a2f0ea167deca340381e55bed4aea60bc09c211417 and your asset name (in hex) is: Copy 416e76696c54657374312038 the **Unit** becomes: Copy 1af660e4c58514a2f0ea167deca340381e55bed4aea60bc09c211417416e76696c54657374312038 * * * **Why Do Policy IDs and Asset Names Matter?** 1. **Token Gating and Access Control** * Imagine you run a membership site or event. You want to grant access only to users who hold a specific NFT (like a “hotel keycard”). * To validate their NFT, you check the **policy ID** (the “hotel”) and the **asset name** (the “room”). If a user’s wallet has the exact combination, they get in. 1. **Verification** * If someone claims they hold a certain collectible or token, you can confirm by comparing their token’s **Unit** to the official “Unit” you expect. * This prevents duplicates or fakes. 1. **Metadata and Searching** * Explorers like preprod.cexplorer.io allow you to search by policy ID or asset name, letting you track how many tokens are minted, who holds them, and more. 1. **Minting and Burning** * To mint or burn tokens, the blockchain needs to know exactly **which** asset under **which** policy you’re referring to. * The policy ID and asset name form that unique identifier. * * * **How to Extract the Policy ID and Asset Name from a Unit** If you have the **full unit string** (`policyId + assetName` in hex), you can slice it into two parts: Copy const unit = "1af660e4c58514a2f0ea167deca340381e55bed4aea60bc09c211417416e76696c54657374312038"; // First 56 characters (28 bytes in hex) is the policy ID: const policyId = unit.slice(0, 56); // The remaining portion is the asset name in hex: const encodedAssetName = unit.slice(56); // Optionally, you can convert the asset name from hex to plain text: const assetName = Buffer.from(encodedAssetName, "hex").toString(); **Tip**: Some asset names contain non-ASCII characters, so you might see garbled text if you convert from hex incorrectly or if it’s truly a binary representation. Always check the token’s known format. * * * **Where to Find Your Policy ID and Asset Name** **1\. From a Blockchain Explorer** 1. Go to an explorer like Preprod CExplorer (for testnet) or CExplorer.io (for mainnet). 2. Paste the asset name or policy ID in the search bar (e.g., “AnvilTest1208”). 3. On the asset’s detail page, you’ll typically see: * **Encoded Asset Name** (hex) * **Policy ID** (hex string) Once you have both, concatenate them to get the **unit**. **2\. From a Wallet (e.g., Eternl)** 1. Open your wallet interface. 2. Locate the NFT or token in question. 3. The wallet interface usually displays both: * **Policy ID** * **Asset Name** (in either plain text or hex) Some wallets (like Eternl) also display the **unit** or a direct link to an explorer page. * * * TBD: Add practical example. * * * **Additional Tips and Best Practices** 1. **Hex vs. ASCII** * Always confirm whether your asset name is stored in **hex** or **ASCII**. * If you do comparisons in your code, stay consistent—comparing hex to ASCII won’t match. 1. **Policy Expiration** * Some policies allow minting indefinitely; others have an expiration. Check the policy’s script if you plan to handle or mint new tokens under it. 1. **Handling Special Characters** * Asset names may include emojis or non-English text. The hex representation ensures compatibility, but the plain text might look odd. 1. **Batching** * For dApps dealing with numerous tokens, consider how you’ll fetch all these units efficiently (paging, caching, etc.). 1. **Don’t Overlook Testnets** * If you’re new to token gating or minted assets, test your approach on **Preprod** or **Preview** first. Real ADA isn’t at risk there. * * * **Summary** * `Unit = policyId + assetName` (in hex). * **Why It Matters**: It’s the unique identifier for any Cardano-native asset, crucial for gating, verification, minting, and more. * **Where to Get Them**: Use explorers, your wallet, or direct blockchain APIs. * **Hotel Keycard Analogy**: The policy ID is the “hotel,” and the asset name is the “room.” Checking them together ensures you grant access to the right person holding the right token. With a solid grasp of **Units**, **Policy IDs**, and **Asset Names**, you can confidently build sophisticated Cardano applications—whether that’s token-gated dApps, NFT marketplaces, or membership sites—knowing exactly which assets users hold and how to verify them securely. * * * **Next Steps** Learn about Utilities Functions to simplify asset handling (e.g., `unitToName()`, `nameToUnit()`, etc.). Jump into Transaction for guidance on how to send assets, sign transactions, or mint new tokens with Anvil. Explore advanced NFT & FT documentation to create, distribute, and manage your custom assets. [PreviousCreating Native Scripts](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts/create-native-script) [NextMint NFTs (CIP-68)](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-68) Last updated 11 days ago Was this helpful? --- # Delegations | Anvil Development Agency [](https://dev.ada-anvil.io/guides/delegations#api-overview) API Overview ------------------------------------------------------------------------------ Delegations in Cardano enable users to participate in the network without running infrastructure themselves. Stake pool delegations allow users to earn rewards by supporting the network's security, while DRep delegations empower users to participate in governance decisions. The Anvil API simplifies implementing both delegation types through a unified interface, letting applications offer these essential features without complex blockchain interactions. Type Purpose Certificate Created `pool` Delegate to stake pool for rewards `StakeDelegation` `drep` Delegate to DRep for governance `VoteDelegation` [](https://dev.ada-anvil.io/guides/delegations#api-endpoint) API Endpoint ------------------------------------------------------------------------------ **URL**: `https://preprod.api.ada-anvil.app/v2/services/transactions/build` **Method**: POST **Headers**: `Content-Type: application/json`, `x-api-key: YOUR_API_KEY` [](https://dev.ada-anvil.io/guides/delegations#request-format) Request Format ---------------------------------------------------------------------------------- Copy { "changeAddress": "addr_test1...", "delegations": [\ {\ "type": "pool" | "drep",\ "address": "addr_test1...",\ "keyHash": "pool_id_or_drep_id"\ }\ ] } [](https://dev.ada-anvil.io/guides/delegations#parameters) Parameters -------------------------------------------------------------------------- Parameter Description Required `type` Either `"pool"` or `"drep"` Yes `address` User's full address Yes `keyHash` Pool ID or DRep ID Yes `changeAddress` Address for change Yes `utxos` Array of UTXOs in CBOR hex format Yes (in production) [](https://dev.ada-anvil.io/guides/delegations#implementation-guides) Implementation Guides ------------------------------------------------------------------------------------------------ [Delegate to a DRep](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-drep) [Delegate to a Stake Pool](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-pool) [](https://dev.ada-anvil.io/guides/delegations#transaction-workflow) Transaction Workflow ---------------------------------------------------------------------------------------------- 1. **Prepare**: Include delegation certificates in transaction 2. **Build & Sign**: Process transaction with proper certificates and signatures 3. **Submit**: Send to network (changes effective after current epoch) [](https://dev.ada-anvil.io/guides/delegations#best-practices) Best Practices ---------------------------------------------------------------------------------- * **Verification**: Confirm pool/DRep IDs before delegation * **Testing**: Use testnet before mainnet operations * **Error Handling**: Implement robust handling for: * Invalid address formats * Unknown pool/DRep IDs * Insufficient funds * Missing signers [](https://dev.ada-anvil.io/guides/delegations#related-resources) Related Resources ---------------------------------------------------------------------------------------- * [Signing Transactions](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/transaction/signing-transaction.md) * [Submitting Transactions](https://dev.ada-anvil.io/guides/transaction/submit-transaction) [PreviousDeno & Fetch](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25/deno-and-fetch) [NextDelegate to a DRep](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-drep) Last updated 16 days ago Was this helpful? --- # Validators | Anvil Development Agency [Spend Validator](https://dev.ada-anvil.io/guides/smart-contract/validators/spend-validator) [PreviousSmart Contract Utilities](https://dev.ada-anvil.io/guides/smart-contract/smart-contract-utilities) [NextSpend Validator](https://dev.ada-anvil.io/guides/smart-contract/validators/spend-validator) Last updated 2 months ago Was this helpful? --- # Deno & Fetch | Anvil Development Agency [](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction/node-and-fetch#requirements) Requirements ----------------------------------------------------------------------------------------------------------------------- * A Cardano wallet with sufficient ADA and assets * Multiple recipient Cardano wallet addresses * Node.js environment (v14+) or Deno runtime * Valid API key for authentication [](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction/node-and-fetch#objectives) Objectives ------------------------------------------------------------------------------------------------------------------- This guide demonstrates the flexibility of the Anvil API for creating complex transactions. **What You'll Accomplish:** * Send different amounts of ADA to multiple recipients * Distribute different native assets to different addresses * Handle everything in a single transaction [](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction/node-and-fetch#api-request-structure) API Request Structure ----------------------------------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction/node-and-fetch#payload-format) Payload Format **Send 10 ADA + 1 asset to address#1 and Send 5 ADA + 1 asset to address#2** Copy { "changeAddress": "addr...", "outputs": [\ {\ "address": "addr...1",\ "lovelace": 10_000_000, // 10 ADA\ "assets": [\ {\ "policyId": "",\ "assetName": "",\ "quantity": 1\ }\ ]\ },\ {\ "address": "addr...2",\ "lovelace": 5_000_000, // 5 ADA\ "assets": [\ {\ "policyId": "",\ "assetName": "",\ "quantity": 1\ }\ ]\ }\ // Add as many outputs as needed\ ] } [](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction/node-and-fetch#implementation) Implementation --------------------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction/node-and-fetch#configuration-and-parameters) Configuration and Parameters Copy // Wallet addresses const SENDER_ADDRESS = "addr_test1qq7fc3ke49nkcsfglltut7apa9t3gdul4utwhxt6j2hdrw7pg4vk6erdshyhdj5xeq0vh8qdy34cpdfstvc8l9su8hgq679eew"; const RECEIVER_ADDRESS_1 = "addr_test1qrydyk6uw6cehk5u3zspyz3dhnwzmhfls2fp42vv5dv9g2z3885pg4kpkn30ptezc855lu3w5ey93zcr5lrezjmwkftqg8xvge"; const RECEIVER_ADDRESS_2 = "addr_test1qr0tkwvlln0v5fljdxceudmlpt5y6szc84vpj4skm836tgn4hsqaesgg97l8ppy5rsn0alj8pth6lqe20fdyydsdgw6sr74cyt"; // Asset information const POLICY_ID = "360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13"; const ASSET_NAME_1 = "616e76696c61706963697032355f333837393837393739"; const ASSET_NAME_2 = "616e76696c61706963697032355f333837393732"; // API configuration const X_API_KEY = "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9"; const API_URL = "https://preprod.api.ada-anvil.app/v2/services"; const HEADERS = { "Content-Type": "application/json", "x-api-key": X_API_KEY }; ### [](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction/node-and-fetch#request-body) Request Body _Body Structure for creating a custom transaction using the previously collected values._ Copy const BODY = { changeAddress: SENDER_ADDRESS, outputs: [\ {\ address: RECEIVER_ADDRESS_1,\ lovelace: 10_000_000, // 10 ADA\ assets: [\ {\ assetName: ASSET_NAME_1,\ policyId: POLICY_ID,\ quantity: 1,\ },\ ],\ },\ {\ address: RECEIVER_ADDRESS_2,\ lovelace: 5_000_000, // 5 ADA\ assets: [\ {\ assetName: ASSET_NAME_2,\ policyId: POLICY_ID,\ quantity: 1,\ },\ ],\ },\ ], }; ### [](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction/node-and-fetch#api-call-using-node.js-and-fetch) API Call (using Node.js and Fetch) Basic POST call with Fetch custom-transaction.ts Copy const response = await fetch(`${API_URL}/transactions/build`, { method: "POST", headers: HEADERS, body: JSON.stringify(BODY), }); const result = await response.json(); console.log(result); ### [](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction/node-and-fetch#deno-command) Deno Command Copy deno run --allow-net custom-transaction.ts ### [](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction/node-and-fetch#output) Output Copy { "hash": "7befd78854418b5465d505fa853a0631c1e8165bb0557b15ec6f194fcacd19a9", "complete": "84a600d90102838258205bf3681e7bfe3322c5e05cda80a5a784177d3239f1aae6d929c8a86be3474198018258206a6bb5dafa917de09a2b727d4050c785f59f39f1b819e43a34570c8a5377cadf00825820e490036d4c93cca91ada4365a2ea99d6a6b030338d041c02ba0a77e91c417eac01018482583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb256821a00989680a1581c360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13a157616e76696c61706963697032355f3338373938373937390182583900debb399ffcdeca27f269b19e377f0ae84d40583d58195616d9e3a5a275bc01dcc1082fbe7084941c26fefe470aefaf832a7a5a42360d43b5821a004c4b40a1581c360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13a154616e76696c61706963697032355f33383739373201a300581d605d2018b32a3752c252a8f08f5d34daf8ff8993da9c30d5bf94e26522011a00155cc0028201d8184a49616e76696c2d746167825839003c9c46d9a9676c4128ffd7c5fba1e95714379faf16eb997a92aed1bbc145596d646d85c976ca86c81ecb9c0d246b80b5305b307f961c3dd0821a1c5c8558a1581c360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13a3581b616e76696c61706963697032355f3137343130333330373738303401581b616e76696c61706963697032355f3137343130333331383531353001581b616e76696c61706963697032355f3137343133363330393135333801021a0003429d031a0522114b081a0521f52b0ed9010281581c5d2018b32a3752c252a8f08f5d34daf8ff8993da9c30d5bf94e26522a100d9010281825820292af64334964e4e37e09a65aa7b14bf91a286236817ccc13acc660ccd2f3b725840003c389bf132640f78158f180b14f057a9919924b79c5e4771a9bccb0c225cde1bf3f1e5da4aac0649d8d905aa6dbe8734fa9da0053af103b1c0e480fbb25503", "stripped": "84a600d90102838258205bf3681e7bfe3322c5e05cda80a5a784177d3239f1aae6d929c8a86be3474198018258206a6bb5dafa917de09a2b727d4050c785f59f39f1b819e43a34570c8a5377cadf00825820e490036d4c93cca91ada4365a2ea99d6a6b030338d041c02ba0a77e91c417eac01018482583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb256821a00989680a1581c360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13a157616e76696c61706963697032355f3338373938373937390182583900debb399ffcdeca27f269b19e377f0ae84d40583d58195616d9e3a5a275bc01dcc1082fbe7084941c26fefe470aefaf832a7a5a42360d43b5821a004c4b40a1581c360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13a154616e76696c61706963697032355f33383739373201a300581d605d2018b32a3752c252a8f08f5d34daf8ff8993da9c30d5bf94e26522011a00155cc0028201d8184a49616e76696c2d746167825839003c9c46d9a9676c4128ffd7c5fba1e95714379faf16eb997a92aed1bbc145596d646d85c976ca86c81ecb9c0d246b80b5305b307f961c3dd0821a1c5c8558a1581c360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13a3581b616e76696c61706963697032355f3137343130333330373738303401581b616e76696c61706963697032355f3137343130333331383531353001581b616e76696c61706963697032355f3137343133363330393135333801021a0003429d031a0522114b081a0521f52b0ed9010281581c5d2018b32a3752c252a8f08f5d34daf8ff8993da9c30d5bf94e26522a0f5f6", "witnessSet": "a100d9010281825820292af64334964e4e37e09a65aa7b14bf91a286236817ccc13acc660ccd2f3b725840003c389bf132640f78158f180b14f057a9919924b79c5e4771a9bccb0c225cde1bf3f1e5da4aac0649d8d905aa6dbe8734fa9da0053af103b1c0e480fbb25503" } [](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction/node-and-fetch#complete-example-deno-node.js) Complete Example (Deno/Node.js) ----------------------------------------------------------------------------------------------------------------------------------------------------------- custom-transaction.ts[](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction/node-and-fetch#custom-transaction.ts) Copy // Wallet addresses const SENDER_ADDRESS = "addr_test1qq7fc3ke49nkcsfglltut7apa9t3gdul4utwhxt6j2hdrw7pg4vk6erdshyhdj5xeq0vh8qdy34cpdfstvc8l9su8hgq679eew"; const RECEIVER_ADDRESS_1 = "addr_test1qrydyk6uw6cehk5u3zspyz3dhnwzmhfls2fp42vv5dv9g2z3885pg4kpkn30ptezc855lu3w5ey93zcr5lrezjmwkftqg8xvge"; const RECEIVER_ADDRESS_2 = "addr_test1qr0tkwvlln0v5fljdxceudmlpt5y6szc84vpj4skm836tgn4hsqaesgg97l8ppy5rsn0alj8pth6lqe20fdyydsdgw6sr74cyt"; // Asset information const POLICY_ID = "360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13"; const ASSET_NAME_1 = "616e76696c61706963697032355f333837393837393739"; const ASSET_NAME_2 = "616e76696c61706963697032355f333837393732"; // API configuration const X_API_KEY = "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9"; const API_URL = "https://preprod.api.ada-anvil.app/v2/services"; const HEADERS = { "Content-Type": "application/json", "x-api-key": X_API_KEY }; const requestBody = { changeAddress: SENDER_ADDRESS, outputs: [\ {\ address: RECEIVER_ADDRESS_1,\ lovelace: 10_000_000, // 10 ADA\ assets: [\ {\ assetName: ASSET_NAME_1,\ policyId: POLICY_ID,\ quantity: 1,\ },\ ],\ },\ {\ address: RECEIVER_ADDRESS_2,\ lovelace: 5_000_000, // 5 ADA\ assets: [\ {\ assetName: ASSET_NAME_2,\ policyId: POLICY_ID,\ quantity: 1,\ },\ ],\ },\ ], }; const response = await fetch(`${API_URL}/transactions/build`, { method: "POST", headers: HEADERS, body: JSON.stringify(requestBody), }); const result = await response.json(); console.log(result); [](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction/node-and-fetch#running-the-example) Running the Example ------------------------------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction/node-and-fetch#node.js) Node.js Copy node custom-transaction.js ### [](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction/node-and-fetch#deno) Deno Copy deno run --allow-net custom-transaction.ts [PreviousBash & cURL](https://dev.ada-anvil.io/guides/transaction/create-custom-transaction/bash-and-curl) [NextCreate Transaction with Metadata (CIP-20)](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20) Last updated 3 months ago Was this helpful? --- # Bash & cURL | Anvil Development Agency [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/bash-and-curl#introduction) Introduction ------------------------------------------------------------------------------------------------------------------------------------ This example demonstrates how to create a transaction with [CIP-20](https://cips.cardano.org/cip/CIP-20) metadata using the Anvil API. CIP-20 provides a standardized way to include human-readable messages in Cardano transactions using metadata label 674. For more information about transactions, please refer to the [Transaction Overview](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/transaction/transaction/README.md) . [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/bash-and-curl#objectives) Objectives -------------------------------------------------------------------------------------------------------------------------------- This example creates a transaction with a CIP-20 compliant message on the Preprod network. [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/bash-and-curl#requirements) Requirements ------------------------------------------------------------------------------------------------------------------------------------ * A Cardano wallet with ADA * A valid API key * Bash shell environment [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/bash-and-curl#api-request-structure) API Request Structure ------------------------------------------------------------------------------------------------------------------------------------------------------ ### [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/bash-and-curl#payload-format) Payload Format Copy { "changeAddress": "addr_sender...", "message": "Your message" // String or array of strings } [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/bash-and-curl#message-format-options) Message Format Options -------------------------------------------------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/bash-and-curl#single-string-message) Single String Message Copy { "changeAddress": "addr_sender...", "message": "Receipt 42" } ### [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/bash-and-curl#array-of-strings) Array of Strings Copy { "changeAddress": "addr_sender...", "message": ["Receipt 42", "Minted by Anvil"] } ### [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/bash-and-curl#auto-split-for-long-messages) Auto-split for Long Messages For messages exceeding 64 bytes, the API automatically splits them: Copy { "changeAddress": "addr_sender...", "message": "A very long message to demonstrate the auto-split feature. I need more words" } **Will be formatted as:** Copy { "msg": [\ "A very long message to demonstrate the auto-split feature. I nee",\ "d more words"\ ] } [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/bash-and-curl#implementation) Implementation ---------------------------------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/bash-and-curl#configuration-and-parameters-using-bash-and-curl) Configuration and Parameters (using Bash and cURL) _Using a preprod wallet for the transaction_ Copy ADDRESS="addr_test1qrvx8wgdndrk98qf62vka3q4fglchk7h940vepdtgcv9fuu0e0aeuac6j2xhz77esaaudku68ha89qesqvd29pmuzw6qk8xkcn" # See Authentication page for API key details. X_API_KEY="testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9" API_URL="https://preprod.api.ada-anvil.app/v2/services" ### [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/bash-and-curl#api-call-using-curl) API Call (using cURL) Basic POST call with cURL Copy curl -XPOST \ -H "Content-Type: application/json" \ -H "X-Api-Key: ${X_API_KEY}" \ -d '{"changeAddress": "'${ADDRESS}'", "message":["Receipt 42", "2025-01-24"]}' \ ${API_URL}/transactions/build ### [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/bash-and-curl#expected-output) Expected Output Copy { "hash": "28bdc13d5f532a10347f9671e5d3cd1aae9fae6a760e96808902f7e0baba878e", "complete": "84a500d9010281825820022ddfa716301851bb582acb30ff6d8be5718ea9565b87d0be70e5615a77a7cd01018182583900d863b90d9b47629c09d2996ec4154a3f8bdbd72d5ecc85ab461854f38fcbfb9e771a928d717bd9877bc6db9a3dfa728330031aa2877c13b41a1de152f0021a0003cb31031a04e44f940758201bd3787203ca834841a6e263f524989c7a2d8d340ae67098cc9ee225e7bb5df2a0f5a11902a2a1636d7367826a526563656970742034326a323032352d30312d3234", "stripped": "84a500d9010281825820022ddfa716301851bb582acb30ff6d8be5718ea9565b87d0be70e5615a77a7cd01018182583900d863b90d9b47629c09d2996ec4154a3f8bdbd72d5ecc85ab461854f38fcbfb9e771a928d717bd9877bc6db9a3dfa728330031aa2877c13b41a1de152f0021a0003cb31031a04e44f940758201bd3787203ca834841a6e263f524989c7a2d8d340ae67098cc9ee225e7bb5df2a0f5f6", "witnessSet": "a0", "auxiliaryData": "a11902a2a1636d7367826a526563656970742034326a323032352d30312d3234" } [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/bash-and-curl#the-whole-script-bash-version) The Whole Script (Bash Version) ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ cip-20.sh[](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/bash-and-curl#cip-20.sh) Copy #!/bin/bash # Run with: bash cip-20.sh ADDRESS="addr_test1qrvx8wgdndrk98qf62vka3q4fglchk7h940vepdtgcv9fuu0e0aeuac6j2xhz77esaaudku68ha89qesqvd29pmuzw6qk8xkcn" # See Authentication page for API key details. X_API_KEY="testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9" API_URL="https://preprod.api.ada-anvil.app/v2/services" curl -XPOST \ -H "Content-Type: application/json" \ -H "X-Api-Key: ${X_API_KEY}" \ -d '{"changeAddress": "'${ADDRESS}'", "message":["Receipt 42", "2025-01-24"]}' \ ${API_URL}/transactions/build [PreviousCreate Transaction with Metadata (CIP-20)](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20) [NextDeno & Fetch](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/node-and-fetch) Last updated 3 months ago Was this helpful? --- # Mint NFTs (CIP-68) | Anvil Development Agency #### [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-68#what-is-cip-68) What is CIP-68? Cardano Improvement Proposal 68 (CIP-68) defines a way to mint native assets whose metadata can be updated after issuance. By storing the metadata on-chain and linking it to each token, creators can evolve NFTs—such as game items or collectibles—without burning and re-minting them. #### [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-68#how-it-works) How It Works CIP-68 uses a dual-token system to achieve this functionality: 1. **Reference Token (**`**label: 100**`**):** This token holds the asset's metadata within an on-chain wallet (or smart contract script address). 2. **User Token (**`**label: 222**`**):** This is the actual token held in a user's wallet. It acts as a pointer to the Reference Token and inherits its metadata. Because the metadata lives with the Reference Token, creators can update an asset’s metadata simply by modifying that single token—no need to re-mint or burn the user-facing token. #### [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-68#validation-approaches-native-scripts-vs.-smart-contracts) Validation Approaches: Native Scripts vs. Smart Contracts CIP-68's updatable metadata is managed by a Reference Token, which can be secured in two ways: 1. **Metadata-Manager Wallet with a Native Script:** This approach uses a standard wallet and a native script to control the Reference Token. It's straightforward and low-cost, making it ideal for projects that don't need complex on-chain logic. The trade-off is that it relies on trusting the wallet owner to manage metadata updates and token supply correctly. 2. **Smart Contract (Plutus/Aiken):** This method uses a full smart contract to govern the Reference Token. While someone still needs to trigger updates, the smart contract validates critical aspects like supply limits and reference token to user token ratios on-chain, requiring less trust than the wallet approach. It enables complex rules for metadata updates, supply caps, and other on-chain logic, but is more complex and expensive to develop and interact with. The choice between them depends on your project's need for trustless validation versus simplicity and cost-effectiveness. If you need custom smart contract development, or are not sure which option to choose, we recommend reaching out at [\[email protected\]](https://dev.ada-anvil.io/cdn-cgi/l/email-protection#e189848d8d8ea1808580cc808f97888dcf888e) . **Forward-Compatibility with Versioning** CIP-68 uses a `version` field in the on-chain datum of the Reference Token (`[metadata, version, extra_data]`) to ensure forward-compatibility. As the standard evolves, this version number is incremented to support new features—for example, V1 introduced NFTs (`222`) and FTs (`333`), while V2 added Rich Fungible Tokens (`444`). By specifying the version at mint, your assets remain readable by older tools while enabling new functionality as it becomes available. ### [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-68#minting-a-cip-68-asset) Minting a CIP-68 Asset Minting a CIP-68 asset typically requires constructing a transaction using Anvil API's `transactions/build` endpoint that creates both the Reference and User tokens simultaneously. Smart contracts usually enforce this simultaneous minting, but the tokens can technically be minted separately. #### [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-68#prerequisites) Prerequisites Before you mint, make sure you have: 1. **Anvil API key** – Obtain one [here](https://dev.ada-anvil.io/anvil-api/authentication) to submit transactions. 2. **Three wallets** * **Customer wallet** – Pays fees and receives the NFT. Must have UTxOs to pay for fees. * **Policy wallet** – Signs the minting transaction due to the native script requirement. For secutiry reasons, make sure no UTxOs are on this address. * **Metadata-manager wallet** (or script address) – Holds the reference token and controls metadata updates. If you want to update the metadata, you need make sure you have some ADA on this address to pay for fees. Use our [wallet CLI](https://dev.ada-anvil.io/developer-tools/wallet-cli) to create them. 3. **NFT metadata** – Name, description, image URL, etc., formatted per CIP-68. 4. **Minting rules** – Define who can sign and when: * For a metadata-manager wallet, see [how to use native scripts](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts) . * For a smart contract, see [how to use smart contracts](https://dev.ada-anvil.io/guides/smart-contract) . Each guide below walks you through collecting these requirements. #### [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-68#payload-example) Payload Example Regardless of using a Metadata Manager Wallet or a Smart Contract, interacting with the API is almost the same. We need to start by [building a transaction](https://dev.ada-anvil.io/guides/transaction) . This transaction defines **what** we want to submit to the blockchain. The native script, or smart contract defines **how** the transaction is validated. **Native Script vs Smart Contract Validation** **Validation options** * **Native Script** – Script that checks: 1. **Signer** – Required key-hash 2. **Timing** – Optional timelock Ideal for straightforward mint/burn rules. See [Native Scripts](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts) . * **Smart Contract (Plutus/Aiken)** – Fully programmable logic. Verify payments, token ownership, game achievements, or any custom rule you write in Plutus or Aiken. Include a `scriptInteractions` section with validator, redeemer, and datum. See [Smart Contract Documentation](https://dev.ada-anvil.io/guides/smart-contract) . Choose based on complexity: • native scripts for quick, signature and time-lock validation rules • smart contracts for advanced, application-specific logic Copy { // Customer wallet address receive any leftover UTxOs from the transaction. "changeAddress": "addr_test1...", // UTXOs from the customer wallet paying for fees (list of CBOR UTXOs) "utxos": ["8282...", "8282..."], "mint": [\ {\ // Reference Token - Label 100 -> Sent to Metadata Manager Wallet or Script Address\ "version": "cip68",\ //Name of the asset. Must be the same for both tokens\ "assetName": { "name": "youruniquecip68asset", "format": "utf8", "label": 100 },\ "metadata": {\ "name": "Your NFT Name",\ "image": "ipfs://your-image-hash",\ "mediaType": "image/png",\ "description": "A description for your CIP-68 asset"\ },\ "policyId": "8e024681ee83f54bd5f9a0334641...",\ "quantity": 1,\ "destAddress": "addr_test2..."\ },\ {\ // User Token - Label 222 -> Sent to User Wallet\ "version": "cip68",\ // Asset name must be identical to the (100) token's assetName\ "assetName": { "name": "youruniquecip68asset", "format": "utf8", "label": 222 },\ "policyId": "8e024681ee83f54bd5f9a0334641...",\ "quantity": 1,\ "destAddress": "addr_test1..."\ }\ ], // The `preloadedScripts` array provides script data for minting authorization. // - Native scripts: contains the actual native script // - Plutus scripts: contains blueprint data (native script derived from blueprint) // Optional but recommended for performance and latency. Required for new/unregistered scripts. "preloadedScripts": [ /* ... script details ... */ ], // The `scriptInteractions` array is ONLY required for smart contract validation. // It is not used for the native script / Metadata-Manager Wallet approach. // It is ONLY required for blueprints that are not registered yet. // The full payload format is covered in the smart contract guide. "scriptInteractions": [ /* ... validator, redeemer, datum ... */ ], } ### [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-68#cip-68-guides) CIP-68 Guides [Deno & Fetch](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-68/deno-and-fetch) [PreviousPolicy Units](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts/policy-units) [NextDeno & Fetch](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-68/deno-and-fetch) Last updated 11 days ago Was this helpful? --- # Mint NFTs (CIP-25) | Anvil Development Agency [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25#introduction) Introduction --------------------------------------------------------------------------------------------- The `transactions/build` endpoint using the `mint` type allows for the creation of unique digital assets (NFTs/Native Assets) following the CIP-25 metadata standard. This guide demonstrates how to structure API requests when working with the Anvil platform to mint these assets, whether you're establishing a new collection or adding to an existing one. ### [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25#cip-25-mint-with-new-policy) CIP-25 Mint with New Policy #### [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25#prerequisites) Prerequisites Before minting a new NFT collection, ensure you have the following: 1. A valid ADA wallet address (change address) 2. A list of UTXOs (Unspent Transaction Outputs) from your wallet. See [UTXOs](https://dev.ada-anvil.io/guides/transaction/selecting-utxos) 3. A unique asset name (UTF-8 or hex format) 4. Custom metadata for your NFT (Use https://metadraft.io to generate accurate metadata easily.) 5. A native script registered on the Cardano blockchain. (See [Creating Native Scripts](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts/create-native-script) for more information.) For more information about native scripts, please see: [Native Scripts](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts) #### [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25#request-structure) Request Structure **Payload** Copy { // Specifies which address should receive any leftover ADA (change) // after the transaction is completed and fees are paid "changeAddress": "addr_test1qqwadht4defe...", // An array of UTXO strings from the wallet paying for the transaction. "utxos": ["8282...", "8282..."], // An array of assets to be minted. "mint": [\ {\ "version": "cip25",\ // UTF-8 or hex format - Must be unique within the collection.\ "assetName": { "name": "anvilapicip25", "format": "utf8" },\ \ // Custom Metadata for your NFT. Replace with your own CIP-25 standard values.\ // Use https://metadraft.io to generate accurate metadata easily.\ "metadata": {\ "name": "YOUR_NFT_NAME_HERE",\ "image": [\ "https://your-storage-provider.com/", // URL to your image\ "image-file.png" // File name of your image\ ],\ "mediaType": "image/png", // Media type of your image\ "description": "Anvil API CIP-25 Mint Example" // Description of your NFT\ },\ // The policy ID of the collection.\ "policyId": "4d5bd6249f0d9e4b2762ce334e2973dc7fd414ec1e08b4b0c2159bfb",\ "quantity": 1 // Accepts integers > 0. Negative numbers will burn assets.\ }\ ], // An array of native scripts that represent the policy that needs to be pre-loaded for validation. // This is required for the initial mint. once the policy is registered this is optional. // Including it saves resources and reduces latency by avoiding blockchain fetches. "preloadedScripts": [\ {\ "type": "simple",\ // The native script details.\ "script": {\ "type": "all",\ "scripts": [\ {\ "type": "sig",\ "keyHash": "fdf151b600df2492005221876c7d7e33056496572c7363c33a1e3609"\ },\ {\ "type": "before",\ "slot": 100000000\ }\ ]\ },\ // The hash of the native script, which is the Policy ID.\ "hash": "4d5bd6249f0d9e4b2762ce334e2973dc7fd414ec1e08b4b0c2159bfb"\ }\ ] } [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25#examples) Examples ------------------------------------------------------------------------------------- In order to reduce page content, the use cases and examples are split into a dedicated repository. * [Vanilla JavaScript CIP-25 Minting Platform](https://github.com/Cardano-Forge/anvil-api-examples/tree/main/minting-platform-cip25) * [Next.js CIP-25 Minting Platform](https://github.com/Cardano-Forge/anvil-api-examples/tree/main/nextjs-minting-platform-cip25) [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25#troubleshooting) Troubleshooting --------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25#fixes-and-best-practices) Fixes and Best Practices 1. Use the [metadata generation tool](https://metadraft.io/) to ensure your metadata follows the CIP-25 standard 2. For multiple assets under the same policy, ensure each asset name is unique 3. Double-check that your time constraints in your native script have not expired [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25#specifications) Specifications ------------------------------------------------------------------------------------------------- * [CIP-25 Specification](https://cips.cardano.org/cip/CIP-25) [PreviousDeno & Fetch](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-68/deno-and-fetch) [NextCLI Tool to Mint Assets](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25/cli-tool-to-mint-assets) Last updated 11 days ago Was this helpful? --- # Delegate to a DRep | Anvil Development Agency DRep (Delegation Representative) delegation enables users to participate in Cardano's governance system by assigning their voting power to chosen representatives. By integrating DRep delegation into your application, you empower users to influence the blockchain's future without requiring them to actively participate in every governance decision. [](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-drep#api-endpoint) API Endpoint ------------------------------------------------------------------------------------------------- **URL**: `https://preprod.api.ada-anvil.app/v2/services/transactions/build` **Method**: POST **Headers**: `Content-Type: application/json`, `x-api-key: YOUR_API_KEY` [](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-drep#request-format) Request Format ----------------------------------------------------------------------------------------------------- Copy { "changeAddress": "addr_test1...", "utxos": ["8282...", "8282..."], "delegations": [\ {\ "type": "drep",\ "address": "addr_test1...",\ "keyHash": "drep13d6sxkyz6st9h65qqrzd8ukpywhr8swe9f6357qntgjqye0gttd"\ }\ ] } [](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-drep#parameter-details) Parameter Details ----------------------------------------------------------------------------------------------------------- Parameter Description Example `type` Must be `"drep"` `"drep"` `address` User's full address `"addr_test1..."` `keyHash` DRep ID to delegate to `"drep13d6s..."` `utxos` Array of UTXOs in CBOR hex format `["8282...", "8282..."]` [](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-drep#implementation-examples) Implementation Examples ----------------------------------------------------------------------------------------------------------------------- **Using Fetch API:** [Deno & Fetch](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-drep/deno-and-fetch) **Using cURL:** [Bash & cURL](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-drep/bash-and-curl) [](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-drep#transaction-signing) Transaction Signing --------------------------------------------------------------------------------------------------------------- After building the transaction, it must be signed before submission: [https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/transaction/signing-transaction.md](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/transaction/signing-transaction.md) [](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-drep#user-interface-examples) User Interface Examples ----------------------------------------------------------------------------------------------------------------------- ![Eternl Vote Delegation](https://dev.ada-anvil.io/~gitbook/image?url=https%3A%2F%2F2837083171-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fi5SfFhLFp4rKhwQyZN9d%252Fuploads%252FohbqQ7pJ3LGjNG8G5bXu%252Fimage.png%3Falt%3Dmedia%26token%3D81beeb74-41c8-4b7f-bd19-3bf763b2ccea&width=768&dpr=4&quality=100&sign=e2546fb4&sv=2) Eternl's DRep delegation interface provides a good reference [PreviousDelegations](https://dev.ada-anvil.io/guides/delegations) [NextBash & cURL](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-drep/bash-and-curl) Last updated 16 days ago Was this helpful? --- # Delegate to a Stake Pool | Anvil Development Agency Stake pool delegation allows users to participate in Cardano's consensus protocol by delegating their stake to a pool that produces blocks. By integrating stake pool delegation into your application, you enable users to earn rewards while supporting the network's security and decentralization without requiring technical expertise in running validator nodes. [](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-pool#api-endpoint) API Endpoint ------------------------------------------------------------------------------------------------- **URL**: `https://preprod.api.ada-anvil.app/v2/services/transactions/build` **Method**: POST **Headers**: `Content-Type: application/json`, `x-api-key: YOUR_API_KEY` [](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-pool#request-format) Request Format ----------------------------------------------------------------------------------------------------- Copy { "changeAddress": "addr_test1...", "utxos": ["8282...", "8282..."], "delegations": [\ {\ "type": "pool",\ "address": "addr_test1...",\ "keyHash": "pool1z5uq..."\ }\ ] } [](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-pool#parameter-details) Parameter Details ----------------------------------------------------------------------------------------------------------- Parameter Description Example `type` Must be `"pool"` `"pool"` `address` User's full address `"addr_test1..."` `keyHash` Pool ID to delegate to `"pool1z5uq..."` `utxos` Array of UTXOs in CBOR hex format `["8282...", "8282..."]` [](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-pool#implementation-examples) Implementation Examples ----------------------------------------------------------------------------------------------------------------------- **Using Fetch API:** [Deno & Fetch](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-pool/deno-and-fetch) **Using cURL:** [Bash & cURL](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-pool/bash-and-curl) [](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-pool#transaction-signing) Transaction Signing --------------------------------------------------------------------------------------------------------------- After building the transaction, it must be signed before submission: [https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/transaction/signing-transaction.md](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/transaction/signing-transaction.md) [](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-pool#user-interface-examples) User Interface Examples ----------------------------------------------------------------------------------------------------------------------- ![Stake Pool Delegation](https://dev.ada-anvil.io/~gitbook/image?url=https%3A%2F%2F2837083171-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fi5SfFhLFp4rKhwQyZN9d%252Fuploads%252Fgit-blob-64cf4b028b3ad76a7afced568797cc089228e389%252Fstake-pool-delegation.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=6efc32d&sv=2) Eternl's Stake Pool delegation interface provides a good reference [PreviousDeno & Fetch](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-drep/deno-and-fetch) [NextBash & cURL](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-pool/bash-and-curl) Last updated 16 days ago Was this helpful? --- # Bash & cURL | Anvil Development Agency ### [](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-drep/bash-and-curl#prepare-delegation-transaction) Prepare Delegation Transaction **Payload** Copy { "changeAddress": "addr...", "delegations": [\ {\ "type": "drep",\ "address": "addr..",\ "keyHash": "drep..."\ }\ ] } **Example** Copy DREP_ID="drep13d6sxkyz6st9h65qqrzd8ukpywhr8swe9f6357qntgjqye0gttd" ADDR="addr_test1qztayr885vqrx6w0j946lvtxl622flxx4asj2z4ludm3y2rewu7hmazv8tm78tvphzlream22pp6zhk0rrsa84nf6qxsrua9nh" # See Authentication page for API key details. X_API_KEY="testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9" Copy curl -X POST \ -H "Content-Type: application/json" \ -H "X-Api-Key: ${X_API_KEY}" \ -d '{ "changeAddress": "'${ADDR}'", "delegations": [\ {\ "type": "drep",\ "address": "'${ADDR}'",\ "keyHash": "'${DREP_ID}'"\ }\ ] }' \ https://preprod.api.ada-anvil.app/v2/services/transactions/build **Output** Copy { "complete": "84a600d90102818258200dbd6bf8170148df5ed6ba8722bbd3bc69e3a825db811413c4461b947923ab830001818258390097d20ce7a3003369cf916bafb166fe94a4fcc6af61250abfe377122879773d7df44c3af7e3ad81b8be3cf76a5043a15ecf18e1d3d669d00d1a0d2fd63e021a0003e36d031a04e05acb04d901028183098200581c79773d7df44c3af7e3ad81b8be3cf76a5043a15ecf18e1d3d669d00d8200581c8b75035882d4165bea8000c4d3f2c123ae33c1d92a751a78135a24020ed9010281581c79773d7df44c3af7e3ad81b8be3cf76a5043a15ecf18e1d3d669d00da0f5f6", "stripped": "84a600d90102818258200dbd6bf8170148df5ed6ba8722bbd3bc69e3a825db811413c4461b947923ab830001818258390097d20ce7a3003369cf916bafb166fe94a4fcc6af61250abfe377122879773d7df44c3af7e3ad81b8be3cf76a5043a15ecf18e1d3d669d00d1a0d2fd63e021a0003e36d031a04e05acb04d901028183098200581c79773d7df44c3af7e3ad81b8be3cf76a5043a15ecf18e1d3d669d00d8200581c8b75035882d4165bea8000c4d3f2c123ae33c1d92a751a78135a24020ed9010281581c79773d7df44c3af7e3ad81b8be3cf76a5043a15ecf18e1d3d669d00da0f5f6", "witnessSet": "a0" } [PreviousDelegate to a DRep](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-drep) [NextDeno & Fetch](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-drep/deno-and-fetch) Last updated 3 months ago Was this helpful? --- # Deno & Fetch | Anvil Development Agency [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/node-and-fetch#introduction) Introduction ------------------------------------------------------------------------------------------------------------------------------------- This example demonstrates how to create a transaction with [CIP-20](https://cips.cardano.org/cip/CIP-20) metadata using the Anvil API. CIP-20 provides a standardized way to include human-readable messages in Cardano transactions using metadata label 674. For more information about transactions, please refer to the [Transaction Overview](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/transaction/transaction/README.md) . [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/node-and-fetch#objectives) Objectives --------------------------------------------------------------------------------------------------------------------------------- This example creates a transaction with a CIP-20 compliant message on the Preprod network. [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/node-and-fetch#requirements) Requirements ------------------------------------------------------------------------------------------------------------------------------------- * A Cardano wallet with ADA * A valid API key * Deno or Node.js environment [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/node-and-fetch#api-request-structure) API Request Structure ------------------------------------------------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/node-and-fetch#payload-format) Payload Format Copy { "changeAddress": "addr_sender...", "message": "Your message" // String or array of strings } [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/node-and-fetch#message-format-options) Message Format Options --------------------------------------------------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/node-and-fetch#single-string-message) Single String Message Copy { "changeAddress": "addr_sender...", "message": "Receipt 42" } ### [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/node-and-fetch#array-of-strings) Array of Strings Copy { "changeAddress": "addr_sender...", "message": ["Receipt 42", "Minted by Anvil"] } ### [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/node-and-fetch#auto-split-for-long-messages) Auto-split for Long Messages For messages exceeding 64 bytes, the API automatically splits them: Copy { "changeAddress": "addr_sender...", "message": "A very long message to demonstrate the auto-split feature. I need more words" } **Will be formatted as:** Copy { "msg": [\ "A very long message to demonstrate the auto-split feature. I nee",\ "d more words"\ ] } [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/node-and-fetch#implementation) Implementation ----------------------------------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/node-and-fetch#configuration-and-parameters-using-deno-and-fetch) Configuration and Parameters (using Deno and Fetch) _Using a preprod wallet for the transaction_ Copy const ADDRESS = "addr_test1qrydyk6uw6cehk5u3zspyz3dhnwzmhfls2fp42vv5dv9g2z3885pg4kpkn30ptezc855lu3w5ey93zcr5lrezjmwkftqg8xvge"; // See Authentication page for API key details. const X_API_KEY = "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9"; const API_URL = "https://preprod.api.ada-anvil.app/v2/services"; const HEADERS = { "Content-Type": "application/json", "x-api-key": X_API_KEY }; ### [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/node-and-fetch#request-body) Request Body _Body Structure for creating a transaction with CIP-20 metadata_ Copy const BODY = { changeAddress: ADDRESS, message: ["Receipt 42", "2025-01-24"], }; ### [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/node-and-fetch#api-call-using-deno-and-fetch) API Call (using Deno and Fetch) Basic POST call with Fetch cip-20.ts Copy const response = await fetch(`${API_URL}/transactions/build`, { method: "POST", headers: HEADERS, body: JSON.stringify(BODY), }); console.log(await response.json()); ### [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/node-and-fetch#deno-command) Deno Command Copy deno run --allow-net cip-20.ts ### [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/node-and-fetch#expected-output) Expected Output Copy { "hash": "b2431ba925d55f3a117b4dd7d388b21277c6f89a27bfdc3895c10cae1dfcca7f", "complete": "84a700d9010282825820557a0804947569e286b0c98d859858bb5fc0697dc8fbb86b1c5d28ff7152a2a001825820593ad1e4983410a61b092d5119f92bd9b66d4a4eb4bdaf61c8cad6f8b9dffc1c010182a300581d6001687d507bb21217905bb35686ae6b373b26ce1ebfe2bc6db1caad5f011a00155cc0028201d8184a49616e76696c2d74616782583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561b000000018d343f3e021a0003098d031a052213630758201bd3787203ca834841a6e263f524989c7a2d8d340ae67098cc9ee225e7bb5df2081a0521f7430ed9010281581c01687d507bb21217905bb35686ae6b373b26ce1ebfe2bc6db1caad5fa100d9010281825820cfd1cfc82efa73c76302b9103f81b4c1fb2e9dc0ed39ab45039fb6d4c3fbe7a35840f32d960934a6511cb120a61dd8283a60ded05026e4b9e682aa3583a3c7d6be84b0749c3e86e4f226f5aa461869b8b9dc2fca81684a872f101691eedbbcd92c0af5a11902a2a1636d7367826a526563656970742034326a323032352d30312d3234", "stripped": "84a700d9010282825820557a0804947569e286b0c98d859858bb5fc0697dc8fbb86b1c5d28ff7152a2a001825820593ad1e4983410a61b092d5119f92bd9b66d4a4eb4bdaf61c8cad6f8b9dffc1c010182a300581d6001687d507bb21217905bb35686ae6b373b26ce1ebfe2bc6db1caad5f011a00155cc0028201d8184a49616e76696c2d74616782583900c8d25b5c76b19bda9c88a0120a2dbcdc2ddd3f82921aa98ca35854285139e81456c1b4e2f0af22c1e94ff22ea648588b03a7c7914b6eb2561b000000018d343f3e021a0003098d031a052213630758201bd3787203ca834841a6e263f524989c7a2d8d340ae67098cc9ee225e7bb5df2081a0521f7430ed9010281581c01687d507bb21217905bb35686ae6b373b26ce1ebfe2bc6db1caad5fa0f5f6", "witnessSet": "a100d9010281825820cfd1cfc82efa73c76302b9103f81b4c1fb2e9dc0ed39ab45039fb6d4c3fbe7a35840f32d960934a6511cb120a61dd8283a60ded05026e4b9e682aa3583a3c7d6be84b0749c3e86e4f226f5aa461869b8b9dc2fca81684a872f101691eedbbcd92c0a", "auxiliaryData": "a11902a2a1636d7367826a526563656970742034326a323032352d30312d3234" } [](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/node-and-fetch#the-whole-file-deno-version) The Whole File (Deno Version) --------------------------------------------------------------------------------------------------------------------------------------------------------------------- cip-20.ts[](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/node-and-fetch#cip-20.ts) Copy // deno run --allow-net cip-20.ts const ADDRESS = "addr_test1qrydyk6uw6cehk5u3zspyz3dhnwzmhfls2fp42vv5dv9g2z3885pg4kpkn30ptezc855lu3w5ey93zcr5lrezjmwkftqg8xvge"; // See Authentication page for API key details. const X_API_KEY = "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9"; const API_URL = "https://preprod.api.ada-anvil.app/v2/services"; const HEADERS = { "Content-Type": "application/json", "x-api-key": X_API_KEY }; const BODY = { changeAddress: ADDRESS, message: ["Receipt 42", "2025-03-12"], }; const response = await fetch(`${API_URL}/transactions/build`, { method: "POST", headers: HEADERS, body: JSON.stringify(BODY), }); console.log(await response.json()); [PreviousBash & cURL](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/bash-and-curl) [NextMetadata](https://dev.ada-anvil.io/guides/payload) Last updated 3 months ago Was this helpful? --- # Deno & Fetch | Anvil Development Agency [](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-pool/deno-and-fetch#prepare-pool-delegation-transaction) Prepare Pool Delegation Transaction -------------------------------------------------------------------------------------------------------------------------------------------------------------- **Payload** Copy { "changeAddress": "addr_test1...", "delegations": [\ {\ "type": "pool",\ "address": "addr_test1...",\ "keyHash": "pool1z5uq..."\ }\ ] } ### [](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-pool/deno-and-fetch#example-using-deno-and-fetch) Example (using Deno & fetch) _Using a preprod wallet all value needed for a delegation_ Copy const POOL_ID = "pool1n3sjq3qvu5vvcd6aud6ndcwq7r3ghmkafcg60gznlwfrk2ucxku"; const ADDR = "addr_test1qzyttcj6czjltcs3tn3vls6yg90542lctrzwg5aagduqlfgupztxhczzmuakzfuwvrht542yrx7ll3fk29lcl2xl8axqh3pjdh"; // See Authentication page for API key details. const X_API_KEY = "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9"; #### [](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-pool/deno-and-fetch#api-post-request-body) API POST Request Body _Body Structure for a delegation to a pool using the previously collected values._ Copy const BODY = { changeAddress: ADDR, delegations: [\ {\ type: "pool",\ address: ADDR,\ keyHash: POOL_ID,\ },\ ], }; #### [](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-pool/deno-and-fetch#fetch-command-with-deno) Fetch Command with Deno POST call with Fetch Copy const response = await fetch( `https://preprod.api.ada-anvil.app/v2/services/transactions/build`, { method: "POST", headers: { "Content-Type": "application/json", "x-api-key": X_API_KEY }, body: JSON.stringify(BODY), } ); console.log(await response.json()); ### [](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-pool/deno-and-fetch#deno-command) Deno Command Copy deno run --allow-net delegate-pool.ts ### [](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-pool/deno-and-fetch#output) Output Copy { "hash": "a4633c716755a8d72d07c058ffabe94c9b8b0f8273a8d7babb1f607050fb3c3b", "complete": "84a700d90102828258203376a58b03a1a26108d79e3db28207d1e3b7db51266bb9d07691b3d724d8198201825820f4bf2eb3572ec503081a24baf506ee26f5a2ee8d5d6c707830b75516db9bb9ed020182a300581d60355efb09c4a29c7e3b63ead47a220efcbbdef1fd5369c4efff947b4b011a001dc130028201d8184a49616e76696c2d7461678258390088b5e25ac0a5f5e2115ce2cfc344415f4aabf858c4e453bd43780fa51c08966be042df3b61278e60eeba554419bdffc536517f8fa8df3f4c1a38227e99021a00032095031a053f215904d901028282008200581c1c08966be042df3b61278e60eeba554419bdffc536517f8fa8df3f4c83028200581c1c08966be042df3b61278e60eeba554419bdffc536517f8fa8df3f4c581c9c6120440ce518cc375de37536e1c0f0e28beedd4e11a7a053fb923b081a053f05390ed9010281581c355efb09c4a29c7e3b63ead47a220efcbbdef1fd5369c4efff947b4ba100d90102818258204bc7618201307ab45cdb3793107822c47230f097e9771fb358473cdb82b2e644584020ecc8e8a0b6d2f4a2bd51d388ee20abebf4982d55ce738d9b1c223bc77152fa175c15729e96073873d262017ea113ff41a8ec26a16441029f00fbf004229b07f5f6", "stripped": "84a700d90102828258203376a58b03a1a26108d79e3db28207d1e3b7db51266bb9d07691b3d724d8198201825820f4bf2eb3572ec503081a24baf506ee26f5a2ee8d5d6c707830b75516db9bb9ed020182a300581d60355efb09c4a29c7e3b63ead47a220efcbbdef1fd5369c4efff947b4b011a001dc130028201d8184a49616e76696c2d7461678258390088b5e25ac0a5f5e2115ce2cfc344415f4aabf858c4e453bd43780fa51c08966be042df3b61278e60eeba554419bdffc536517f8fa8df3f4c1a38227e99021a00032095031a053f215904d901028282008200581c1c08966be042df3b61278e60eeba554419bdffc536517f8fa8df3f4c83028200581c1c08966be042df3b61278e60eeba554419bdffc536517f8fa8df3f4c581c9c6120440ce518cc375de37536e1c0f0e28beedd4e11a7a053fb923b081a053f05390ed9010281581c355efb09c4a29c7e3b63ead47a220efcbbdef1fd5369c4efff947b4ba0f5f6", "witnessSet": "a100d90102818258204bc7618201307ab45cdb3793107822c47230f097e9771fb358473cdb82b2e644584020ecc8e8a0b6d2f4a2bd51d388ee20abebf4982d55ce738d9b1c223bc77152fa175c15729e96073873d262017ea113ff41a8ec26a16441029f00fbf004229b07" } [](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-pool/deno-and-fetch#the-whole-file-deno-version) The Whole File (Deno Version) ------------------------------------------------------------------------------------------------------------------------------------------------ delegate-pool.ts[](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-pool/deno-and-fetch#delegate-pool.ts) Copy const POOL_ID = "pool1n3sjq3qvu5vvcd6aud6ndcwq7r3ghmkafcg60gznlwfrk2ucxku"; const ADDR = "addr_test1qzj4p30l8kzrm95wmfuqh4spnvpvyjt4lyc8xj2mtl2xtrsqma6nh06158nvt3w9gf2xyl35h5csz5awufc6xtvpdhszkvcml"; // See Authentication page for API key details. const X_API_KEY = "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9"; const BODY = { changeAddress: ADDR, delegations: [\ {\ type: "pool",\ address: ADDR,\ keyHash: POOL_ID,\ },\ ], }; const response = await fetch( `https://preprod.api.ada-anvil.app/v2/services/transactions/build`, { method: "POST", headers: { "Content-Type": "application/json", "x-api-key": X_API_KEY }, body: JSON.stringify(BODY), } ); console.log(await response.json()); export {}; [PreviousBash & cURL](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-pool/bash-and-curl) [NextSmart Contracts](https://dev.ada-anvil.io/guides/smart-contract) Last updated 3 months ago Was this helpful? --- # Deno & Fetch | Anvil Development Agency [](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-drep/deno-and-fetch#prepare-delegation-transaction) Prepare Delegation Transaction ---------------------------------------------------------------------------------------------------------------------------------------------------- **Payload** Copy { "changeAddress": "addr...", "delegations": [\ {\ "type": "drep",\ "address": "addr..",\ "keyHash": "drep..."\ }\ ] } ### [](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-drep/deno-and-fetch#example-using-deno-and-fetch) Example (using Deno & fetch) _Using a preprod wallet all value needed for a delegation_ Copy const DREP_ID = "drep13d6sxkyz6st9h65qqrzd8ukpywhr8swe9f6357qntgjqye0gttd"; const ADDR = "addr_test1qq7fc3ke49nkcsfglltut7apa9t3gdul4utwhxt6j2hdrw7pg4vk6erdshyhdj5xeq0vh8qdy34cpdfstvc8l9su8hgq679eew"; // See Authentication page for API key details. const X_API_KEY = "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9"; #### [](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-drep/deno-and-fetch#api-post-request-body) API POST Request Body _Body Structure for a delegation to a DRep using the previously collected values._ Copy const BODY = { changeAddress: ADDR, delegations: [\ {\ type: "drep",\ address: ADDR,\ keyHash: DREP_ID,\ },\ ], }; #### [](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-drep/deno-and-fetch#fetch-command-with-deno) Fetch Command with Deno POST call with Fetch Copy const response = await fetch( `https://preprod.api.ada-anvil.app/v2/services/transactions/build`, { method: "POST", headers: { "Content-Type": "application/json", "x-api-key": X_API_KEY }, body: JSON.stringify(BODY), } ); console.log(await response.json()); ### [](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-drep/deno-and-fetch#deno-command) Deno Command Copy deno run --allow-net delegate-drep.ts ### [](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-drep/deno-and-fetch#output) Output Copy { "hash": "309afc6f9e06e70a0431eb929f4bacdfff463a0879c5d7daaef3357a4e6dbf64", "complete": "84a600d9010281825820f115e0c125b4023379833df656b5a5535d715f3eabb31e5bd5697d2604866b46000181825839003c9c46d9a9676c4128ffd7c5fba1e95714379faf16eb997a92aed1bbc145596d646d85c976ca86c81ecb9c0d246b80b5305b307f961c3dd0821a0ea25013a1581c360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13a1581b616e76696c61706963697032355f3137333838363236313738393001021a0003f419031a04f5649904d901028282008200581cc145596d646d85c976ca86c81ecb9c0d246b80b5305b307f961c3dd083098200581cc145596d646d85c976ca86c81ecb9c0d246b80b5305b307f961c3dd08200581c8b75035882d4165bea8000c4d3f2c123ae33c1d92a751a78135a24020ed9010281581cc145596d646d85c976ca86c81ecb9c0d246b80b5305b307f961c3dd0a0f5f6", "stripped": "84a600d9010281825820f115e0c125b4023379833df656b5a5535d715f3eabb31e5bd5697d2604866b46000181825839003c9c46d9a9676c4128ffd7c5fba1e95714379faf16eb997a92aed1bbc145596d646d85c976ca86c81ecb9c0d246b80b5305b307f961c3dd0821a0ea25013a1581c360fd38656e5204f22ec058d18d5a90c18745ca8325e51d077c38a13a1581b616e76696c61706963697032355f3137333838363236313738393001021a0003f419031a04f5649904d901028282008200581cc145596d646d85c976ca86c81ecb9c0d246b80b5305b307f961c3dd083098200581cc145596d646d85c976ca86c81ecb9c0d246b80b5305b307f961c3dd08200581c8b75035882d4165bea8000c4d3f2c123ae33c1d92a751a78135a24020ed9010281581cc145596d646d85c976ca86c81ecb9c0d246b80b5305b307f961c3dd0a0f5f6", "witnessSet": "a0" } [](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-drep/deno-and-fetch#the-whole-file-deno-version) The Whole File (Deno Version) ------------------------------------------------------------------------------------------------------------------------------------------------ delegate-drep.ts[](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-drep/deno-and-fetch#delegate-drep.ts) Copy const DREP_ID = "drep13d6sxkyz6st9h65qqrzd8ukpywhr8swe9f6357qntgjqye0gttd"; const ADDR = "addr_test1qq7fc3ke49nkcsfglltut7apa9t3gdul4utwhxt6j2hdrw7pg4vk6erdshyhdj5xeq0vh8qdy34cpdfstvc8l9su8hgq679eew"; // See Authentication page for API key details. const X_API_KEY = "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9"; const BODY = { changeAddress: ADDR, delegations: [\ {\ type: "drep",\ address: ADDR,\ keyHash: DREP_ID,\ },\ ], }; const response = await fetch( `https://preprod.api.ada-anvil.app/v2/services/transactions/build`, { method: "POST", headers: { "Content-Type": "application/json", "x-api-key": X_API_KEY }, body: JSON.stringify(BODY), } ); console.log(await response.json()); export {}; [PreviousBash & cURL](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-drep/bash-and-curl) [NextDelegate to a Stake Pool](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-pool) Last updated 3 months ago Was this helpful? --- # Profile Information | Anvil Development Agency [Profile Listings](https://dev.ada-anvil.io/wayup/profile-information/get-profile-listings) [Profile Offers](https://dev.ada-anvil.io/wayup/profile-information/get-profile-offers) [PreviousSubmit Transaction](https://dev.ada-anvil.io/wayup/submit-tx) [NextProfile Listings](https://dev.ada-anvil.io/wayup/profile-information/get-profile-listings) Last updated 1 month ago Was this helpful? --- # Part 3: Building Transactions | Anvil Development Agency [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-transactions#introduction) Introduction ------------------------------------------------------------------------------------------------------------------------------------------------------ Now for the best part: With our wallet integration in place, we're now ready to implement the transaction functionality. This involves creating API endpoints for transaction building and submission, as well as building the user interface components for transaction input and status feedback. [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-transactions#transaction-flow-overview) Transaction Flow Overview -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- For a comprehensive explanation of Cardano transaction concepts, please refer to our [Transaction Guide](https://dev.ada-anvil.io/guides/transaction) . In this tutorial, we'll focus on implementing the following streamlined transaction flow: 1. **Get Wallet Data**: Retrieve the user's wallet address and UTXOs 2. **Build Transaction**: Call Anvil API to construct the transaction 3. **Sign Transaction**: Use the connected wallet to sign the transaction 4. **Submit Transaction**: Send the signed transaction to the Cardano network 5. **Display Result**: Show the transaction ID and success/error feedback [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-transactions#implementation-steps) Implementation Steps ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-transactions#id-1.-create-anvil-api-utility-functions) 1\. Create Anvil API Utility Functions Instead of calling the Anvil API directly, we'll create utility functions that offer several advantages including type safety, error handling, and secure API key management through Next.js server components. Our implementation will include three key functions: * **callAnvilApi**: A generic request handler with proper error management * **buildTransaction**: Creates transactions with sender addresses and payment details * **submitTransaction**: Sends signed transactions to the Cardano network Let's implement these utilities: Copy // src/utils/anvil-api.ts "use server"; const BASE_URL = process.env.NEXT_PUBLIC_ANVIL_API_URL; interface AnvilApiConfig> { endpoint: string; method?: "GET" | "POST"; body?: T; apiKey?: string; } export async function callAnvilApi>({ endpoint, method = "POST", body, apiKey, }: AnvilApiConfig): Promise { if (!BASE_URL) { throw new Error("Anvil API base URL is not configured"); } const key = apiKey || process.env.ANVIL_API_KEY; if (!key) { throw new Error("API key is required for Anvil API"); } const headers = { "Content-Type": "application/json", "X-Api-Key": key, }; try { const response = await fetch(`${BASE_URL}/${endpoint}`, { method, headers, body: body ? JSON.stringify(body) : undefined, }); if (!response.ok) { const errorData = await response .json() .catch(() => ({ message: "Unknown error" })); throw new Error( `Anvil API Error (${response.status}): ${errorData.message || "Unknown error"}`, ); } return (await response.json()) as T; } catch (error) { console.error("Anvil API request failed:", error); throw error; } } export interface BuildTransactionParams { changeAddress: string; utxos: string[]; outputs: { address: string; lovelace: number; assets?: Record; }[]; } export interface TransactionBuildResult { hash: string; complete: string; // CBOR stripped: string; // CBOR witnessSet: string; // CBOR } export async function buildTransaction( params: BuildTransactionParams, ): Promise { return callAnvilApi({ endpoint: "transactions/build", body: params, }); } export interface SubmitTransactionParams { transaction: string; // CBOR signatures?: string[]; // CBOR } export interface TransactionSubmitResult { txHash: string; } export async function submitTransaction( params: SubmitTransactionParams, ): Promise { return callAnvilApi({ endpoint: "transactions/submit", body: params, }); } ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-transactions#id-2.-create-api-endpoints-for-transaction-building-and-submission) 2\. Create API Endpoints for Transaction Building and Submission Next.js offers a built-in API routes system through its App Router architecture that allows us to create serverless functions. For this application, we'll create two API endpoints to handle transaction building and submission. See the [Next.js Route Handlers documentation](https://nextjs.org/docs/app/building-your-application/routing/route-handlers) for more details on how these API routes work. #### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-transactions#transaction-building-endpoint) Transaction Building Endpoint First, let's create the endpoint that builds a transaction with our Anvil API utility: Copy // src/app/api/transaction/build/route.ts import { NextRequest, NextResponse } from 'next/server'; import { buildTransaction, BuildTransactionParams } from '@/utils/anvil-api'; /** * Build a Cardano transaction using Anvil API * * This endpoint builds a transaction with the provided inputs and outputs * It requires: * - changeAddress: The sender's address for change * - utxos: Array of UTXOs from the wallet * - outputs: Where to send the ADA/assets with amounts */ export async function POST(request: NextRequest) { try { const body = await request.json(); const { changeAddress, utxos, outputs } = body as BuildTransactionParams; if (!changeAddress || !utxos || !outputs) { return NextResponse.json( { error: "Missing required parameters" }, { status: 400 } ); } // Call utility function to build the transaction const transactionData = await buildTransaction({ changeAddress, utxos, outputs }); // Return the transaction data for signing return NextResponse.json({ hash: transactionData.hash, complete: transactionData.complete, stripped: transactionData.stripped, witnessSet: transactionData.witnessSet }); } catch (error) { console.error("Error building transaction:", error); return NextResponse.json( { error: error instanceof Error ? error.message : "Failed to build transaction" }, { status: 500 } ); } } #### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-transactions#transaction-submission-endpoint) Transaction Submission Endpoint Next, let's create the endpoint that submits a signed transaction to the Cardano network: Copy // src/app/api/transaction/submit/route.ts import { NextRequest, NextResponse } from "next/server"; import { submitTransaction, SubmitTransactionParams } from "@/utils/anvil-api"; /** * Submit a signed Cardano transaction to the blockchain using Anvil API * * This endpoint accepts: * - transaction: The transaction CBOR (can be unsigned) * - signatures: Optional array of signatures from the wallet */ export async function POST(request: NextRequest) { try { const body = await request.json(); const { transaction, signatures } = body as SubmitTransactionParams; if (!transaction) { return NextResponse.json( { error: "Missing transaction" }, { status: 400 }, ); } // Call utility function to submit the transaction const submissionData = await submitTransaction({ transaction, signatures }); // Return the transaction hash and success message return NextResponse.json({ hash: submissionData.txHash, message: "Transaction submitted successfully", }); } catch (error) { console.error("Error submitting transaction:", error); return NextResponse.json( { error: error instanceof Error ? error.message : "Failed to submit transaction", }, { status: 500 }, ); } } ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-transactions#id-3.-create-a-custom-hook-for-transaction-management) 3\. Create a Custom Hook for Transaction Management To cleanly manage our transaction logic, we'll create a reusable custom hook that: * Tracks the entire transaction lifecycle with multiple status states * Manages API calls to our transaction endpoints * Handles wallet integration for signing * Provides consistent error handling and user feedback * Exposes a simple interface for components to use This pattern keeps our UI components focused on presentation rather than complex transaction logic. Copy // src/hooks/useTransactionSubmission.ts "use client"; import { useState, useCallback } from "react"; import { useWallet } from "@ada-anvil/weld/react"; // Transaction states for managing the flow export type TransactionStatus = | "idle" | "building" | "signing" | "submitting" | "success" | "error"; export interface TransactionParams { recipient: string; ada: number; } export function useTransactionSubmission() { const [status, setStatus] = useState("idle"); const [txHash, setTxHash] = useState(null); const [error, setError] = useState(null); const wallet = useWallet(); // Use Weld's wallet hook const submitTransaction = useCallback( async ({ recipient, ada }: TransactionParams) => { // Convert ADA to lovelace (1 ADA = 1,000,000 lovelace) const lovelace = Math.floor(ada * 1_000_000); try { // Reset states setError(null); setTxHash(null); setStatus("building"); // Validate wallet connection if ( !wallet.isConnected || !wallet.changeAddressBech32 || !wallet.handler ) { throw new Error("Please connect your wallet first"); } // Step 1: Build transaction const buildResponse = await fetch("/api/transaction/build", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ changeAddress: wallet.changeAddressBech32, // Sender's address from Weld utxos: await wallet.handler.getUtxos(), // Get available UTXOs from Weld outputs: [{ address: recipient, lovelace }], }), }); if (!buildResponse.ok) { const errorData = await buildResponse.json(); throw new Error(errorData.error || "Failed to build transaction"); } const buildData = await buildResponse.json(); // Step 2: Sign transaction setStatus("signing"); const signature = await wallet.handler.signTx(buildData.complete); if (!signature) { throw new Error("Failed to sign transaction"); } // Step 3: Submit transaction setStatus("submitting"); const submitResponse = await fetch("/api/transaction/submit", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ transaction: buildData.complete, signatures: [signature], }), }); if (!submitResponse.ok) { const errorData = await submitResponse.json(); throw new Error(errorData.error || "Failed to submit transaction"); } const submitData = await submitResponse.json(); setTxHash(submitData.hash); setStatus("success"); return submitData.hash; } catch (err) { setError(err instanceof Error ? err.message : "Unknown error"); setStatus("error"); return null; } }, [wallet], ); const reset = useCallback(() => { setStatus("idle"); setTxHash(null); setError(null); }, []); return { status, txHash, error, submitTransaction, reset, isProcessing: status !== "idle" && status !== "error" && status !== "success", }; } ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-transactions#id-4.-create-the-transaction-form-component) 4\. Create the Transaction Form Component Now, let's create the UI component for transactions. This component will: * Provide inputs for recipient address and ADA amount * Display transaction status and results * Handle form validation and submission * Show a link to a blockchain explorer after successful transactions The form integrates with our custom hook from the previous step to manage transaction state and processing. It also demonstrates proper error handling and user feedback throughout the transaction flow. Copy // src/components/TransactionForm.tsx "use client"; import { useState, useCallback } from "react"; import { useWallet } from "@ada-anvil/weld/react"; import { useTransactionSubmission } from "@/hooks/useTransactionSubmission"; const NETWORK = process.env.NEXT_PUBLIC_NETWORK || "preprod"; const getExplorerUrl = (txHash: string) => { return NETWORK === "mainnet" ? `https://cexplorer.io/tx/${txHash}` : `https://${NETWORK}.cexplorer.io/tx/${txHash}`; }; function getButtonText(status: string, adaAmount: number): string { if (status === "building") return "Building..."; if (status === "signing") return "Signing..."; if (status === "submitting") return "Submitting..."; if (adaAmount === 0) return "Send ADA"; return `Send ${adaAmount} ADA`; } export default function TransactionForm() { // Form state const [recipient, setRecipient] = useState(""); const [adaAmount, setAdaAmount] = useState(0); // Use our custom hook for transaction management const { status, txHash, error, submitTransaction, reset: resetTransaction, } = useTransactionSubmission(); // Get wallet information from the Weld hook const wallet = useWallet(); // Handle the transaction submission process const handleSendTransaction = useCallback( async (e: React.FormEvent) => { e.preventDefault(); await submitTransaction({ recipient, ada: adaAmount }); }, [submitTransaction, recipient, adaAmount], ); // Render success state if transaction completed if (status === "success" && txHash) { return (

Transaction Successful

Transaction ID:
{txHash}
); } const isFormDisabled = !wallet.isConnected || (status !== "idle" && status !== "error"); // Render transaction form return (

Send Transaction

{/* Wallet connection status */} {!wallet.isConnected && (
Please connect your wallet first.
)} {/* Recipient address input */}
setRecipient(e.target.value)} type="text" placeholder="addr..." required disabled={isFormDisabled} />
{/* ADA amount input */}
{ const value = parseFloat(e.target.value); setAdaAmount(isNaN(value) ? 0 : value); }} type="number" min="1" step="1" placeholder="Amount in ADA" required disabled={isFormDisabled} />
{/* Submit button */} {error && (
Error: {error}
)}
); } ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-transactions#id-5.-update-the-home-page-to-include-the-transaction-form) 5\. Update the Home Page to Include the Transaction Form Finally, let's update the home page to include both the wallet connector and transaction form: Copy // src/app/page.tsx import WalletConnector from "@/components/WalletConnector"; import TransactionForm from "@/components/TransactionForm"; export default function Home() { return (

Cardano Transaction App

{/* Add the TransactionForm component below */}
); } [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-transactions#testing-your-transaction-flow) Testing Your Transaction Flow ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 1. **Start your development server**: Copy npm run dev 1. **Navigate to your application** (usually at http://localhost:3000) 2. **Connect your wallet** following the instructions from Part 2 3. **Test the transaction flow**: * Enter a valid recipient address (use a testnet address for testing) * Enter a small amount of ADA (e.g., 1-2 ADA) * Click the "Send" button * Observe the transaction states: Building → Signing → Submitting → Success * Verify the transaction ID appears and links to a block explorer Make sure you're using a testnet wallet with testnet ADA for testing. Never use real ADA or mainnet addresses during development and testing. [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-transactions#troubleshooting) Troubleshooting ------------------------------------------------------------------------------------------------------------------------------------------------------------ ### [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-transactions#common-transaction-errors) Common Transaction Errors 1. **Insufficient Funds** * Ensure your wallet has enough testnet ADA for the transaction and fees * Try sending a smaller amount 2. **Invalid Address** * Verify you're using a valid Cardano address format * Check that you're using a testnet address when testing on testnet 3. **Signature Failure** * Make sure your wallet is unlocked * Try reconnecting your wallet if signing fails * Check that your wallet is enabled for dApp interactions 4. **Network Issues** * Check your internet connection * Verify the Anvil API is available and responding (Use `/health` endpoint) [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-transactions#deployment-considerations) Deployment Considerations -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- When deploying your application to production: 1. **Environment Variables**: Ensure your production environment has the correct API URLs and keys 2. **Network Selection**: Update the `NEXT_PUBLIC_NETWORK` to `mainnet` for production use 3. **Error Handling**: Implement more robust error handling and user feedback 4. **Security**: Ensure your API keys are properly secured and not exposed to clients [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-transactions#conclusion) Conclusion -------------------------------------------------------------------------------------------------------------------------------------------------- Congratulations! You've successfully built a complete Cardano transaction application using Next.js and Weld. Your application can now: * Connect to Cardano wallets * Build and sign transactions * Submit transactions to the Cardano network * Display transaction results ![Completed Cardano Transaction App with Next.js and Weld](https://dev.ada-anvil.io/~gitbook/image?url=https%3A%2F%2F2837083171-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fi5SfFhLFp4rKhwQyZN9d%252Fuploads%252Fgit-blob-1fee610f3235816585f06f64dd19b02913e7ccd7%252Fnextjs-basic-transaction-finished.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=f1d4cbc1&sv=2) Your completed Cardano transaction application You've completed all three parts of this guide! You now have a working Cardano transaction application that demonstrates the core functionality needed for Cardano dApp development. [](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-transactions#next-steps) Next Steps -------------------------------------------------------------------------------------------------------------------------------------------------- To further enhance your application, consider exploring: * [Creating Custom Transactions](https://github.com/Cardano-Forge/anvil-api/blob/main/guides/transaction/create-custom-transaction/README.md) for more complex use cases * [Adding Transaction Metadata](https://github.com/Cardano-Forge/anvil-api/blob/main/guides/transaction/create-transaction-with-metadata-cip-20/README.md) following CIP-20 * [NFT & FT Minting](https://github.com/Cardano-Forge/anvil-api/blob/main/guides/nft-and-ft/README.md) to add token functionality * [Smart Contract Integration](https://github.com/Cardano-Forge/anvil-api/blob/main/guides/smart-contract/README.md) for advanced use cases [PreviousPart 2: Wallet Integration](https://dev.ada-anvil.io/guides/transaction/create-basic-transaction/nextjs-with-weld/nextjs-with-weld-wallet) [NextSelect UTXOs](https://dev.ada-anvil.io/guides/transaction/selecting-utxos) Last updated 4 months ago Was this helpful? --- # Creating Native Scripts | Anvil Development Agency [](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts/create-native-script#introduction) Introduction ----------------------------------------------------------------------------------------------------------------- This guide provides step-by-step instructions for creating native scripts using the Anvil API's utility endpoints. For conceptual information about native scripts, their structure, and common patterns, see the [Native Scripts Overview](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts) . [](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts/create-native-script#workflow-overview) Workflow Overview --------------------------------------------------------------------------------------------------------------------------- The process of creating and using native scripts with the Anvil API follows these key steps: 1. Parse Wallet Address - obtain key hash(es) for signature requirements (supports multisig with multiple addresses) 2. Convert Date to Slot - create a time constraint (optional but recommended) 3. Create JSON Native Script - combine key hash(s) and time constraints 4. Convert to Native Script and get policy ID - use the Anvil API to create a machine-readable format 5. Use in Transaction - include the script when minting tokens [](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts/create-native-script#prerequisites) Prerequisites ------------------------------------------------------------------------------------------------------------------- * An Anvil API key * A wallet address for signing transactions * Basic understanding of [native scripts](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts) and their role in minting tokens [](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts/create-native-script#using-the-utils-endpoints) Using the Utils Endpoints ------------------------------------------------------------------------------------------------------------------------------------------- The Anvil API provides several utility endpoints to assist with creating and managing native scripts: Endpoint Description `/utils/addresses/parse` Parses an address and returns its payment and stake credentials `/utils/native-scripts/parse` Returns information about a provided native script (e.g., policy ID) `/utils/native-scripts/serialize` Creates a native script from it's JSON schema definition `/utils/network/time-to-slot` Converts a date/time to a Cardano slot number `/utils/network/slot-to-time` Converts a Cardano slot number to a date/time [](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts/create-native-script#step-by-step-guide-to-creating-a-native-script-for-minting-rules) Step-by-Step Guide to Creating a Native Script for Minting rules. -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts/create-native-script#step-1-obtain-a-key-used-for-policy-actions) Step 1: Obtain a key used for policy actions. To create a signature-based native script, you first need to obtain the key hash of the wallet that will sign the minting transactions. You can get this by parsing a wallet address using the address parsing endpoint: Copy // Example request await fetch('https://preprod.api.ada-anvil.app/v2/services/utils/addresses/parse', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Api-Key': 'YOUR_API_KEY' }, body: JSON.stringify({ address: 'stake_address_or_payment_address_here' }) }); // Example response { "payment": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", "stake": "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210" } The `payment` value from the response is what you'll use as the `keyHash` in your native script. ### [](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts/create-native-script#step-2-convert-date-to-slot-optional) Step 2: Convert Date to Slot (Optional) To add a time constraint to your native script, convert a future date to a Cardano slot number: Copy // Example request await fetch('https://preprod.api.ada-anvil.app/v2/services/utils/network/time-to-slot', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Api-Key': 'YOUR_API_KEY' }, body: JSON.stringify({ timestamp: '2030-01-01T00:00:00Z' }) }); // Example response { "slot": 98765432 } This slot number will be used in your native script to create a time-limited minting policy. ### [](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts/create-native-script#step-3-create-json-native-script) Step 3: Create JSON Native Script Now that you have both the key hash (from Step 1) and optionally a slot number for time constraint (from Step 2), you can create the JSON structure for your native script. Native scripts in the Anvil API follow a specific JSON structure that maps to Cardano's native script format. Each script uses a discriminated union with a `type` field that determines the script's behavior. Here's how to combine the signature requirement and time constraint into a complete native script: Copy { "type": "all", "scripts": [\ {\ "type": "sig",\ // Required Key hash from Step 1\ "keyHash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"\ },\ {\ "type": "before",\ // Required Slot number from Step 2\ "slot": 98765432\ }\ ] } This JSON structure represents a native script that requires both the signature from the specified key hash AND the transaction to be submitted before the specified slot number. ### [](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts/create-native-script#step-4-serialize-the-native-script-via-api) Step 4: Serialize the Native Script via API While the JSON representation is a clear way to define the native script, it must be serialized into a specific hex format to be included in a transaction. The Anvil API provides a utility endpoint to handle this conversion. Here's how you can call the API with the JSON native script from Step 3: Copy // The JSON native script from Step 3 const nativeScriptJSON = { type: "all", scripts: [\ {\ type: "sig",\ // Required Key hash from Step 1\ keyHash: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"\ },\ {\ type: "before",\ // Required Slot number from Step 2\ slot: 98765432\ }\ ] }; async function serializeNativeScript(nativeScriptJSON) { const response = await fetch("https://preprod.api.ada-anvil.app/v2/services/utils/native-scripts/serialize", { method: "POST", headers: { "Content-Type": "application/json", "X-Api-Key": "YOUR_API_KEY" }, body: JSON.stringify({ schema: nativeScriptJSON }), }); if (!response.ok) { const errorText = await response.text(); throw new Error(`API call failed: ${errorText}`); } const { script, policyId } = await response.json(); console.log("CBOR Hex-encoded Script:", script); console.log("Policy ID:", policyId); return { script, policyId }; } // Get the serialized script and policy ID const { script: hexEncodedScript, policyId } = await serializeNativeScript(nativeScriptJSON); The API returns two crucial pieces of information: 1. `**script**`: The CBOR hex-encoded native script. This is what you will include in the transaction body. 2. `**policyId**`: The unique identifier for your native script, derived from hashing the script. ### [](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts/create-native-script#step-5-use-in-transaction) Step 5: Use in Transaction The final step is to use your native script when building transactions that mint or burn assets. You'll include the native script in the `preloadedScripts` section of your transaction request: Copy const data = { changeAddress: "addr_test...", utxos: ["8282...", "8282..."], mint: [\ {\ version: "cip25",\ assetName: { name: "MyAsset", format: "utf8" },\ metadata: {\ name: "My Asset",\ image: ["ipfs://Qm..."],\ // Additional metadata fields\ },\ policyId, // The policy ID from Step 4\ quantity: 1,\ \ // Destination address for the minted asset\ destAddress: "addr_test..."\ }\ ], // This is required for the first mint transaction. Anvil API will fetch the scripts from the provided policy ID for any subsequent transactions. preloadedScripts: [\ {\ type: "simple",\ script: {\ // Your native script from Step 3\ type: "all",\ scripts: [\ {\ type: "sig",\ keyHash: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"\ },\ {\ type: "before",\ slot: 98765432\ }\ ]\ },\ hash: policyId // The policy ID from Step 4\ }\ ] }; const tx = await fetch(`https://preprod.api.ada-anvil.app/v2/services/transactions/build`, { method: "POST", body: JSON.stringify(data), headers: { "Content-Type": "application/json", "X-Api-Key": "YOUR_API_KEY" } }); This transaction, when signed and submitted, will mint a new asset under the policy ID you generated. [](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts/create-native-script#additional-native-script-patterns) Additional Native Script Patterns ----------------------------------------------------------------------------------------------------------------------------------------------------------- For more native script patterns including multi-signature policies, threshold requirements, and complex logical combinations, see the [Native Scripts Overview](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts#common-native-script-patterns) . [](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts/create-native-script#conclusion) Conclusion ------------------------------------------------------------------------------------------------------------- You now have a complete native script that can be used for minting tokens. The script includes both signature requirements and time constraints, providing security and immutability for your token collection. For security considerations, best practices, and additional script patterns, refer to the [Native Scripts Overview](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts) . [](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts/create-native-script#related-resources) Related Resources --------------------------------------------------------------------------------------------------------------------------- * [Native Scripts](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts) * [Minting a CIP-25 NFT](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25) * [Minting a CIP-68 NFT](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-68) [PreviousNative Scripts](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts) [NextPolicy Units](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts/policy-units) Last updated 11 days ago Was this helpful? --- # Smart Contracts | Anvil Development Agency [](https://dev.ada-anvil.io/guides/smart-contract#introduction) Introduction --------------------------------------------------------------------------------- The Anvil API provides a comprehensive suite of tools for working with Cardano smart contracts, offering a streamlined experience for developers to deploy, interact with, and manage smart contracts without dealing with the underlying complexity of the Cardano blockchain. > **Prerequisites**: This guide assumes familiarity with basic Cardano concepts and smart contract development principles. If you're new to Cardano smart contracts, we recommend reviewing [Aiken Fundamentals - EUTxO](https://aiken-lang.org/fundamentals/eutxo) > before proceeding. [](https://dev.ada-anvil.io/guides/smart-contract#where-anvil-fits-in-the-cardano-smart-contract-ecosystem) Where Anvil Fits in the Cardano Smart Contract Ecosystem ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Smart contract development and usage follows a lifecycle from code to on-chain interaction. The diagram below illustrates where Anvil API fits in this process: ![Smart Contract Lifecycle](https://dev.ada-anvil.io/~gitbook/image?url=https%3A%2F%2F2837083171-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fi5SfFhLFp4rKhwQyZN9d%252Fuploads%252Fgit-blob-d6761ade89014d7ac7e54ed88490c3f789fc0bed%252Fsmart-contract-flow.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=e41c733f&sv=2) Smart Contract Lifecycle As shown in the diagram, Anvil API comes into play after you've written and compiled your smart contract code with Aiken. Our tools focus on: * **Blueprint Management**: Storing and managing your compiled smart contract blueprints * **Deployment**: Saving your contracts to the Cardano blockchain * **Transaction Building**: Creating the simple or complex transactions needed to interact with contracts * **Script Execution**: Handling the validation and execution logic upon submit This clear separation of concerns allows you to focus on writing high-quality contract logic while Anvil handles the complexities of blockchain interaction. [](https://dev.ada-anvil.io/guides/smart-contract#cip-57-blueprint-format) CIP-57 Blueprint Format ------------------------------------------------------------------------------------------------------- The Anvil API works with [CIP-57 Plutus Blueprint](https://cips.cardano.org/cip/CIP-57) format, which provides a standardized way to describe Cardano smart contracts. ### [](https://dev.ada-anvil.io/guides/smart-contract#blueprint-generation-with-aiken) Blueprint Generation with Aiken Aiken is the recommended smart contract language for Anvil API. It generates CIP-57 compliant blueprints automatically when you build your project: Copy # Generate CIP-57 blueprint file with verbose output for debugging aiken build --verbose The build process creates a `plutus.json` file in your project's `build` directory, which is a complete CIP-57 blueprint that can be directly used with the Anvil API. Using the `--verbose` flag will provide detailed compilation information and help with debugging. Once you have your blueprint file, you can use the Anvil API to manage and interact with your smart contracts without having to deal with the low-level details of the Cardano blockchain. [](https://dev.ada-anvil.io/guides/smart-contract#working-with-smart-contracts) Working with Smart Contracts ----------------------------------------------------------------------------------------------------------------- Once you have your blueprint file, you can use the Anvil API to manage and interact with your smart contracts. The Anvil API provides several ways to work with Cardano smart contracts: ### [](https://dev.ada-anvil.io/guides/smart-contract#validator-types-and-script-purposes) Validator Types and Script Purposes Cardano smart contracts use validators to define the conditions under which certain operations can occur. Understanding validator types is essential for implementing effective smart contracts. [Validators](https://dev.ada-anvil.io/guides/smart-contract/validators) ### [](https://dev.ada-anvil.io/guides/smart-contract#blueprint-management) Blueprint Management Learn how to store, retrieve, update, and delete your CIP-57 blueprints using the Anvil API. [Blueprint Management (CIP-57)](https://dev.ada-anvil.io/guides/smart-contract/blueprint-management) [](https://dev.ada-anvil.io/guides/smart-contract#example-smart-contract-interactions) Example Smart Contract Interactions ------------------------------------------------------------------------------------------------------------------------------- To help you get started with smart contracts on Cardano, we provide several example implementations: ### [](https://dev.ada-anvil.io/guides/smart-contract#hello-world-smart-contract) Hello World Smart Contract A simple smart contract example to help you understand the basics. [Hello World Smart Contract](https://dev.ada-anvil.io/guides/smart-contract/hello-world-smart-contract) ### [](https://dev.ada-anvil.io/guides/smart-contract#minting-tokens-with-smart-contracts) Minting Tokens with Smart Contracts Learn how to create and manage tokens using mint validators. [Mint with Smart Contract](https://dev.ada-anvil.io/guides/smart-contract/mint-smart-contract) ### [](https://dev.ada-anvil.io/guides/smart-contract#escrow-smart-contract) Escrow Smart Contract A complete end-to-end example implementing an escrow system with Next.js, Weld, and Blockfrost Webhooks for real-time updates. [End-to-End Next.js Escrow Guide](https://dev.ada-anvil.io/guides/smart-contract/escrow) [PreviousDeno & Fetch](https://dev.ada-anvil.io/guides/delegations/delegate-to-a-pool/deno-and-fetch) [NextBlueprint Management (CIP-57)](https://dev.ada-anvil.io/guides/smart-contract/blueprint-management) Last updated 11 days ago Was this helpful? --- # Collection Information | Anvil Development Agency [Get Collection Assets](https://dev.ada-anvil.io/wayup/collection-information/get-collection-assets) [PreviousProfile Offers](https://dev.ada-anvil.io/wayup/profile-information/get-profile-offers) [NextGet Collection Assets](https://dev.ada-anvil.io/wayup/collection-information/get-collection-assets) Last updated 1 month ago Was this helpful? --- # Get Collection Assets | Anvil Development Agency Retrieve assets from a specific NFT collection with rich filtering and pagination capabilities. **Endpoint (API)**: `GET marketplace/api/get-collection-assets` [](https://dev.ada-anvil.io/wayup/collection-information/get-collection-assets#introduction) Introduction -------------------------------------------------------------------------------------------------------------- This guide shows how to fetch listed NFTs of a collection on the Wayup Marketplace. You can use it to build collection pages, price dashboards, or rarity-based explorers. [](https://dev.ada-anvil.io/wayup/collection-information/get-collection-assets#requirements) Requirements -------------------------------------------------------------------------------------------------------------- * A collection `policyId` [](https://dev.ada-anvil.io/wayup/collection-information/get-collection-assets#api-request-structure) API Request Structure -------------------------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/wayup/collection-information/get-collection-assets#configuration) Configuration Copy // API endpoint for Wayup Marketplace const BASE_URL = "https://prod.api.ada-anvil.app/marketplace/api"; // Collection policy ID to query assets for const POLICY_ID = "6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01"; ### [](https://dev.ada-anvil.io/wayup/collection-information/get-collection-assets#type-definitions) Type Definitions Copy interface CollectionAssetsRequest { policyId: string; limit?: number; cursor?: string; saleType?: 'all' | 'listedOnly' | 'bundles'; orderBy?: 'priceAsc' | 'priceDesc' | 'newest' | 'oldest'; } // AssetResult type omitted for brevity (see Response Schema below) interface CollectionAssetsResponse { results: AssetResult[]; pageState: string | null; } ### [](https://dev.ada-anvil.io/wayup/collection-information/get-collection-assets#query-parameters) Query Parameters Parameter Type Required Description `policyId` `string` Yes Collection policy ID `limit` `number` No Max items per page. Default `10`. `cursor` `string` No Pagination cursor from a previous response. Use it only when requesting **additional pages**; omit it for the first page or if you only need one page. `minPrice` `string` No Minimum listing price (lovelace). `maxPrice` `string` No Maximum listing price (lovelace). `minRarity` `string` No Minimum rarity rank. `maxRarity` `string` No Maximum rarity rank. `orderBy` `string` No One of: `priceAsc`, `priceDesc`, `nameAsc`, `idxAsc`, `recentlyListed`, `rarityAsc`, `recentlyMinted`. Default `priceAsc`. `term` `string` No Full-text search term (name / attributes). `listingType` `string` No Filter by marketplace source: `jpgstore`, `wayup`, `spacebudz`. `saleType` `string` No `all`, `listedOnly`, `bundles`. Use `listedOnly` to return **only actively listed assets**. `properties` `Array<{ key: string; value: string }>` No Attribute filters (exact match). [](https://dev.ada-anvil.io/wayup/collection-information/get-collection-assets#response-schema) Response Schema -------------------------------------------------------------------------------------------------------------------- Copy { "pageState": "string | null", // pass as `cursor` to fetch the next page "count": 20, "results": [\ /* AssetResult */\ ] } `AssetResult` structure: Copy interface AssetResult { unit: string; // policyId.assetName (hex) policyId: string; assetName: string; // hex name: string; // readable image?: string; media?: { src: string; blur?: string }; quantity: number; attributes?: Record; rarity?: number | null; listing?: Listing | null; // present if listed collection?: Collection; // parent collection meta } interface Listing { txHashIndex: string; price: number; // lovelace priceCurrency: string | null; bundleSize: number | null; isProcessing: boolean; type: 'jpgstore' | 'wayup' | 'spacebudz'; version: 'v2' | 'v3'; scriptHash: string; } interface Collection { policyId: string; name: string; handle?: string; verified: boolean; image?: string; banner?: string | null; description?: string | null; royaltyAddress?: string; royaltyPct?: number; socials?: Record; } [](https://dev.ada-anvil.io/wayup/collection-information/get-collection-assets#implementation) Implementation ------------------------------------------------------------------------------------------------------------------ ### [](https://dev.ada-anvil.io/wayup/collection-information/get-collection-assets#step-1-build-the-request-url) Step 1: Build the Request URL Copy // Optional parameters const PAGE_LIMIT = 5; // Number of results to return const PAGE_CURSOR = null; // Pagination cursor (leave as null for first page) const requestUrl = `${BASE_URL}/get-collection-assets?policyId=${POLICY_ID}&saleType=listedOnly&orderBy=priceAsc&limit=${PAGE_LIMIT}`; // If you need the next page: // const requestUrl = `${BASE_URL}/get-collection-assets?policyId=${POLICY_ID}&saleType=listedOnly&orderBy=priceAsc&limit=${PAGE_LIMIT}&cursor=${PAGE_CURSOR}`; ### [](https://dev.ada-anvil.io/wayup/collection-information/get-collection-assets#step-2-execute-the-request) Step 2: Execute the Request Copy console.log("Querying assets for policy ID:", POLICY_ID); const response = await fetch(requestUrl); ### [](https://dev.ada-anvil.io/wayup/collection-information/get-collection-assets#step-3-process-the-response) Step 3: Process the Response Copy const result = await response.json(); const { results, pageState } = result; ### [](https://dev.ada-anvil.io/wayup/collection-information/get-collection-assets#step-4-display-the-results) Step 4: Display the Results Copy console.log(`Found ${results.length} assets:`); results.forEach((asset, idx) => { console.log(`\nAsset #${idx + 1}:`); console.log(` Policy ID: ${asset.policyId}`); console.log(` Asset Name: ${asset.assetName}`); if (asset.listing) { console.log(` Listed Price: ${Number(asset.listing.price) / 1_000_000} ADA`); console.log(` Listing ID: ${asset.listing.txHashIndex}`); } }); ### [](https://dev.ada-anvil.io/wayup/collection-information/get-collection-assets#step-5-handle-pagination) Step 5: Handle Pagination Copy if (pageState) { console.log(`\nMore results available. Use this cursor for the next page: ${pageState}`); } ### [](https://dev.ada-anvil.io/wayup/collection-information/get-collection-assets#running-the-example) Running the Example Copy deno run --allow-net get-collection-assets.ts ### [](https://dev.ada-anvil.io/wayup/collection-information/get-collection-assets#example-output) Example Output Copy Querying assets for policy ID: 6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01 Found 5 assets: Asset #1: Policy ID: 6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01 Asset Name: 4646506f776572436f7265733437 Listed Price: 5 ADA Listing ID: 4baa167631a62343cc4f37f9313ee05d6149b1fda39fd563e12832c8dd49fac9#0 Asset #2: Policy ID: 6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01 Asset Name: 4646506f776572436f726573373338 Listed Price: 5 ADA Listing ID: b808df86c4bb38072dac95bddb65121c30f2e1cf6190514ddac63eadfe1f7c97#0 Asset #3: Policy ID: 6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01 Asset Name: 4646506f776572436f72657331333239 Listed Price: 10 ADA Listing ID: f4a54134caf96b0cdc0f6843e781a6ab4b8d60a3c85adbcabdfd005a2bf7af39#0 Asset #4: Policy ID: 6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01 Asset Name: 4646506f776572436f726573393533 Listed Price: 10 ADA Listing ID: c2f6a8ab662fe06596dfa630b0a6d289acde68af9a8b49fb725a367c4f7140c4#0 Asset #5: Policy ID: 6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01 Asset Name: 4646506f776572436f72657331363835 Listed Price: 11 ADA Listing ID: cb1d799ec0d9f611594a596c930b1710a7311462246a1b4ed605b333cdbe5b41#0 ### [](https://dev.ada-anvil.io/wayup/collection-information/get-collection-assets#the-complete-file) The Complete File get-collection-assets.ts[](https://dev.ada-anvil.io/wayup/collection-information/get-collection-assets#get-collection-assets.ts) Copy // API endpoint for Wayup Marketplace const BASE_URL = "https://prod.api.ada-anvil.app/marketplace/api"; // NFT Collection to query const POLICY_ID = "6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01"; // Step 1: Define the query parameters console.log(`Querying assets for policy ID: ${POLICY_ID}`); // Step 2: Construct the URL with encoded parameters const url = `${BASE_URL}/get-collection-assets?policyId=${POLICY_ID}&saleType=listedOnly&orderBy=priceAsc&limit=5`; // Step 3: Fetch the assets const response = await fetch(url); const result = await response.json(); // Step 4: Process and display results const assets = result.results || []; console.log(`Found ${assets.length} assets:`); // Step 5: Display asset details assets.forEach((asset, index) => { console.log(`\nAsset #${index + 1}:`); console.log(` Policy ID: ${asset.policyId}`); console.log(` Asset Name: ${asset.assetName}`); if (asset.listing) { // 1 ADA = 1_000_000 lovelace console.log(` Listed Price: ${asset.listing.price / 1_000_000} ADA`); console.log(` Listing ID: ${asset.listing.txHashIndex}`); } else { console.log(` Not listed for sale`); } // If metadata is available if (asset.metadata) { console.log(` Name: ${asset.metadata.name || "N/A"}`); if (asset.metadata.traits) { console.log(` Traits:`); Object.entries(asset.metadata.traits).forEach(([key, value]) => { console.log(` ${key}: ${value}`); }); } } }); // Run this with: deno run --allow-net get-collection-assets.ts [PreviousCollection Information](https://dev.ada-anvil.io/wayup/collection-information) [NextOverview](https://dev.ada-anvil.io/tournament-builder/overview) Last updated 1 month ago Was this helpful? --- # Make Offer | Anvil Development Agency [](https://dev.ada-anvil.io/wayup/wayup/create-offer#introduction) Introduction ------------------------------------------------------------------------------------ This guide demonstrates how to make an offer (bid) on an NFT in the Wayup Marketplace using the REST API. Making an offer requires the NFT's policy ID and asset name. [](https://dev.ada-anvil.io/wayup/wayup/create-offer#requirements) Requirements ------------------------------------------------------------------------------------ * A Cardano wallet with sufficient ADA (minimum 5 ADA per offer) * UTXOs from your wallet to fund the transaction. See [Select UTXOs](https://dev.ada-anvil.io/guides/transaction/selecting-utxos#fetching-utxos-multiple-approaches) * Policy ID and asset name of the target NFT [](https://dev.ada-anvil.io/wayup/wayup/create-offer#api-request-structure) API Request Structure ------------------------------------------------------------------------------------------------------ ### [](https://dev.ada-anvil.io/wayup/wayup/create-offer#configuration) Configuration Copy // API endpoint for Wayup Marketplace const NFT_BASE_URL = "https://prod.api.ada-anvil.app/marketplace/api"; // NFT Collection and asset to bid on const NFT_POLICY_ID = "6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01"; const NFT_ASSET_NAME_HEX = "4646506f776572436f726573373338"; // Wallet address to receive change const BIDDER_CHANGE_ADDRESS = "addr1qx33ycd2ymg02dxh6vnnf8dsk54l8ranr9cfjk9qrlj3309c69jc4n8h3uvnczncaqu2sm03pl99h99n75uvtl3mhv0q3z8s8m"; // UTXOs from your wallet to fund the transaction const BIDDER_UTXOS: string[] = [\ "82825820fa7a8f907051db7783be036684481fdce2a5fbcf19f6e5ea5f9f09128288dd1f0382583901a31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1e821a0092b832a1581c6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01a2504646506f776572436f7265733136363001504646506f776572436f7265733137323001",\ "82825820fd43f693f0cd4c690ca3adde613262fc41530e62b3cacc906a0056603f5514ae0082583901a31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1e821a0085b174a1581c6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01a34e4646506f776572436f7265733437014f4646506f776572436f72657336323901504646506f776572436f7265733132343801"\ ]; // Offer amount in ADA (minimum 5 ADA) const OFFER_AMOUNT_ADA = 5; ### [](https://dev.ada-anvil.io/wayup/wayup/create-offer#type-definitions) Type Definitions Copy interface OfferPayload { changeAddress: string; utxos: string[]; createOffer: Array<{ policyId: string; assetName: string; priceAda: number; }>; } [](https://dev.ada-anvil.io/wayup/wayup/create-offer#implementation) Implementation ---------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/wayup/wayup/create-offer#step-1-identify-the-nft) Step 1: Identify the NFT First, you need to identify the NFT you want to bid on by its policy ID and asset name: Copy // Step 1: Identify the NFT to bid on console.log(`NFT to bid on: Policy ID ${NFT_POLICY_ID}, Asset Name ${NFT_ASSET_NAME_HEX}`); ### [](https://dev.ada-anvil.io/wayup/wayup/create-offer#step-2-create-the-offer-payload) Step 2: Create the Offer Payload Next, create the payload for the offer transaction: Copy // Step 2: Create the offer payload const offerPayload = { changeAddress: BIDDER_CHANGE_ADDRESS, utxos: BIDDER_UTXOS, createOffer: [\ {\ policyId: NFT_POLICY_ID,\ assetName: NFT_ASSET_NAME_HEX,\ priceAda: OFFER_AMOUNT_ADA,\ },\ ], }; console.log(`Creating offer for ${OFFER_AMOUNT_ADA} ADA`); ### [](https://dev.ada-anvil.io/wayup/wayup/create-offer#step-3-build-the-transaction) Step 3: Build the Transaction Use the `build-tx` endpoint to create the transaction: Copy // Step 3: Build the transaction const response = await fetch(`${NFT_BASE_URL}/build-tx`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(offerPayload), }); const result = await response.json(); console.log("Transaction:", result.transactions); ### [](https://dev.ada-anvil.io/wayup/wayup/create-offer#running-the-example) Running the Example Copy deno run --allow-net make-offer.ts ### [](https://dev.ada-anvil.io/wayup/wayup/create-offer#example-output) Example Output Copy NFT to bid on: Policy ID 6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01, Asset Name 4646506f776572436f726573373338 Creating offer for 6 ADA Transaction: [\ "84a900d9010282825820fa7a8f907051db7783be036684481fdce2a5fbcf19f6e5ea5f9f09128288dd1f03825820fd43f693f0cd4c690ca3adde613262fc41530e62b3cacc906a0056603f5514ae0001828358391127d46ecbec94b052d8f875cf3beafd0e8ca40e8ad069f677e0a128eab8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1e1a005b8d805820c9f4df9d0e4167f222d78665488e9c2f34e009175cd035e64c34299b40fb61b182583901a31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1e821a00b85e35a1581c6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01a54e4646506f776572436f7265733437014f4646506f776572436f72657336323901504646506f776572436f7265733132343801504646506f776572436f7265733136363001504646506f776572436f7265733137323001021a00047df1031a09744f8407582010d70a7bf1ff1ed57b4ac55c6ed323880724390905b3f69b92615166c3ac96990b5820f695042874028762804e62bef426bf89859028ec2374d8efa258a115f1c868bf0dd9010281825820fd43f693f0cd4c690ca3adde613262fc41530e62b3cacc906a0056603f5514ae001082583901a31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1e821a00700a34a1581c6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01a34e4646506f776572436f7265733437014f4646506f776572436f72657336323901504646506f776572436f7265733132343801111a0015a740a104d901029fd8799f581cb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1e9fd8799fd8799fd8799f581c5f08a64f580e581735070e1b1d2ce29ae6942ab45ccff5a1747d2283ffd8799fd8799fd8799f581c28f17fdd2d8b8f559ad61e899e31eae90b9e209cbedb1ee8a8c6c7d1ffffffffa140d8799f00a1401a000f4240ffffd8799fd8799fd8799f581ca31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcffd8799fd8799fd8799f581cb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1effffffffa1581c6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01d8799f00a14f4646506f776572436f72657337333801ffffd8799fd8799fd8799f581c5288ee085dc108f6fdc262b9e0cdfa92663b302836830efc0c5b4fdfffd8799fd8799fd8799f581c41ff84ecc10aae4f3ba8526ba742c739de9d513ace95e17170b1f04cffffffffa140d8799f00a1401a000f4240fffffffffff5a11902a2a1636d7367715761797570205472616e73616374696f6e"\ ] [](https://dev.ada-anvil.io/wayup/wayup/create-offer#the-complete-file) The Complete File ---------------------------------------------------------------------------------------------- See our examples repository for the complete file: [make-offer.ts](https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/wayup/make-offer.ts) make-offer.ts[](https://dev.ada-anvil.io/wayup/wayup/create-offer#make-offer.ts) Copy // API endpoint for Wayup Marketplace const NFT_BASE_URL = "https://prod.api.ada-anvil.app/marketplace/api"; // NFT Collection and asset to bid on const NFT_POLICY_ID = "POLICY_ID"; const NFT_ASSET_NAME_HEX = "ASSET_NAME_HEX"; // Wallet address to receive change const BIDDER_CHANGE_ADDRESS = "BIDDER_CHANGE_ADDRESS"; // UTXOs from your wallet to fund the transaction const BIDDER_UTXOS: string[] = [\ "BIDDER_UTXOS"\ ]; // Offer amount in ADA (minimum 5 ADA) const OFFER_AMOUNT_ADA = 5; // Step 1: Identify the NFT to bid on console.log(`NFT to bid on: Policy ID ${NFT_POLICY_ID}, Asset Name ${NFT_ASSET_NAME_HEX}`); // Step 2: Create the offer payload const offerPayload = { changeAddress: BIDDER_CHANGE_ADDRESS, utxos: BIDDER_UTXOS, createOffer: [\ {\ policyId: NFT_POLICY_ID,\ assetName: NFT_ASSET_NAME_HEX,\ priceAda: OFFER_AMOUNT_ADA,\ },\ ], }; console.log(`Creating offer for ${OFFER_AMOUNT_ADA} ADA`); // Step 3: Build the transaction const response = await fetch(`${NFT_BASE_URL}/build-tx`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(offerPayload), }); const result = await response.json(); console.log("Transaction:", result.transactions); // Next steps: Sign the hex with your wallet and submit via /submit endpoint // Run this with: deno run --allow-net make-offer.ts [](https://dev.ada-anvil.io/wayup/wayup/create-offer#next-steps) Next Steps -------------------------------------------------------------------------------- After building the transaction: 1. Sign the transaction hex with your wallet 2. Submit the signed transaction using the `/submit` endpoint 3. Wait for confirmation on the blockchain [](https://dev.ada-anvil.io/wayup/wayup/create-offer#best-practices) Best Practices ---------------------------------------------------------------------------------------- 1. **Offer Amount**: Always offer at least 5 ADA per NFT (marketplace minimum) 2. **Wallet Balance**: Ensure your wallet has sufficient funds for the offer plus transaction fees 3. **Asset Verification**: Double-check the policy ID and asset name before submitting offers 4. **Transaction Fees**: Account for network fees in addition to your offer amount [](https://dev.ada-anvil.io/wayup/wayup/create-offer#end-to-end-flow) End-to-End Flow ------------------------------------------------------------------------------------------ 1. Identify the NFT you want to bid on using `get-collection-assets` 2. Build an offer transaction via `/build-tx` with the `createOffer` property 3. Sign the transaction with your wallet 4. Submit the transaction via `/submit`. See [Submit Transactions](https://dev.ada-anvil.io/wayup/submit-tx) 5. Wait for confirmation; your offer will appear to the NFT owner [PreviousBuy NFT](https://dev.ada-anvil.io/wayup/wayup/buy-nft) [NextCreate Listing](https://dev.ada-anvil.io/wayup/wayup/create-listing) Last updated 16 days ago Was this helpful? --- # Native Assets (NFTs/FTs) | Anvil Development Agency [](https://dev.ada-anvil.io/guides/nft-and-ft#introduction) Introduction ----------------------------------------------------------------------------- Cardano supports native assets—both Fungible Tokens (FTs) (like a custom currency) and Non-Fungible Tokens (NFTs) (unique collectibles). Anvil provides straightforward ways to mint, send, or burn these assets, whether they follow CIP-25 (the classic NFT metadata standard) or CIP-68 (a more advanced approach allowing evolving or updatable data). [](https://dev.ada-anvil.io/guides/nft-and-ft#in-this-section) In this section ----------------------------------------------------------------------------------- * We’ll show how CIP-25 metadata might be integrated in a transaction payload. * We’ll highlight how you can combine CIP-68 references similarly (should you want dynamic or updatable NFTs). * We’ll note how to incorporate these tokens into your transaction using mint or outputs. [](https://dev.ada-anvil.io/guides/nft-and-ft#nfts-vs.-fts-dollar-bills-vs.-art) NFTs vs. FTs: Dollar Bills vs. Art ------------------------------------------------------------------------------------------------------------------------ ### [](https://dev.ada-anvil.io/guides/nft-and-ft#fungible-tokens-ft) Fungible Tokens (FT) **Analogy:** Think of them like dollar bills—each bill has the same value and is interchangeable with any other. **1 FT = 1 FT, regardless of its “serial number.”** **Use Cases:** * In-Game Currencies: A consistent token players can trade. * Community Tokens: Reward points or governance tokens. * Stablecoins: Pegged to fiat currency. ### [](https://dev.ada-anvil.io/guides/nft-and-ft#non-fungible-tokens-nft) Non-Fungible Tokens (NFT) **Analogy:** Think of them like a one-of-a-kind painting—unique art with no duplicates. **1 NFT cannot simply be swapped for another NFT of the same “collection” because each is different.** **Use Cases:** * Digital Art & Collectibles: Rare items or artwork minted under a policy. * Event Tickets: Each ticket is unique, can’t be replaced by another. * Real-World Asset Tokens: Tokenizing property deeds, certificates, or identity credentials. * * * [](https://dev.ada-anvil.io/guides/nft-and-ft#policy-ids-the-artists-collection) Policy IDs: The Artist’s Collection ------------------------------------------------------------------------------------------------------------------------- Tokens on Cardano—whether NFT or FT—are grouped under a **Policy ID**. * **Policy ID** = A unique hash generated from a minting script or policy. See [Native Scripts](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/native-scripts/README.md) . * **Collection**: If an artist mints multiple NFTs under the same policy ID, they effectively form a “series” or “collection.” * **Locking**: The policy can be time-locked or multi-signature locked, controlling whether more tokens can be minted in the future. **In our analogy:** Policy ID = The “brand” or “collection” label that says “all these paintings or bills come from the same source.” * * * [](https://dev.ada-anvil.io/guides/nft-and-ft#cip-25-cip-68-metadata-for-nfts) CIP-25 / CIP-68 Metadata (for NFTs) ----------------------------------------------------------------------------------------------------------------------- When you mint an NFT, you usually include metadata that describes the token’s name, image, description, attributes, etc. The most common metadata standards: * **CIP-25 (Label 721):** Legacy standard for NFT metadata, widely supported by wallets and marketplaces. * **CIP-68:** Newer approach that places more data in the on-chain datum, allowing dynamic NFTs or advanced features. Many still store a CIP-25–style JSON under label 721 for backward compatibility. ### [](https://dev.ada-anvil.io/guides/nft-and-ft#example) Example: Copy { "version": "cip-25", "assetName": {"name": "my_collection_nft_01", "format": "utf8"}, "metadata": { "name": "My Collection NFT #1", "image": "ipfs://QmExampleHash", "mediaType": "image/png", "description": "Unique artwork minted with the Anvil API", "quantity": 1 } } Depending on your approach, you embed this in your transaction’s metadata—Anvil can help you structure that under label 721. * * * [](https://dev.ada-anvil.io/guides/nft-and-ft#cip-25-payload) CIP-25 Payload --------------------------------------------------------------------------------- **Minting** Copy { // Address to pay for the TX and receive any leftover ADA from the transaction. "changeAddress": "addr_test1...", // UTXOs from the wallet paying for fees (list of CBOR UTXOs) "utxos": ["8282...", "8282..."], // Message to include in the transaction "message": "Asset minted with anvil api", // Array of assets to mint "mint": [\ {\ "version": "cip-25",\ "assetName": {"name": "anvil_api_cip_25", "format": "utf8"},\ "metadata": {\ "name": "anvil_api_cip_25 #1",\ "image": [\ "https://ada-anvil.s3.ca-central-1.amazonaws.com/",\ "logo_pres_V2_3.png"\ ],\ "mediaType": "image/png",\ "description": "Testing CIP-25 using anvil API",\ },\ "policyId": "4d5bd6249f0d9e4b2762ce334e2973dc7fd414ec1e08b4b0c2159bfb",\ "quantity": 1,\ // Destination to send minted asset.\ "destAddress": "addr_test1..."\ }\ ], // Optional but recommended for performance and latency. Required for new/unregistered scripts. "preloadedScripts": [\ {\ "type": "simple",\ "script": {\ "type": "all",\ "scripts": [\ {\ // Defines the policy wallet that will sign the transaction\ "type": "sig",\ "keyHash": "fdf151b600df2492005221876c7d7e33056496572c7363c33a1e3609",\ },\ {\ // Defines the slot number that this policy can mint/burn tokens\ "type": "before",\ "slot": "100000000",\ },\ ],\ },\ // The hash of the native script, which is the Policy ID.\ "hash": "4d5bd6249f0d9e4b2762ce334e2973dc7fd414ec1e08b4b0c2159bfb",\ }\ ], } [](https://dev.ada-anvil.io/guides/nft-and-ft#cip-68-payload) CIP-68 Payload --------------------------------------------------------------------------------- Copy { // Address receive any leftover UTxOs from the transaction. Usually the "changeAddress": "addr_test1...", // UTXOs from the wallet paying for fees (list of CBOR UTXOs) "utxos": ["8282...", "8282..."], "mint": [\ {\ // Reference Token - Label 100 -> Sent to Metadata Manager Wallet or Script Address\ "version": "cip68",\ //Name of the asset. Must be the same for both tokens\ "assetName": { "name": "youruniquecip68asset", "format": "utf8", "label": 100 },\ "metadata": {\ "name": "Your NFT Name",\ "image": "ipfs://your-image-hash",\ "mediaType": "image/png",\ "description": "A description for your CIP-68 asset"\ },\ "policyId": "8e024681ee83f54bd5f9a0334641...",\ "quantity": 1,\ // Destination to send minted asset.\ "destAddress": "addr_test2..."\ },\ {\ // User Token - Label 222 -> Sent to User Wallet\ "version": "cip68",\ // Asset name must be identical to the (100) token's assetName\ "assetName": { "name": "youruniquecip68asset", "format": "utf8", "label": 222 },\ "policyId": "8e024681ee83f54bd5f9a033464...",\ "quantity": 1,\ // Destination to send minted asset.\ "destAddress": "addr_test1..."\ }\ ], // The `preloadedScripts` array provides script data for minting authorization. // - Native scripts: contains the actual native script // - Plutus scripts: contains blueprint data (native script derived from blueprint) // Optional but recommended for performance and latency. Required for new/unregistered scripts. "preloadedScripts": [ /* ... script details ... */ ], // The `scriptInteractions` array is ONLY required for smart contract validation. // It is not used for the native script / Metadata-Manager Wallet approach. // It is ONLY required for blueprints that are not registered yet. // The full payload format is covered in the smart contract guide. "scriptInteractions": [ /* ... validator, redeemer, datum ... */ ], } * * * [](https://dev.ada-anvil.io/guides/nft-and-ft#explanation) Explanation --------------------------------------------------------------------------- * **changeAddress:** Where leftover funds or minted assets return if they’re not explicitly assigned. * **message:** This sets CIP-20–style text if you want a short on-chain memo. * **mint array:** * **version:** "cip-25" tells Anvil you’re embedding CIP-25–compliant metadata. * **assetName:** The token’s name as a JSON object * **name:** The name of the asset. * **format:** The format of the name. Can be "utf8" or "hex". * **Example:** {name: "MyAssetName", format: "utf8"} * **metadata:** CIP-25 fields like name, image, mediaType, description. * **policyId:** The unique 56-hex string for your minting native script. * **quantity:** How many tokens to mint (1 for a single NFT). * **destAddress:** If you want the minted asset to go to a specific address different from your changeAddress. * **preloadedScripts:** Example of a native script using `type": "all"` (ScriptAll). Includes a signature requirement and time-lock. * * * [](https://dev.ada-anvil.io/guides/nft-and-ft#minting-vs.-sending) Minting vs. Sending ------------------------------------------------------------------------------------------- * **Mint:** Use the `mint` array. If you want to send additional tokens after minting, include them in the `outputs[]`. * **Transferring an existing token:** Just specify it under `outputs[].assets[]`. No need to define `mint`. * * * [](https://dev.ada-anvil.io/guides/nft-and-ft#key-steps-to-use) Key Steps to Use ------------------------------------------------------------------------------------- 1. Assemble the payload: `changeAddress` or `utxos` + the `mint` array if creating tokens. 2. Optionally add `CIP-20` or `CIP-25` metadata under `metadata` or `message`. 3. Call `POST /transactions/build`. 4. Sign the resulting transaction with your policy key and/or user’s key. 5. Submit the signed transaction to `POST /transactions/submit`. * * * [](https://dev.ada-anvil.io/guides/nft-and-ft#conclusion) Conclusion ------------------------------------------------------------------------- NFT & FT usage on Cardano with Anvil revolves around two primary tasks: * **Minting tokens** by specifying them in the `mint` array (plus a reference native script if needed). * **Transferring tokens** by listing them in `outputs[].assets[]`. For CIP-25 or CIP-68 metadata, you embed JSON describing your asset (NFT name, image, etc.) so wallets and explorers can parse it. The snippet above with preloadedScripts and policyId is a typical CIP-25 single NFT example. Whether you’re distributing a fungible currency or a one-of-a-kind collectible, the approach is the same—CIP standards define the metadata, and Anvil’s transaction builder handles the heavy lifting. [PreviousMetadata](https://dev.ada-anvil.io/guides/payload) [NextNative Scripts](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts) Last updated 11 days ago Was this helpful? --- # Spend Validator | Anvil Development Agency [](https://dev.ada-anvil.io/guides/smart-contract/validators/spend-validator#overview) Overview ---------------------------------------------------------------------------------------------------- Spend validators control the conditions under which UTXOs can be spent from script addresses. [](https://dev.ada-anvil.io/guides/smart-contract/validators/spend-validator#real-world-use-cases) Real-world Use Cases ---------------------------------------------------------------------------------------------------------------------------- * **Asset Locking**: Define conditions for when locked assets can be accessed * **Multi-signature Schemes**: Require approval from multiple parties * **Time-locked Funds**: Allow access only after certain time conditions * **Conditional Payments**: Release funds when specific on-chain/off-chain conditions are met [](https://dev.ada-anvil.io/guides/smart-contract/validators/spend-validator#required-utxo-fields-and-deployed-blueprint) Required UTxO Fields and Deployed Blueprint -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- **PREREQUISITE: DEPLOYED SMART CONTRACT** Before you can interact with any validator, you must have a compiled and deployed smart contract blueprint. The validator hash used in these examples comes from your deployed blueprint. Refer to the [Blueprint Management](https://dev.ada-anvil.io/guides/smart-contract/blueprint-management) guide for details on how to deploy your smart contract blueprint. **Don't forget to lock assets at the script address in a prior transaction.** Then when spending, specify the locked UTxO using the `outputRef` field, which pinpoints the UTxO by its transaction hash and output index. Copy { "txHash": "transaction_hash", // Transaction hash where funds were locked "index": 0 // Output index within that transaction } **Anvil API returns the UTXOs in the order they were added when you previously locked the assets.** [](https://dev.ada-anvil.io/guides/smart-contract/validators/spend-validator#spend-validator-api-interaction-example) Spend Validator API Interaction Example ------------------------------------------------------------------------------------------------------------------------------------------------------------------ Use the below examples to build transactions that interact with the spend validator. If you are unfamiliar with the Anvil API transaction builder, review the [Transaction Overview](https://dev.ada-anvil.io/guides/transaction) guide first. ### [](https://dev.ada-anvil.io/guides/smart-contract/validators/spend-validator#id-1.-locking-assets-at-script-address) 1\. Locking Assets at Script Address Copy // Build a transaction to lock funds at a script address const lockInput = { // Address where change outputs will be sent (your wallet address) changeAddress: "ADDR_YOUR_CHANGE_ADDRESS", // Optional message for transaction metadata (appears in block explorers) message: "Locking funds using the spend validator", // UTxOs to use as inputs (hex-encoded transaction outputs) // These provide the funds being locked at the script address utxos: [\ "8282...", "8282...", "8282...", "8282..."\ ], // Outputs to create in this transaction outputs: [\ {\ // Script address derived from validator hash\ address: scriptAddress,\ \ // Amount of lovelace (unit of ADA) to lock at script address\ lovelace: LOVELACE_AMOUNT,\ \ // Datum stores information needed for future spending\ datum: {\ // Stored directly on-chain (vs. hash-only)\ type: "inline",\ \ // The actual datum content as specified by your validator's blueprint\ value: {\ owner: "addr_test1xyz...", // Owner's verification key hash who can unlock\ },\ \ // References validator hash and purpose for serialization\ shape: { validatorHash, purpose: "spend" }\ }\ }\ ] }; ### [](https://dev.ada-anvil.io/guides/smart-contract/validators/spend-validator#id-2.-spending-unlocking-locked-utxo) 2\. Spending (Unlocking) Locked UTxO Copy // Build a transaction to spend (unlock) funds from the script address const unlockInput = { // Address where unlocked funds will be sent (your wallet address) changeAddress: "ADDR_YOUR_CHANGE_ADDRESS", // Optional message for transaction metadata (appears in block explorers) message: "Unlocking funds using the spend validator", // UTxOs to use for transaction fees and collateral // These are regular wallet UTxOs, not the script UTxO being spent utxos: [\ "8282...", "8282...", "8282...", "8282..."\ ], // Define how to interact with smart contract validators scriptInteractions: [\ {\ // Validator script hash (matches one used when locking funds)\ hash: validatorHash,\ \ // We're using the spend validator\ purpose: "spend",\ \ // Points to the specific UTXO at the script address we want to spend/unlock\ outputRef: { \ txHash: "TRANSACTION_HASH", // Transaction hash where funds were locked\ index: 0 // Output index within that transaction\ },\ \ // Redeemer provides arguments to the validator\ // Here we're unlocking the funds with a simple message defined in the smart contract\ redeemer: { \ type: "json", \ value: { \ msg: "hex-encoded-message" \ } \ }\ }\ ], // Transaction must be signed by these key hashes // Often matches owner key from the datum to verify authorization requiredSigners: [signerKeyHash] }; [](https://dev.ada-anvil.io/guides/smart-contract/validators/spend-validator#see-examples) See Examples ------------------------------------------------------------------------------------------------------------ [https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/hello-world-smart-contract.md](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/hello-world-smart-contract.md) [https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/escrow/README.md](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/escrow/README.md) [PreviousValidators](https://dev.ada-anvil.io/guides/smart-contract/validators) [NextHello World Smart Contract](https://dev.ada-anvil.io/guides/smart-contract/hello-world-smart-contract) Last updated 2 months ago Was this helpful? --- # CLI Tool to Mint Assets | Anvil Development Agency This a a very specific example, the goal is to show how flexible and versatile the Anvil API is. **This example will be entirely done into one file named** `**index.ts**` [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25/cli-tool-to-mint-assets#objectives) **Objectives** --------------------------------------------------------------------------------------------------------------------- * Build a simple CLI tool to mint assets * Create a Policy (an NFT Collection) * Create a custom function to customize the asset metadata * Create 2 wallets, one for the policy and one to act as the customer * Submit transaction to the network * This is only an example showing the anvil api versatility * The example is built on Cardano Preprod ### [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25/cli-tool-to-mint-assets#dependencies) **Dependencies** Copy import { Buffer } from "node:buffer"; import { Credential, type Ed25519KeyHash, FixedTransaction, NativeScript, NativeScripts, PrivateKey, ScriptAll, ScriptPubkey, TimelockExpiry, } from "npm:@emurgo/[email protected]"; import { parseArgs } from "jsr:@std/cli/parse-args"; ### [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25/cli-tool-to-mint-assets#cardano-wallets) Cardano Wallets We have this utility to generate wallets: [https://github.com/Cardano-Forge/cardano-wallet-cli/releases](https://github.com/Cardano-Forge/cardano-wallet-cli/releases) Copy ~/Downloads/cardano-wallet-macos-latest --name policy --mnemonic ~/Downloads/cardano-wallet-macos-latest --name customer --mnemonic You'll need to add some tADA to both wallets—100 should be more than enough. ### [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25/cli-tool-to-mint-assets#import-helper-functions) Import Helper Functions To reduce the amount of content in this guide, you only have to import all functions defined here: [https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/utilities-functions/README.md](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/utilities-functions/README.md) ### [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25/cli-tool-to-mint-assets#cli) CLI Copy const args: { _: []; expireDate: string; customerWalletPath: string; policyWalletPath: string; metadataTemplatePath: string; counter: number; submit: boolean; } = parseArgs(Deno.args); Parse and prepare the policy and wallets: Copy // NOTE: Be sure to send ADA in this address, it will be used to pay the tx fee. const customerWallet = JSON.parse( Deno.readTextFileSync(args.customerWalletPath) ); // Wallet to create the policy with, no ADA is required for this one. const policyWallet = JSON.parse(Deno.readTextFileSync(args.policyWalletPath)); // Expiration date, you can interact with the policy until this date is reached. // After that the policy is locked. const slot = dateToSlot(new Date(args.expireDate)); const keyhash = get_keyhash(policyWallet.skey); if (!keyhash) { throw new Error("Unable to get key hash for policy, missing or invalid skey"); } const policyAnvilApi = create_policy_script(keyhash, slot); ### [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25/cli-tool-to-mint-assets#collection-configurations) Collection configurations This example requires 2 rules. Copy const policyAnvilApiScript = { type: "all", scripts: [\ {\ type: "sig",\ keyHash: keyhash.to_hex(),\ },\ {\ type: "before",\ slot: slot,\ },\ ], }; Meaning that the policy has to be signed by the wallet defined in the policy wallet path parameter AND all mutations must be done before the date defined. ### [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25/cli-tool-to-mint-assets#create-metadata) Create Metadata This function is where you have to define your own data and configuration per asset. CIP-25 enforces few fields as mandatory see here: [https://cips.cardano.org/cip/CIP-25](https://cips.cardano.org/cip/CIP-25) You can also use our powerful metadata validator: [https://metadraft.io](https://metadraft.io/) **For example** Copy const assets: { version: string; assetName: string; metadata: { name: string; image: string | string[]; mediaType: string; description: string; epoch: number; }; policyId: string; quantity: 1; }[] = []; const assetMetadataTemplate = JSON.parse( Deno.readTextFileSync(args.metadataTemplatePath) ); const counter = args.counter; // Simulate use case assets.push({ version: "cip25", assetName: `anvilapicip25_${counter}`, metadata: { ...assetMetadataTemplate, // Adding custom data just to test the flow name: `anvil-api-${counter}`, epoch: new Date().getTime(), // dummy data }, policyId: get_policy_id(policyAnvilApi.mint_script), quantity: 1, }); The counter is used to be sure that the `assetName` remains unique. TBD: Show an example output (screenshot or link to preprod url or all of them) ### [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25/cli-tool-to-mint-assets#the-transaction) The transaction Copy const data = { changeAddress: customerWallet.enterprise_address_preprod, mint: assets, preloadedScripts: [\ {\ type: "simple",\ script: policyAnvilApiScript,\ hash: get_policy_id(policyAnvilApi.mint_script),\ },\ ], }; const urlTX = "https://preprod.api.ada-anvil.app/v2/services/transactions/build"; const transactionToSignWithPolicyKey = await fetch(urlTX, { method: "POST", body: JSON.stringify(data), headers: { "Content-Type": "application/json", "X-Api-Key": "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9", }, }).then((res) => res.json()); ### [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25/cli-tool-to-mint-assets#sign-with-the-policy-wallet) Sign with the policy wallet Copy // Sign transaction with policy key const transactionToSignWithCustomerKey = FixedTransaction.from_bytes( Buffer.from(transactionToSignWithPolicyKey.complete, "hex") ); transactionToSignWithCustomerKey.sign_and_add_vkey_signature( PrivateKey.from_bech32(policyWallet.skey) ); ### [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25/cli-tool-to-mint-assets#sign-with-the-customer-wallet) Sign with the customer wallet _usually done using the browser extension_ Copy const txToSubmitOnChain = FixedTransaction.from_bytes( Buffer.from(transactionToSignWithCustomerKey.to_hex(), "hex") ); // This sign the tx and add vkeys to the txToSubmitOnChain, so in submit we don't need to provide signautres txToSubmitOnChain.sign_and_add_vkey_signature( PrivateKey.from_bech32(customerWallet.skey) ); ### [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25/cli-tool-to-mint-assets#submit-transaction) Submit Transaction Copy if (args.submit) { const urlSubmit = "https://preprod.api.ada-anvil.app/v2/services/transactions/submit"; const submitted = await fetch(urlSubmit, { method: "POST", body: JSON.stringify({ signatures: [], // This empty because the txToSubmitOnChain has the vkeys transaction: txToSubmitOnChain.to_hex(), }), headers: { "Content-Type": "application/json", "X-Api-Key": "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9", }, }).then((res) => res.json()); console.debug(submitted); } else { console.log(txToSubmitOnChain.to_hex()); } * * * ### [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25/cli-tool-to-mint-assets#using-the-cli) Using the CLI Copy deno compile --allow-read --allow-write --allow-net index.ts _Dont forget to increase the counter, otherwise you will double mint._ Copy ./index --expireDate=2025-01-01 \ --customerWalletPath=customer.json \ --policyWalletPath=policy.json \ --metadataTemplatePath=metatemplate.json \ --counter=2 \ --submit File Content - (customer.json, policy.json, metatemplate.json)[](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25/cli-tool-to-mint-assets#file-content-customer.json-policy.json-metatemplate.json) **customer.json** Copy { "skey": "REDACTED", "skey_hex": "REDACTED", "pkey": "REDACTED", "pkey_hex": "REDACTED", "key_hash": "REDACTED", "base_address_preview": "addr_test1qqc0f6py9qeyp5k2vme7pxct0t0ut5mczmq8yfumx8cfpfx7kdf62usuu92g463pyumktcckmd9x7nmrvfxc9w04dnfqz7ycf9", "base_address_preprod": "addr_test1qqc0f6py9qeyp5k2vme7pxct0t0ut5mczmq8yfumx8cfpfx7kdf62usuu92g463pyumktcckmd9x7nmrvfxc9w04dnfqz7ycf9", "base_address_mainnet": "addr1qyc0f6py9qeyp5k2vme7pxct0t0ut5mczmq8yfumx8cfpfx7kdf62usuu92g463pyumktcckmd9x7nmrvfxc9w04dnfqpgec96", "enterprise_address_mainnet": "addr1vyc0f6py9qeyp5k2vme7pxct0t0ut5mczmq8yfumx8cfpfq5xuxsn", "enterprise_address_preview": "addr_test1vqc0f6py9qeyp5k2vme7pxct0t0ut5mczmq8yfumx8cfpfq0wg6lk", "enterprise_address_preprod": "addr_test1vqc0f6py9qeyp5k2vme7pxct0t0ut5mczmq8yfumx8cfpfq0wg6lk", "reward_address_mainnet": "stake1u80tx5a9wgwwz4y2agsjwdm9uvtdkjn0fa3kynvzh86ke5se4shkt", "reward_address_preview": "stake_test1ur0tx5a9wgwwz4y2agsjwdm9uvtdkjn0fa3kynvzh86ke5s7l64jk", "reward_address_preprod": "stake_test1ur0tx5a9wgwwz4y2agsjwdm9uvtdkjn0fa3kynvzh86ke5s7l64jk", "mnemonic": "REDACTED" } **policy.json** Copy { "skey": "REDACTED", "skey_hex": "REDACTED", "pkey": "REDACTED", "pkey_hex": "REDACTED", "key_hash": "REDACTED", "base_address_preview": "addr_test1qzmdaaddmhut3e3kyjsv5f3nsws0mcxk7tu2gpwql8f8pfschsxexl9yakag0rdpf7m4rgg4dvlwuzklghq7trdr4qkqpckdye", "base_address_preprod": "addr_test1qzmdaaddmhut3e3kyjsv5f3nsws0mcxk7tu2gpwql8f8pfschsxexl9yakag0rdpf7m4rgg4dvlwuzklghq7trdr4qkqpckdye", "base_address_mainnet": "addr1qxmdaaddmhut3e3kyjsv5f3nsws0mcxk7tu2gpwql8f8pfschsxexl9yakag0rdpf7m4rgg4dvlwuzklghq7trdr4qkqzwtdgx", "enterprise_address_mainnet": "addr1vxmdaaddmhut3e3kyjsv5f3nsws0mcxk7tu2gpwql8f8pfsc3x6e8", "enterprise_address_testnet": "addr_test1vzmdaaddmhut3e3kyjsv5f3nsws0mcxk7tu2gpwql8f8pfsrejxkz", "reward_address_mainnet": "stake1uyvtcrvn0jjwmw583ks5ld635y2kk0hwpt05ts093k36stqfn5cxe", "reward_address_testnet": "stake_test1uqvtcrvn0jjwmw583ks5ld635y2kk0hwpt05ts093k36stqwe76zy", "mnemonic": "REDACTED" } **metatemplate.json** Copy { "name": "TBD", "image": [\ "https://ada-anvil.s3.ca-central-1.amazonaws.com/",\ "logo_pres_V2_3.png"\ ], "mediaType": "image/png", "description": "Testing CIP-25 using anvil API" } _The code will override the name key_ [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25/cli-tool-to-mint-assets#explorer) Explorer ------------------------------------------------------------------------------------------------------------- * [https://preprod.cexplorer.io/policy/fa16f906cb86e6b147683c0bede27215b85550f8b91a3f677292eabb](https://preprod.cexplorer.io/policy/fa16f906cb86e6b147683c0bede27215b85550f8b91a3f677292eabb) [](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25/cli-tool-to-mint-assets#the-whole-file-deno-version) The Whole File (Deno Version) ----------------------------------------------------------------------------------------------------------------------------------------------------- index.ts[](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25/cli-tool-to-mint-assets#index.ts) Copy import { Buffer } from "node:buffer"; import { Credential, type Ed25519KeyHash, FixedTransaction, NativeScript, NativeScripts, PrivateKey, ScriptAll, ScriptPubkey, TimelockExpiry, } from "npm:@emurgo/[email protected]"; import { parseArgs } from "jsr:@std/cli/parse-args"; const args: { _: []; expireDate: string; customerWalletPath: string; policyWalletPath: string; metadataTemplatePath: string; counter: number; submit: boolean; } = parseArgs(Deno.args); // deno run -A steps.ts \ // --expireDate=2026-01-01 \ // --customerWalletPath=customer.json \ // --policyWalletPath=policy.json \ // --metadataTemplatePath=metatemplate.json \ // --counter=20250117 \ // --submit const dateToSlot = (date: Date) => { return Math.floor(date.getTime() / 1000) - 1596491091 + 4924800; }; export function get_keyhash(private_key: string): Ed25519KeyHash | undefined { return Credential.from_keyhash( PrivateKey.from_bech32(private_key).to_public().hash() ).to_keyhash(); } export function create_policy_script( policy_key_hash: Ed25519KeyHash, ttl: number, with_timelock = true ): { mint_script: NativeScript; policy_ttl: number } { const scripts = NativeScripts.new(); const key_hash_script = NativeScript.new_script_pubkey( ScriptPubkey.new(policy_key_hash) ); scripts.add(key_hash_script); const policy_ttl: number = ttl; if (with_timelock) { const timelock = TimelockExpiry.new(policy_ttl); const timelock_script = NativeScript.new_timelock_expiry(timelock); scripts.add(timelock_script); } const mint_script = NativeScript.new_script_all(ScriptAll.new(scripts)); return { mint_script, policy_ttl }; } export function bytes_to_hex(input: Uint8Array): string { return Buffer.from(input).toString("hex"); } export function get_policy_id(mint_script: NativeScript): string { return bytes_to_hex(mint_script.hash().to_bytes()); } // NOTE: Be sure to send ADA in this address, it will be used to pay the tx fee. const customerWallet = JSON.parse( Deno.readTextFileSync(args.customerWalletPath) ); const policyWallet = JSON.parse(Deno.readTextFileSync(args.policyWalletPath)); // Date before you can interact with the policy const slot = dateToSlot(new Date(args.expireDate)); const keyhash = get_keyhash(policyWallet.skey); if (!keyhash) { throw new Error("Unable to get key hash for policy, missing or invalid skey"); } const policyAnvilApi = create_policy_script(keyhash, slot); // // CUSTOM FOR EACH MINT COLLECTION // // not the best way to handle this, a config file would be better const policyAnvilApiScript = { type: "all", scripts: [\ {\ type: "sig",\ keyHash: keyhash.to_hex(),\ },\ {\ type: "before",\ slot: slot,\ },\ ], }; const assets: { version: string; assetName: string; metadata: { name: string; image: string | string[]; mediaType: string; description: string; epoch: number; }; policyId: string; quantity: 1; }[] = []; const assetMetadataTemplate = JSON.parse( Deno.readTextFileSync(args.metadataTemplatePath) ); const counter = args.counter; // Simulate use case assets.push({ version: "cip25", assetName: `anvilapicip25_${counter}`, metadata: { ...assetMetadataTemplate, // Adding custom data just to test the flow name: `anvil-api-${counter}`, epoch: new Date().getTime(), // dummy data }, policyId: get_policy_id(policyAnvilApi.mint_script), quantity: 1, }); // // Generic calls to create, sign and submit tx // const data = { changeAddress: customerWallet.enterprise_address_preprod, mint: assets, preloadedScripts: [\ {\ type: "simple",\ script: policyAnvilApiScript,\ hash: get_policy_id(policyAnvilApi.mint_script),\ },\ ], }; const urlTX = "https://preprod.api.ada-anvil.app/v2/services/transactions/build"; const transactionToSignWithPolicyKey = await fetch(urlTX, { method: "POST", body: JSON.stringify(data), headers: { "Content-Type": "application/json", "X-Api-Key": "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9", }, }).then((res) => res.json()); console.debug( "transactionToSignWithPolicyKey: ", transactionToSignWithPolicyKey ); // // policy signature // const transactionToSignWithCustomerKey = FixedTransaction.from_bytes( Buffer.from(transactionToSignWithPolicyKey.complete, "hex") ); transactionToSignWithCustomerKey.sign_and_add_vkey_signature( PrivateKey.from_bech32(policyWallet.skey) ); console.debug( "transactionToSignWithCustomerKey: ", transactionToSignWithCustomerKey.to_hex() ); // // customer signature // const txToSubmitOnChain = FixedTransaction.from_bytes( Buffer.from(transactionToSignWithCustomerKey.to_hex(), "hex") ); console.debug("Customer Wallet", customerWallet); // This sign the tx and add vkeys to the txToSubmitOnChain, so in submit we don't need to provide signautres txToSubmitOnChain.sign_and_add_vkey_signature( PrivateKey.from_bech32(customerWallet.skey) ); console.debug("txToSubmitOnChain: ", txToSubmitOnChain.to_hex()); console.debug( "txToSubmitOnChain JSON: ", txToSubmitOnChain.witness_set().to_json() ); // // Submit tx // if (args.submit) { const urlSubmit = "https://preprod.api.ada-anvil.app/v2/services/transactions/submit"; const submitted = await fetch(urlSubmit, { method: "POST", body: JSON.stringify({ signatures: [], // This empty because the txToSubmitOnChain has the vkeys transaction: txToSubmitOnChain.to_hex(), }), headers: { "Content-Type": "application/json", "X-Api-Key": "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9", }, }).then((res) => res.json()); console.debug(submitted); } else { console.log(txToSubmitOnChain.to_hex()); } [PreviousMint NFTs (CIP-25)](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25) [NextDeno & Fetch](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25/deno-and-fetch) Last updated 3 months ago Was this helpful? --- # Blueprint Management (CIP-57) | Anvil Development Agency [](https://dev.ada-anvil.io/guides/smart-contract/blueprint-management#overview) Overview ---------------------------------------------------------------------------------------------- The Anvil API provides powerful endpoints for managing Cardano smart contract blueprints. These endpoints allow you to create, retrieve, update, and delete CIP-57 compliant blueprints in a standardized way. This is usually the compiled `plutus.json` file from an Aiken build. **SECURITY NOTE** All blueprint operations require an API key, which serves as your authentication credential and determines which blueprints you can access. The system maintains blueprint isolation between users, ensuring that: * When you create a blueprint, it's associated with your API key * When you search for blueprints, you only see blueprints created with your API key * When you delete blueprints, you can only remove blueprints you own This security model ensures that your smart contract blueprints remain private to your account while still enabling collaboration through controlled sharing mechanisms. [](https://dev.ada-anvil.io/guides/smart-contract/blueprint-management#blueprint-management-api-endpoints) Blueprint Management API Endpoints -------------------------------------------------------------------------------------------------------------------------------------------------- Endpoint Method Description Required Parameters Optional Parameters `/blueprints` POST Create or update a blueprint `blueprint`: CIP-57 compliant blueprint `refs`: Reference UTXOs `/blueprints` GET Find blueprints matching criteria None `title`, `version`, `limit`, `offset`, `sortBy`, `order` `/blueprints` DELETE Remove blueprints `title`: Blueprint title `version`: Blueprint version [](https://dev.ada-anvil.io/guides/smart-contract/blueprint-management#common-setup) Common Setup ------------------------------------------------------------------------------------------------------ The following setup is common to all blueprint operations: Copy // Import your blueprint (typically from an Aiken build) import plutusJson from "../hello-world/plutus.json" with { type: "json" }; // Setup API key and content type const headers = { "x-api-key": "YOUR_API_KEY", "Content-Type": "application/json", }; // Define the API endpoint const blueprintsUrl = "https://preprod.api.ada-anvil.app/v2/services/blueprints"; [](https://dev.ada-anvil.io/guides/smart-contract/blueprint-management#creating-or-updating-blueprints) Creating or Updating Blueprints -------------------------------------------------------------------------------------------------------------------------------------------- Use the `POST` method to upload a new blueprint or update an existing one. The API automatically determines whether to create or update based on the blueprint's title and version. Copy async function upsertBlueprint() { try { // Prepare the request body with your CIP-57 blueprint const input = { blueprint: plutusJson }; const res = await fetch(blueprintsUrl, { method: "POST", body: JSON.stringify(input), headers, }); const json = await res.json(); // The response contains: // - jsonSchema: Generated JSON schema from the blueprint // - scriptAddresses: Record of validator hashes and their addresses // - blueprintId: ID of the created/updated blueprint console.log("json", JSON.stringify(json, null, 2)); } catch (error) { console.log("error", error); } } [](https://dev.ada-anvil.io/guides/smart-contract/blueprint-management#finding-blueprints) Finding Blueprints ------------------------------------------------------------------------------------------------------------------ Use the `GET` method to search for blueprints by title, version, or other criteria. Copy async function findBlueprints() { try { const url = new URL(blueprintsUrl); // Optional search parameters url.searchParams.set("title", "cardano-forge/vesting-contract"); // Filter by title url.searchParams.set("version", "0.0.0"); // Filter by version // Additional optional parameters: limit, offset, sortBy, order const res = await fetch(url, { method: "GET", headers, }); const json = await res.json(); // The response contains: // - results: Array of matching blueprints // - total: Total count of matching blueprints console.log("json", JSON.stringify(json, null, 2)); } catch (error) { console.log("error", error); } } [](https://dev.ada-anvil.io/guides/smart-contract/blueprint-management#deleting-blueprints) Deleting Blueprints -------------------------------------------------------------------------------------------------------------------- Use the `DELETE` method to remove blueprints from the system. Copy async function deleteBlueprint() { try { const url = new URL(blueprintsUrl); // Required parameter url.searchParams.set("title", "cardano-forge/vesting-contract"); // Optional parameter - if omitted, all versions with the specified title will be deleted url.searchParams.set("version", "0.0.0"); const res = await fetch(url, { method: "DELETE", headers, }); const json = await res.json(); // The response contains: // - deleted: Array of strings identifying the deleted blueprints console.log("json", JSON.stringify(json, null, 2)); } catch (error) { console.log("error", error); } } [PreviousSmart Contracts](https://dev.ada-anvil.io/guides/smart-contract) [NextSmart Contract Utilities](https://dev.ada-anvil.io/guides/smart-contract/smart-contract-utilities) Last updated 11 days ago Was this helpful? --- # Wallet CLI | Anvil Development Agency Use it when you need to: * Spin up wallets on-demand for backends or minting policies. * Automate tests that require fresh addresses. * Handle keys and addresses straight from the terminal (no GUI). **Security Best Practices & Environment** This tool generates private keys offline. The security of the keys depends **entirely on the environment** where you run the tool. * **For Production Keys (e.g., Treasury, Policy IDs):** To generate keys that will secure significant value, you **should** use a dedicated, **air-gapped machine** that has never been and will never be connected to the internet. Store the output securely offline. * **For Development/Testing:** Using your regular development machine is acceptable for temporary wallets that will not hold significant value. * **NEVER** use a personal or primary wallet's seed phrase with this tool. This avoids exposing your main wallet's keys. ### [](https://dev.ada-anvil.io/developer-tools/wallet-cli#features) Features * **Offline:** No internet needed. * **Script-Ready:** Single binary, perfect for CI/CD or bash scripts. * **Configurable:** 12/24 words, with an optional passphrase for mnemonic wallets. * **Portable:** Use the `--seed` flag to restore an existing wallet, or use a newly generated phrase in other standard wallet apps (Eternl, Lace, etc.). ### [](https://dev.ada-anvil.io/developer-tools/wallet-cli#getting-started) Getting Started You can get the **latest compiled version** from the official releases page: [**Download Latest Release**](https://github.com/Cardano-Forge/cardano-wallet-cli/releases) Alternatively, you can build it directly from the source code. #### [](https://dev.ada-anvil.io/developer-tools/wallet-cli#build-from-source) Build from Source Source code requires [Deno](https://deno.land/) to be installed on your system. Copy git clone https://github.com/Cardano-Forge/cardano-wallet-cli.git cd cardano-wallet-cli/ deno compile --allow-read --allow-write --output cardano-wallet src/mod.ts * * * ### [](https://dev.ada-anvil.io/developer-tools/wallet-cli#wallet-types-explained) Wallet Types Explained This tool can generate two fundamental types of wallets, which in turn determine the kinds of addresses you can create. The type of wallet you generate determines which keys are created and, therefore, which types of addresses you can use. Wallet Type Keys Generated Address Types Available How to Generate **Mnemonic (HD) Wallet** Payment & Staking Keys **Base Address** (for staking) **Enterprise Address** Use the `--mnemonic` or `--seed` flag. **Single Key-Pair Wallet** Payment Key Only **Enterprise Address** only Default behavior (no flags). * **Base Address:** The standard address for users who want to receive funds and participate in staking. It combines both a payment and a staking key. * **Enterprise Address:** A simpler address that only uses a payment key and does not include staking rights. This makes it ideal for specific use cases: * **Exchanges and Custodians:** To hold customer funds without participating in staking, providing transparency. * **Smart Contracts & Scripts:** For programmatic wallets that need to hold funds or native tokens but do not require staking capabilities. * **Automated Services:** For backend systems that generate addresses for payments or other automated tasks. ### [](https://dev.ada-anvil.io/developer-tools/wallet-cli#usage-and-examples) Usage and Examples #### [](https://dev.ada-anvil.io/developer-tools/wallet-cli#command-line-options) Command-Line Options Flag Description Required Default `--name` The base name for the output wallet file (e.g., `my-wallet`). Yes \- `--mnemonic` If present, creates a mnemonic (HD) wallet. No \- `--seed` A 12 or 24-word mnemonic phrase to restore a wallet. No \- `--bits` Set mnemonic length. Use `128` for 12 words, `256` for 24. No `256` `--password` An optional password to secure the mnemonic wallet. No (but strongly recommended) \- #### [](https://dev.ada-anvil.io/developer-tools/wallet-cli#examples) Examples **1\. Create a 24-Word Mnemonic Wallet (Base Address)** Copy ./cardano-wallet --name=main-wallet --mnemonic **2\. Create a 12-Word Mnemonic Wallet (Base Address)** Copy ./cardano-wallet --name=quick-wallet --mnemonic --bits=128 **3\. Restore a Wallet from a Mnemonic Phrase (Base Address)** Copy ./cardano-wallet --name=restored-wallet --seed="your twelve or twenty four word seed phrase goes here" **4\. Create a Single Key-Pair Wallet (Enterprise Address)** This is the default behavior and creates a simple wallet with no staking capabilities. Copy ./cardano-wallet --name=payment-key ### [](https://dev.ada-anvil.io/developer-tools/wallet-cli#output-example) Output Example The tool generates a JSON file containing all keys and addresses. Copy { "skey": "ed25519e_sk18qn7l5w0pspu2uf6pz5qvn392g23fwpn99vrzjmy6j3fxvjrhdvcm8r44jkvmnv2h5uqx06k5x20gcdlhns0d6xzewfzpgtzk25vxmsw2c3nh", "skey_hex": "3827efd1cf0c03c5713a08a8064e25521514b8332958314b64d4a2933243bb598d9c75acaccdcd8abd38033f56a194f461bfbce0f6e8c2cb9220a162b2a8c36e", "pkey": "ed25519_pk1l4k5ydd0nlal4vd3zum2e76wx7hpunz5quq7ntqy3z5fnpk9nm3scy6ad0", "pkey_hex": "fd6d4235af9ffbfab1b11736acfb4e37ae1e4c540701e9ac0488a89986c59ee3", "key_hash": "e9c2caf1072e363420c7a4c84b622284f82a3799d8acf6af0c87ee41", "base_address_preview": "addr_test1qr5u9jh3quhrvdpqc7jvsjmzy2z0s23hn8v2ea40pjr7usfqf0xczeqwhr8k0q556shm2xc5s04cyd36448vz8rnrdtsu6u3gq", "base_address_preprod": "addr_test1qr5u9jh3quhrvdpqc7jvsjmzy2z0s23hn8v2ea40pjr7usfqf0xczeqwhr8k0q556shm2xc5s04cyd36448vz8rnrdtsu6u3gq", "base_address_mainnet": "addr1q85u9jh3quhrvdpqc7jvsjmzy2z0s23hn8v2ea40pjr7usfqf0xczeqwhr8k0q556shm2xc5s04cyd36448vz8rnrdtslvp3yl", "enterprise_address_mainnet": "addr1v85u9jh3quhrvdpqc7jvsjmzy2z0s23hn8v2ea40pjr7usgqjrh84", "enterprise_address_preview": "addr_test1vr5u9jh3quhrvdpqc7jvsjmzy2z0s23hn8v2ea40pjr7usgm6htgs", "enterprise_address_preprod": "addr_test1vr5u9jh3quhrvdpqc7jvsjmzy2z0s23hn8v2ea40pjr7usgm6htgs", "reward_address_mainnet": "stake1uysyhnvpvs8t3nm8s22dgta4rv2g86uzxca26nkpr3e3k4cwpgth2", "reward_address_preview": "stake_test1uqsyhnvpvs8t3nm8s22dgta4rv2g86uzxca26nkpr3e3k4cftzfnh", "reward_address_preprod": "stake_test1uqsyhnvpvs8t3nm8s22dgta4rv2g86uzxca26nkpr3e3k4cftzfnh", "mnemonic": "essence taste already amount black shell neutral amused negative chronic mechanic warm famous clerk zero barely random october pipe antenna glue volume silver donor" } [](https://dev.ada-anvil.io/developer-tools/wallet-cli#disclaimer) Disclaimer ---------------------------------------------------------------------------------- The 2 seed phrases used in this document must not be used in your project. The `mnemonic`, `skey` and `skey_hex` must stay **private at all time**. [PreviousUtility Functions](https://dev.ada-anvil.io/developer-tools/utility-functions) [NextWeb3 Authentication](https://dev.ada-anvil.io/developer-tools/cip8-auth-guide) Last updated 11 days ago Was this helpful? --- # Native Scripts | Anvil Development Agency ### [](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts#introduction) Introduction Native scripts are a fundamental component of the Cardano blockchain that define who has permission to mint or burn assets under a specific policy ID. These scripts create the cryptographic foundation of your NFT or token collection and establish the rules for minting authority. This guide explains how native scripts work, their structure, and best practices for implementation. ### [](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts#what-is-a-native-script) What is a Native Script? A native script is a logical expression that defines the conditions in which assets can be minted or burned under a specific policy ID. The policy ID is derived by hashing the native script, creating a unique identifier for your asset collection on the blockchain. When included in a minting or burning transaction, native scripts are evaluated by the Cardano network to verify that the transaction satisfies all conditions before allowing new assets to be created or burned. For a detailed, step-by-step guide on creating native scripts using the Anvil API, see our [Creating Native Scripts](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts/create-native-script) guide. ### [](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts#native-script-structure) Native Script Structure Native scripts in the Anvil API follow a specific JSON structure that maps to Cardano's native script format. Each script uses a discriminated union with a `type` field that determines the script's behavior: Copy // Base structure for all native scripts { "type": "sig" | "before" | "after" | "all" | "any" | "atLeast", // Additional properties based on the type } The underlying zod schema validates the following structures: Copy // Main Native Script Schema export const nativeScriptSchema = z.discriminatedUnion("type", [\ // Signature requirement\ z.object({ type: z.literal("sig"), keyHash: z.string() }),\ \ // Time constraints\ z.object({ type: z.enum(["before", "after"]), slot: z.number() }),\ \ // Logical AND/OR\ z.object({ \ type: z.enum(["any", "all"]), \ scripts: z.array(/*recursive native scripts*/) \ }),\ \ // At least N of M\ z.object({ \ type: z.literal("atLeast"), \ required: z.number(),\ scripts: z.array(/*recursive native scripts*/) \ }),\ ]); #### [](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts#script-types) Script Types **Signature Scripts** Signature scripts (`sig`) require a specific key to sign the transaction. This is the most common constraint used to ensure only authorized parties can mint tokens: Copy { "type": "sig", "keyHash": "KEY_HASH_HERE" } The `keyHash` is the hash of the verification key that must sign the transaction. **Time Constraints** Time constraints limit minting to specific time windows using slot numbers: Copy { "type": "before", "slot": 100000000 } or Copy { "type": "after", "slot": 50000000 } * `before`: The transaction must be submitted before the specified slot number * `after`: The transaction must be submitted after the specified slot number **Logical Operators** > **In Simple Terms**: `all` means "AND" (everything must be true), `any` means "OR" (at least one thing must be true). So if you want to allow either Alice OR Bob to sign, use `any`. If you need both Alice AND Bob to sign, use `all`. Logical operators combine multiple constraints: Copy { "type": "all", "scripts": [\ // Array of other scripts that must ALL evaluate to true\ ] } or Copy { "type": "any", "scripts": [\ // Array of other scripts where at least ONE must evaluate to true\ ] } **Threshold Requirements** The `atLeast` operator allows you to specify a minimum number of conditions that must be met: Copy { "type": "atLeast", "required": 2, "scripts": [\ // Array of scripts where at least 'required' number must evaluate to true\ ] } This is particularly useful for multi-signature scenarios where you might want to require, for example, at least 2 out of 3 possible signers to authorize minting. ### [](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts#common-native-script-patterns) Common Native Script Patterns #### [](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts#basic-single-signature-policy) Basic Single-Signature Policy Copy { "type": "sig", "keyHash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" } #### [](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts#time-limited-single-signature-policy-recommended-for-nfts) Time-Limited Single-Signature Policy (Recommended for NFTs) Copy { "type": "all", "scripts": [\ {\ "type": "sig",\ "keyHash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"\ },\ {\ "type": "before",\ "slot": 98765432\ }\ ] } #### [](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts#multi-signature-policy-any-of-two-keys) Multi-Signature Policy (Any of Two Keys) Copy { "type": "any", "scripts": [\ {\ "type": "sig",\ "keyHash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"\ },\ {\ "type": "sig",\ "keyHash": "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210"\ }\ ] } #### [](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts#multi-signature-policy-with-threshold-2-of-3-keys-required) Multi-Signature Policy with Threshold (2 of 3 Keys Required) Copy { "type": "atLeast", "required": 2, "scripts": [\ {\ "type": "sig",\ "keyHash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"\ },\ {\ "type": "sig",\ "keyHash": "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210"\ },\ {\ "type": "sig",\ "keyHash": "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"\ }\ ] } ### [](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts#best-practices) Best Practices #### [](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts#security-considerations) Security Considerations 1. **Always Include a Signature Requirement**: While technically optional, you should always include at least one signature constraint in your native scripts for security. A native script without signature requirements allows anyone to mint tokens with your native script ID. 2. **Time-Limited Policies**: For NFT collections, consider implementing time-limited minting windows using both signature and time constraints. This pattern ensures your collection becomes immutable after a certain date, which is important for NFT value and trust. 3. **Multi-Signature Requirements**: For projects with multiple stakeholders, consider using nested logical operators to require multiple approvals. #### [](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts#implications-for-token-operations) Implications for Token Operations **Time-Limited Policies and Token Operations** When using time-limited policies (with a `before` constraint), understand the implications for various operations: 1. **Minting New Tokens**: * Only possible before the specified slot time * Attempting to mint after this time will fail validation * Keep track of your policy's deadline to avoid transaction failures 2. **Burning Tokens**: * Requires the same native script verification as minting * Cannot burn tokens after the time limit expires * To burn tokens, use a negative quantity in your mint request 3. **Metadata Updates**: * On-chain metadata (label 721) cannot be modified after being written * For updatable NFTs, consider using CIP-68 which stores data in datums * Time-limitations affect your ability to issue updated versions of tokens > **Important**: If your use case requires future updates to tokens, avoid time-limited policies or ensure the time window is sufficient for your project's lifecycle. ### [](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts#using-native-scripts-with-the-anvil-api) Using Native Scripts with the Anvil API #### [](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts#when-to-include-native-scripts) When to Include Native Scripts Native scripts are provided via the `preloadedScripts` array in your Anvil API request: * **Required**: When minting tokens with a new policy ID that hasn't been used before * **Optional**: When minting additional tokens with an existing policy ID > **Performance Note**: Including the native script even for existing policies improves performance. Without it, the Anvil API will automatically fetch the script from the blockchain in the background, which adds processing time to your request. #### [](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts#script-verification) Script Verification When you provide a native script, the Anvil API performs several validations: 1. The native script is parsed and its hash is computed 2. The computed hash is compared to the policy ID specified in the `policyId` field 3. If the hashes don't match, the transaction is rejected with a `Policy script and policy Id do not match` error #### [](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts#integration-with-transaction-building) Integration with Transaction Building Native scripts are provided in the `preloadedScripts` array and automatically resolved during the transaction preparation phase. The system: 1. Identifies required policy IDs from mint assets 2. Looks for matching scripts in `preloadedScripts` 3. Falls back to blockchain lookup if not found 4. Validates script hash matches policy ID 5. Uses resolved scripts for transaction building ### [](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts#common-errors-and-troubleshooting) Common Errors and Troubleshooting * **Unable to retrieve script for policy**: The policy doesn't exist on-chain and wasn't provided in `preloadedScripts` * **Policy script and policy ID do not match**: The provided script's hash doesn't match the specified policy ID * **Missing policy scripts**: Required for first-time minting but wasn't included in the request ### [](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts#references) References * [Creating Native Scripts](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/nft-and-ft/create-native-script/README.md) * [Cardano Developers - Native Scripts](https://developers.cardano.org/docs/get-started/cardano-cli/simple-scripts/) * [CIP-25 Specification](https://cips.cardano.org/cip/CIP-25) * [CIP-68 Specification](https://cips.cardano.org/cip/CIP-68) [PreviousNative Assets (NFTs/FTs)](https://dev.ada-anvil.io/guides/nft-and-ft) [NextCreating Native Scripts](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts/create-native-script) Last updated 11 days ago Was this helpful? --- # Metadata | Anvil Development Agency [](https://dev.ada-anvil.io/guides/payload#overview) Overview ------------------------------------------------------------------ On-chain metadata is a powerful feature of the Cardano blockchain that enables storing data within transactions (CIP-25) or datum in UTXOs (CIP-68). The Anvil API provides flexible options for including metadata in your transactions through a streamlined interface. Metadata serves several key purposes in the Cardano ecosystem: * **NFT Information** (CIP-25, CIP-68): Store details about tokens like name, description, and image links * **Transaction Messages** (CIP-20): Include human-readable messages or technical information such as: * Payment references or receipt identifiers * Invoice numbers for business transactions * Short notes explaining the purpose of transfers * DEX transaction identifiers for tracking operations * **Protocol-specific Data**: Support for custom protocols through specialized metadata formats [](https://dev.ada-anvil.io/guides/payload#how-anvil-handles-metadata) How Anvil Handles Metadata ------------------------------------------------------------------------------------------------------ The Anvil API processes metadata through the `meta` array in your transaction payload. Each entry in this array can contain a `label` and `data`. ### [](https://dev.ada-anvil.io/guides/payload#supported-label-mappings) Supported Label Mappings Anvil supports the following predefined friendly labels that map to Cardano metadata specifications: Friendly Label Numeric Label Purpose `"message"` `674` Transaction messages (CIP-20) `"token"` `721` NFT metadata (CIP-25, CIP-68) If you use a friendly label, Anvil will automatically convert it to the corresponding numeric value. ### [](https://dev.ada-anvil.io/guides/payload#metadata-structure) Metadata Structure > **Note**: You can use the predefined friendly labels above, any numeric label, or no label at all. You can use the following structure to define metadata in your transaction payload: Copy // Complete transaction payload with metadata const transactionPayload = { // Other transaction fields like utxos, changeAddress, etc. // Metadata array - this is where you define your metadata entries meta: [\ // Option 1: Using a predefined friendly string label\ {\ label: "message", // Must be one of: "message" or "token"\ data: { \ msg: ["This is a transaction message"] \ }\ },\ \ // Option 2: Using a numeric label\ {\ label: 674, // Can be any number (674, 721, or custom numeric value)\ data: { \ msg: ["This is another transaction message"] \ }\ },\ \ // Option 3: No label, direct structure\ {\ data: {\ 674: { \ msg: ["This is a message with direct structure"] \ }\ }\ }\ ] }; Behind the scenes, Anvil employs sophisticated metadata handling with these features: * **Friendly Labels**: Convert human-readable labels to their numeric counterparts (e.g., `"token"` → `721`) * **Structure Merging**: Similar metadata entries are intelligently combined * **Automatic String Chunking**: Long strings exceeding Cardano's 64-byte limit are split into arrays * **Format Validation**: Ensures compliance with Cardano transaction specifications Example: How Anvil API does Structure Merging[](https://dev.ada-anvil.io/guides/payload#example-how-anvil-api-does-structure-merging) When multiple metadata entries use the same label, Anvil merges them instead of overwriting or duplicating: Copy // Your input: Two separate metadata entries const data = { meta: [\ {\ label: "token",\ data: { \ "policyId_placeholder": { \ asset1: { name: "Asset1", description: "First asset" } \ } \ }\ },\ {\ label: "token",\ data: { \ "policyId_placeholder": { \ asset2: { name: "Asset2", description: "Second asset" } \ } \ }\ }\ ] }; // Transformed by Anvil for on-chain storage: Combined into a single structure { "721": { "policyId_placeholder": { "asset1": { "name": "Asset1", "description": "First asset" }, "asset2": { "name": "Asset2", "description": "Second asset" } } } } This allows you to build complex metadata incrementally from different parts of your application. Example: How Anvil API does Automatic String Chunking[](https://dev.ada-anvil.io/guides/payload#example-how-anvil-api-does-automatic-string-chunking) When a string exceeds Cardano's 64-byte limit, Anvil automatically converts it to an array of shorter strings: Copy // Your input { data: { description: "This is a very long description that exceeds the 64-byte limit imposed by Cardano transaction metadata specifications..." } } // Transformed by Anvil for on-chain storage { data: { description: [\ "This is a very long description that exceeds the 64-byte limit ",\ "imposed by Cardano transaction metadata specifications..."\ ] } } [](https://dev.ada-anvil.io/guides/payload#working-with-cip-25-and-cip-68-metadata-label-721) Working with CIP-25 and CIP-68 Metadata (Label 721) ------------------------------------------------------------------------------------------------------------------------------------------------------ > **IMPORTANT WARNING**: Metadata is only one part of the NFT minting process. For proper CIP-25 and CIP-68 implementation, you should follow the complete minting documentation. Attempting to implement NFT minting by manually constructing transactions with just the metadata structure below will lead to complexity and likely errors. Please refer to the [CIP-25 NFT Minting Guide](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25) > or [CIP-68 NFT Minting Guide](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-68) > for the complete implementation. CIP-25 established the original NFT metadata standard for Cardano, while CIP-68 expanded on this with additional capabilities for evolving NFTs. Most applications support both standards with the following characteristics: * **Hierarchical Structure**: Organized by policy ID and asset name * **Rich Content Support**: Name, description, image, and other media attributes * **Extensibility**: Support for custom attributes beyond the core specification > **Note on CIP-68 Implementation**: While both CIP-25 and CIP-68 use the same metadata structure under label `721`, CIP-68 requires additional implementation steps beyond metadata definition. CIP-68 uses a two-token model (reference NFT label `100` + user token label `222`) with the metadata stored in a datum. For complete CIP-68 implementation, see the [CIP-68 NFT Minting Guide](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-68) > . [CIP-25 Specification](https://cips.cardano.org/cip/CIP-25) | [CIP-68 Specification](https://cips.cardano.org/cip/CIP-68) Anvil provides three equivalent ways to define CIP-25/CIP-68 metadata: 1. **Using the friendly** `**"token"**` **label** 2. **Using the numeric** `**721**` **label directly or any other numeric label** 3. **Using no label with direct structure** All three approaches result in identical on-chain metadata. ### [](https://dev.ada-anvil.io/guides/payload#example) Example Copy const asset = { metadata: { name: "Placeholder", image: [\ "https://ada-anvil.s3.ca-central-1.amazonaws.com/",\ "logo_pres_V2_3.png",\ ], mediaType: "image/png", description: "Testing CIP-25 using anvil API", }, }; const data = { meta: [\ // Option 1: Using friendly "token" label\ {\ label: "token",\ data: {\ "policyId_placeholder": {\ "Placeholder label token": {\ ...asset.metadata,\ name: "Placeholder label token",\ },\ },\ },\ },\ // Option 2: Using numeric 721 label\ {\ label: 721,\ data: {\ "policyId_placeholder": {\ "Placeholder label 721": {\ ...asset.metadata,\ name: "Placeholder label 721",\ },\ },\ },\ },\ // Option 3: No label with direct structure\ {\ data: {\ 721: {\ "policyId_placeholder": {\ "Placeholder no label": {\ ...asset.metadata,\ name: "Placeholder no label",\ },\ },\ },\ },\ },\ ], changeAddress: "CHANGE_ADDRESS", // Replace with your wallet address utxos: "UTXOS", // Replace with your UTXOs }; [](https://dev.ada-anvil.io/guides/payload#working-with-cip-20-transaction-messages-label-674) Working with CIP-20 Transaction Messages (Label 674) -------------------------------------------------------------------------------------------------------------------------------------------------------- [CIP-20](https://cips.cardano.org/cip/CIP-20) defines a standard for including human-readable messages in transactions using label `674`. These messages are ideal for providing context, receipts, or references within transactions. Anvil provides four equivalent ways to define CIP-20 transaction messages: 1. **Using the friendly** `**"message"**` **label** - Simplest approach that automatically converts to label `674` on-chain 2. **Using the numeric** `**674**` **label directly** - Same outcome as the friendly label approach 3. **Using no label with direct structure** - Maximum control over the final structure 4. **Using the simplified** `**message**` **property** - A special Anvil shorthand that handles all the formatting All approaches result in correctly formatted message metadata on-chain, with each message appearing as an entry in the `msg` array under label `674`. ### [](https://dev.ada-anvil.io/guides/payload#example-1) Example Copy // Transaction build payload with all four message approaches const data = { meta: [\ // Option 1: Using friendly "message" label\ {\ label: "message",\ data: {\ msg: ["This is the first approach with the friendly label"]\ }\ },\ // Option 2: Using numeric 674 label\ {\ label: 674,\ data: {\ msg: ["This is the second approach with the numeric label"]\ }\ },\ // Option 3: No label with direct structure\ {\ data: {\ 674: {\ msg: ["This is the third approach with direct structure"]\ }\ }\ },\ ], // Option 4: Using the simplified message property message: ["This is the fourth approach with the message property"], changeAddress: "CHANGE_ADDRESS", // Replace with your wallet address utxos: "UTXOS", // Replace with your UTXOs }; [](https://dev.ada-anvil.io/guides/payload#conclusion) Conclusion ---------------------------------------------------------------------- The Anvil API provides a flexible system for incorporating metadata in your Cardano transactions: * Use the **CIP-25/CIP-68 format** (label `721`) for NFT metadata * Use the **CIP-20 format** (label `674`) for transaction messages * Choose the definition approach that best fits your workflow - friendly labels or direct numeric labels In all cases, the resulting on-chain metadata follows the respective CIP standards, ensuring compatibility with wallets, explorers, and applications that interact with the Cardano blockchain. ### [](https://dev.ada-anvil.io/guides/payload#further-resources) Further Resources [Mint NFTs (CIP-25)](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-25) [Mint NFTs (CIP-68)](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-68) [PreviousDeno & Fetch](https://dev.ada-anvil.io/guides/transaction/create-transaction-with-metadata-cip-20/node-and-fetch) [NextNative Assets (NFTs/FTs)](https://dev.ada-anvil.io/guides/nft-and-ft) Last updated 11 days ago Was this helpful? --- # Web3 Authentication | Anvil Development Agency To "Sign in with Cardano," users cryptographically prove control of their stake address by signing a unique challenge with their wallet’s private key via CIP-8. This guide shows how to integrate Weld for wallet interactions, craft and sign challenges, verify signatures on your server, and extend authentication with on-chain business logic. (i.e. NFT gating, checking delegation status, wallet whitelisting, etc...) * * * [](https://dev.ada-anvil.io/developer-tools/cip8-auth-guide#core-components) Core Components ------------------------------------------------------------------------------------------------- * **FrontEnd UI (Browser):** Initiates the request. We recommend a library like [Weld](https://github.com/cardano-forge/weld) to handle different wallet APIs. * **Wallet UI (e.g., Lace, Vespr, Eternl):** Holds the user's private key and creates the signature. * **Backend Server:** Verifies the signature to authenticate the user. * * * * * * [](https://dev.ada-anvil.io/developer-tools/cip8-auth-guide#authentication-flow) Authentication Flow --------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/developer-tools/cip8-auth-guide#id-1.-frontend-ui-create-and-sign-payload) 1\. FrontEnd UI: Create and Sign Payload 1. **Get Address:** Retrieve the user's stake address from the connected wallet. 2. **Create Payload:** Construct a unique message including the stake address to prevent replay attacks (e.g., `"Auth for MyDApp: stake1u..."`). 3. **Sign Payload:** Use the wallet's `signData` function to sign the payload. The wallet returns a `COSE_Sign1` structure (the proof). 4. **Send Proof:** Send the `COSE_Sign1` proof and the user's public key to the backend server. ### [](https://dev.ada-anvil.io/developer-tools/cip8-auth-guide#id-2.-backend-server-verify-proof-and-apply-business-logic) 2\. Backend Server: Verify Proof & Apply Business Logic The server receives the `COSE_Sign1` proof, which contains a **protected header** (with the user's address), the original **payload**, and the **signature**. It must perform these checks: * **Check 1: Key Matches Address** * Hash the public key received from the client. * Extract the public key hash from the `address` in the proof's protected header. * **Verify they match.** This confirms the public key belongs to the address in the proof. * **Check 2: Signature is Valid** * Using a crypto library, verify the `signature` against the `protected header` and `payload`. * This confirms the proof was signed by the user's private key and hasn't been tampered with. * **Check 3: Business Logic Hooks (Optional)** With the address cryptographically verified, you can now implement custom business logic. Examples include: * **Token/NFT Gating:** Query a Cardano API (e.g., Blockfrost, Koios) to confirm the wallet's UTXOs contain a required asset policy ID or fingerprint. * **Stake & Delegation Checks:** Use the stake address to check delegation status, stake pool, or total ADA balance against your requirements. * **Wallet Whitelists/Blacklists:** For higher security applications, you might maintain a list of approved stake addresses. * **Session Management:** If you're using a session-based authentication system, set a secure cookie on the client to grant access. * **Replay Protection:** The payload should include a `timestamp`. On the server, enforce a time-to-live (TTL) to reject signatures that are too old. * **Domain Separation:** The payload should include the `uri` of the request origin and a specific `action` description. The server must validate these fields to prevent a signature from one domain being reused on another (phishing) and to ensure the user approved the specific action intended. Implementation Details: Full Backend Verification[](https://dev.ada-anvil.io/developer-tools/cip8-auth-guide#implementation-details-full-backend-verification) While working directly with Cardano's serialization libraries (CSL) can have a steep learning curve, you don't need to be an expert to implement secure authentication. The function below encapsulates the entire CIP-8 verification process. We'll use this similar functions in the full examples provided later, giving you a practical, production-ready tool for validating signature packages. Copy import { COSESign1, COSEKey, Label, Int, BigNum } from '@emurgo/cardano-message-signing-asmjs'; import { Address, Ed25519Signature, PublicKey, RewardAddress, } from '@emurgo/cardano-serialization-lib-asmjs'; export async function verifyCIP8Signature( sigData: { signature: string; key?: string }, options: { expectedDomain: string; expectedAction: string } ): Promise<{ isValid: boolean; stakeAddress: string | null }> { try { if (typeof sigData !== 'object' || !sigData.signature || !sigData.key) { console.error('Invalid signature data format'); return { isValid: false, stakeAddress: null }; } // 1. Decode the signature and key const decoded = COSESign1.from_bytes(hexToBytes(sigData.signature)); const key = COSEKey.from_bytes(hexToBytes(sigData.key)); const headermap = decoded.headers().protected().deserialized_headers(); const address = Address.from_bytes(headermap.header(Label.new_text('address')).to_bytes()); const pubKeyBytes = key.header(Label.new_int(Int.new_negative(BigNum.from_str('2')))).as_bytes(); const publicKey = PublicKey.from_bytes(pubKeyBytes); // 2. Verify the cryptographic signature const signatureBytes = Ed25519Signature.from_bytes(decoded.signature()); const signedData = decoded.signed_data().to_bytes(); const isSignatureValid = publicKey.verify(signedData, signatureBytes); if (!isSignatureValid) throw new Error('Cryptographic signature is invalid.'); // 3. Parse and validate the payload for security const payloadBytes = decoded.payload(); if (!payloadBytes) throw new Error('No payload in signature'); const payload = JSON.parse(new TextDecoder().decode(payloadBytes)); // 4. Replay Protection: Check if the timestamp is recent const TTL = 5 * 60 * 1000; // 5 minutes const isTimestampValid = Date.now() - payload.timestamp < TTL; if (!isTimestampValid) throw new Error('Timestamp is too old, possible replay attack.'); // 5. Domain Separation: Check URI and action to prevent phishing const isDomainValid = payload.uri === options.expectedDomain; const isActionValid = payload.action === options.expectedAction; if (!isDomainValid || !isActionValid) throw new Error('Domain or action mismatch.'); // 6. Final verification: Extract stake address const signerStakeAddrBech32 = RewardAddress.from_address(address)?.to_address()?.to_bech32(); if (!signerStakeAddrBech32) throw new Error('Could not extract stake address.'); console.log('CIP-8 verification successful for:', signerStakeAddrBech32); return { isValid: true, stakeAddress: signerStakeAddrBech32 }; } catch (error) { console.error('CIP-8 verification error:', error); return { isValid: false, stakeAddress: null }; } } export function hexToBytes(hex: string): Uint8Array { const bytes = new Uint8Array(Math.floor(hex.length / 2)); for (let i = 0; i < bytes.length; i++) { const hexByte = hex.substring(i * 2, i * 2 + 2); bytes[i] = parseInt(hexByte, 16); } return bytes; } * * * ### [](https://dev.ada-anvil.io/developer-tools/cip8-auth-guide#id-3.-frontend-ui-render-authenticated-ui-state) 3\. FrontEnd UI: Render Authenticated UI State 1. **Render Authenticated UI State:** Once the server has verified the signature, it can grant access to the user's authenticated UI state. * * * [](https://dev.ada-anvil.io/developer-tools/cip8-auth-guide#full-examples) Full Examples --------------------------------------------------------------------------------------------- [https://github.com/Cardano-Forge/anvil-api/blob/main/docs/developer-tools/guides/transaction/README.md](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/developer-tools/guides/transaction/README.md) [PreviousWallet CLI](https://dev.ada-anvil.io/developer-tools/wallet-cli) [NextBuild Transaction](https://dev.ada-anvil.io/wayup/wayup) Last updated 11 days ago Was this helpful? --- # Utility Functions | Anvil Development Agency Anvil provides several utility endpoints to simplify common operations when building Cardano applications. These endpoints handle tasks like address parsing, native script analysis, and time-slot conversions. See full examples of the utility functions in the [examples repository](https://github.com/Cardano-Forge/anvil-api-examples/blob/main/utils/shared.ts) . [](https://dev.ada-anvil.io/developer-tools/utility-functions#available-utility-endpoints) Available Utility Endpoints --------------------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/developer-tools/utility-functions#address-utilities) Address Utilities #### [](https://dev.ada-anvil.io/developer-tools/utility-functions#post-utils-addresses-parse) POST `/utils/addresses/parse` **What It Does:** * Extracts payment and stake key hashes from Cardano addresses **Parameters:** * `address`: Bech32 or hex encoded address **Returns:** * `payment`: Payment key hash (hex encoded) * `stake`: Optional stake key hash (hex encoded) **Common Use Case:** * Getting the payment key hash from a wallet address for native scripts * Extracting stake key hash for delegation operations TypeScript Example: Parse an address[](https://dev.ada-anvil.io/developer-tools/utility-functions#typescript-example-parse-an-address) Copy async function getPaymentCredentialFromAddress(address) { const response = await fetch('https://preprod.api.ada-anvil.app/v2/services/utils/addresses/parse', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Api-Key': 'YOUR_API_KEY' }, body: JSON.stringify({ address: address }) }); return await response.json(); } // Sample Output // { // "payment": "addr1egrtarthsndgre...", // "stake": "stake1grhyjajuwsfbudh...", // } ### [](https://dev.ada-anvil.io/developer-tools/utility-functions#native-script-utilities) Native Script Utilities #### [](https://dev.ada-anvil.io/developer-tools/utility-functions#post-utils-native-scripts-parse) POST `/utils/native-scripts/parse` **What It Does:** * Calculates policy ID and returns policy ID from the provided native script **Parameters:** * `script`: Hex encoded native script **Returns:** * `policyId`: The hash of the script (policy ID) **Common Use Case:** * Extracting a policy ID from a minting script * Verifying script properties before transaction submission TypeScript Example: Get policy ID from a native script[](https://dev.ada-anvil.io/developer-tools/utility-functions#typescript-example-get-policy-id-from-a-native-script) Copy async function getPolicyId(nativeScriptHex) { const response = await fetch('https://preprod.api.ada-anvil.app/v2/services/utils/native-scripts/parse', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Api-Key': 'YOUR_API_KEY' }, body: JSON.stringify({ script: nativeScriptHex }) }); const result = await response.json(); return result.policyId; } // Sample Output // { // "policyId": "df37e17a949b061c15c53c485609652a6516315de90a88b611c03a9c" // } #### [](https://dev.ada-anvil.io/developer-tools/utility-functions#post-utils-native-scripts-serialize) POST `/utils/native-scripts/serialize` **What It Does:** * Creates a native script from it's JSON schema definition **Parameters:** * `schema`: The JSON representation of the script **Returns:** * `policyId`: The hash of the script (policy ID) * `script`: The CBOR hex-encoded script TypeScript Example: Serialize a native script[](https://dev.ada-anvil.io/developer-tools/utility-functions#typescript-example-serialize-a-native-script) Copy async function serializeNativeScript(schema) { const response = await fetch('https://preprod.api.ada-anvil.app/v2/services/utils/native-scripts/serialize', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Api-Key': 'YOUR_API_KEY' }, body: JSON.stringify({ schema: schema }) }); const result = await response.json(); return result; } // Sample Output // { // "policyId": "df37e17a949b061c15c53c485609652a6516315de90a88b611c03a9c", // "script": "8201828200581c3910f0db63599cd9cb1484d5591491629470761a89cde0473e4b94f682051a06a60080" // } ### [](https://dev.ada-anvil.io/developer-tools/utility-functions#network-time-utilities) Network Time Utilities #### [](https://dev.ada-anvil.io/developer-tools/utility-functions#post-utils-network-slot-to-time) POST `/utils/network/slot-to-time` **What It Does:** * Converts a Cardano slot number to a timestamp **Parameters:** * `slot`: Absolute slot number **Returns:** * `time`: Unix timestamp in milliseconds (UTC) **Common Use Case:** * Converting slot-based time locks to human-readable dates * Displaying blockchain time information to users TypeScript Example: Convert a slot to a timestamp[](https://dev.ada-anvil.io/developer-tools/utility-functions#typescript-example-convert-a-slot-to-a-timestamp) Copy async function slotToTimestamp(slotNumber) { const response = await fetch('https://preprod.api.ada-anvil.app/v2/services/utils/network/slot-to-time', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Api-Key': 'YOUR_API_KEY' }, body: JSON.stringify({ slot: slotNumber }) }); const result = await response.json(); // Returns Unix timestamp in milliseconds return new Date(result.time); } #### [](https://dev.ada-anvil.io/developer-tools/utility-functions#post-utils-network-time-to-slot) POST `/utils/network/time-to-slot` **What It Does:** * Returns the enclosing slot of the provided timestamp **Parameters:** * `time`: Unix timestamp in milliseconds (UTC) **Returns:** * `slot`: Absolute slot number **Common Use Case:** * Setting expiration times for minting policies * Creating time-bounded transactions TypeScript Example: Convert a date to a slot[](https://dev.ada-anvil.io/developer-tools/utility-functions#typescript-example-convert-a-date-to-a-slot) Copy async function dateToSlot(date) { const response = await fetch('https://preprod.api.ada-anvil.app/v2/services/utils/network/time-to-slot', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Api-Key': 'YOUR_API_KEY' }, body: JSON.stringify({ time: date.getTime() // Unix timestamp in milliseconds }) }); const result = await response.json(); return result.slot; } [](https://dev.ada-anvil.io/developer-tools/utility-functions#best-practices) Best Practices ------------------------------------------------------------------------------------------------- 1. **Environment Awareness** * Remember that slot calculations differ between Mainnet, Preprod, and Preview * Always use the appropriate network endpoint for your target environment 2. **Error Handling** * Always check for error responses from utility endpoints * Handle edge cases appropriately in your application logic 3. **Caching** * Consider caching results from utility functions when appropriate * This can reduce API calls and improve application performance [PreviousFund Unlocking](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-unlocking) [NextWallet CLI](https://dev.ada-anvil.io/developer-tools/wallet-cli) Last updated 11 days ago Was this helpful? --- # Overview | Anvil Development Agency The Tournament Builder is a premium feature developed for Forge Digital Ventures. If you would like to enable this functionality with your Anvil API access, please contact the Anvil support team. [](https://dev.ada-anvil.io/tournament-builder/overview#overview) Overview ------------------------------------------------------------------------------- The Tournament Builder is a Cardano-based solution for running transparent, on-chain tournaments. It supports customizable brackets, entry fees, and payout structures, with tournament records stored as verifiable NFTs. On-chain actions like registration and prize distribution are automated by smart contracts, while gameplay and rules remain flexible and off-chain—managed by organizers. This ensures both transparency and ease of integration. For a better understanding of the Anvil API transaction flow, please refer to the [Transaction Overview](https://dev.ada-anvil.io/guides/transaction) documentation. In particular, you need to have an understanding of the transaction submission process. Every transaction generated by the tournament builder must be signed and submitted through the Anvil API. [](https://dev.ada-anvil.io/tournament-builder/overview#smart-contract-and-api-architecture) Smart Contract & API Architecture ----------------------------------------------------------------------------------------------------------------------------------- **Key Concepts:** 1. **Simple, Consistent API Endpoints** * All tournament actions—creating tournaments, registering participants, adding players, and submitting transactions—are exposed as clear, RESTful API endpoints. * Endpoints are designed for ease of use: [**Tournament Management**](https://dev.ada-anvil.io/tournament-builder/tournament) * `POST /tournaments` — Create a new tournament * `GET /tournaments` — List all tournaments * `GET /tournaments/{id}` — Get tournament info * `POST /tournaments/{id}/participants` — Add participants to a tournament * `PATCH /tournaments/{id}/settle` — Settle tournament and distribute prizes [**Registration Management**](https://dev.ada-anvil.io/tournament-builder/registration) * `POST /tournaments/{id}/register` — Register as a participant * `GET /tournaments/{id}/registrations` — List all registrations for a tournament * `POST /tournaments/{id}/unregister` — Cancel a registration **Transaction Submission** * `POST /tournaments/submit-tx` — Submit signed transactions for all tournament operations 2. **Dual Validator (Plutus Script) Architecture** * **Tournament Validator:** * Handles tournament creation, participant management, and payout logic. * Mints a unique NFT (CIP-25) for each tournament, which acts as its on-chain identity. * **Registration Validator:** * Manages individual registrations, ensuring only valid participants are added. 3. **NFT-Based Tournament Representation** * Each tournament is a unique NFT, minted at creation. * Tournament configuration and state are stored in the datum of the NFT’s UTXO. * NFTs use the CIP-25 metadata standard for broad wallet and ecosystem compatibility. 4. **EUTXO Transaction Model** * All actions are UTXO-based: tournament and registration records are UTXOs managed by validators. * Transactions are deterministic and can be validated off-chain before submission, reducing surprises and increasing reliability. 5. **Transparent Fee Management** * Entry fees, protocol fees, and per-participant fees are all handled on-chain by the smart contract. * Refunds and payouts are enforced by validator logic, ensuring fairness. [](https://dev.ada-anvil.io/tournament-builder/overview#why-this-approach) Why This Approach? -------------------------------------------------------------------------------------------------- * **Simplicity:** The API hides blockchain complexity. Developers interact with intuitive endpoints and clear request/response formats. * **Security & Transparency:** All critical actions are enforced by on-chain logic and are fully auditable. * **Flexibility:** Organizers retain control over tournament rules and flow, while the contract guarantees fair registration and prize handling. [](https://dev.ada-anvil.io/tournament-builder/overview#documentation-resources) Documentation Resources ------------------------------------------------------------------------------------------------------------- Before diving into the API, we recommend reviewing the integration flow and fee structure documentation to understand the complete tournament process. ### [](https://dev.ada-anvil.io/tournament-builder/overview#core-documentation) Core Documentation [Integration Flow](https://dev.ada-anvil.io/tournament-builder/integration-flow) [Fee Structure](https://dev.ada-anvil.io/tournament-builder/fee-structure) ### [](https://dev.ada-anvil.io/tournament-builder/overview#api-reference) API Reference [Tournament](https://dev.ada-anvil.io/tournament-builder/tournament) [Registration](https://dev.ada-anvil.io/tournament-builder/registration) [PreviousGet Collection Assets](https://dev.ada-anvil.io/wayup/collection-information/get-collection-assets) [NextIntegration Flow](https://dev.ada-anvil.io/tournament-builder/integration-flow) Last updated 1 month ago Was this helpful? --- # Submit | Anvil Development Agency [](https://dev.ada-anvil.io/tournament-builder/submit#submitting-signed-transactions) Submitting Signed Transactions ------------------------------------------------------------------------------------------------------------------------- **Endpoint**: `POST /tournaments/submit-tx` **CRITICAL**: This endpoint is required to complete all Tournament operations. Each transaction-generating endpoint (tournament creation, registration, adding participants, settlement) returns an unsigned transaction that must be signed and submitted using this endpoint. **Request Parameters**: * `transaction` (string): Hex-encoded signed transaction * `signatures` (string\[\], optional): Array of additional signatures to add to the transaction **Response**: * `txHash` (string): Transaction hash of the successfully submitted transaction Transactions must be properly signed before submission. Unsigned or improperly signed transactions will be rejected by the Cardano network. **Technical Notes**: * All tournament operations follow a two-step process: 1. Generate an unsigned transaction using one of the endpoints 2. Sign the transaction and submit it using this endpoint * Multiple signatures can be combined in a single submission * The transaction hash returned can be used to track the transaction on the blockchain **Example (Node.js with fetch)**: Copy async function submitTransaction(signedTransaction) { const apiUrl = 'https://preprod.api.ada-anvil.app/v2/services/tournaments/submit-tx'; const requestData = { transaction: signedTransaction, // Optional additional signatures // signatures: ["signature1", "signature2"] }; const response = await fetch(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(requestData), }); const data = await response.json(); console.log(`Transaction submitted successfully with hash: ${data.txHash}`); return data.txHash; } For more details on transaction creation, signing, and the complete Anvil API transaction process, see the [Transaction Overview](https://dev.ada-anvil.io/guides/transaction) documentation. [PreviousRegistration](https://dev.ada-anvil.io/tournament-builder/registration) [NextFee Structure](https://dev.ada-anvil.io/tournament-builder/fee-structure) Last updated 1 month ago Was this helpful? --- # End-to-End Next.js Escrow Guide | Anvil Development Agency [](https://dev.ada-anvil.io/guides/smart-contract/escrow#introduction) Introduction ---------------------------------------------------------------------------------------- This guide walks you through building a **Cardano escrow application** using Next.js and the Weld wallet connector. You'll create a web application that demonstrates the practical application of Cardano smart contracts through a real-world escrow use case. ![Cardano Escrow Application Interface](https://dev.ada-anvil.io/~gitbook/image?url=https%3A%2F%2F2837083171-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fi5SfFhLFp4rKhwQyZN9d%252Fuploads%252Fgit-blob-6c8e4f511e3fc564d5a820497d6b755f79c10c3d%252Fnextjs-smart-contract-escrow-finished.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=8b16d3a2&sv=2) Cardano Escrow Application with wallet integration and transaction monitoring If you have not already, make sure you have a deployed [Aiken's Hello World](https://aiken-lang.org/example--hello-world/basics) smart contract and have a validator hash handy. See the below guides for more details: [Smart Contracts](https://dev.ada-anvil.io/guides/smart-contract) [Blueprint Management (CIP-57)](https://dev.ada-anvil.io/guides/smart-contract/blueprint-management) [](https://dev.ada-anvil.io/guides/smart-contract/escrow#user-journey-through-smart-contract-interaction) User Journey Through Smart Contract Interaction -------------------------------------------------------------------------------------------------------------------------------------------------------------- This application demonstrates the complete lifecycle of smart contract interaction on Cardano: 1. **Connect Wallet**: Users connect their Cardano wallet to establish their blockchain identity 2. **Lock Funds**: Users lock ADA in a smart contract, specifying themselves as the future recipient 3. **Monitor Status**: Users track the status of their locked funds in real-time 4. **Unlock Funds**: When ready, users reclaim their funds by meeting the contract's conditions Through this process, users experience firsthand how Cardano's eUTXO model and validator scripts work together to create secure, programmable transactions. [](https://dev.ada-anvil.io/guides/smart-contract/escrow#real-world-applications) Real-World Applications -------------------------------------------------------------------------------------------------------------- The principles demonstrated in this escrow application can be extended to build a variety of real-world solutions: * **Marketplace Escrow**: Hold buyer funds until the seller confirms delivery of goods or services * **Rental Security Deposits**: Lock tenants' deposits and release after lease terms are met * **Milestone Payments**: Release funds to contractors upon completion of deliverables * **Token Vesting**: Gradually unlock tokens to team members based on a schedule * **Shared Savings**: Create group savings where funds unlock only when a goal is reached [](https://dev.ada-anvil.io/guides/smart-contract/escrow#the-escrow-contract) The Escrow Contract ------------------------------------------------------------------------------------------------------ ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow#what-youll-build) What You'll Build With the Cardano Escrow application, users can: * Connect any [Weld](https://github.com/Cardano-Forge/weld) supported Cardano wallet (Eternl, Lace, etc.) * Lock ADA at a script address with owner information stored in the datum * Monitor transaction status in real-time * Unlock funds when ready * Signature verification ensures only the rightful owner can access funds * Use the special message from your redeemer ("Hello, World!") to unlock funds. This message is required by the smart contract in order to spend the locked funds. ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow#smart-contract-overview) Smart Contract Overview This application uses a Hello World smart contract to create a secure escrow by enforcing two simple conditions: 1. The transaction must include the specific message "Hello, World!" in the `redeemer` field 2. The transaction must be signed by the owner's key specified in the `datum` When both conditions are met, the validator returns `true` and the locked funds can be spent. Expand for technical details about the Smart Contract[](https://dev.ada-anvil.io/guides/smart-contract/escrow#expand-for-technical-details-about-the-smart-contract) **How Cardano Validators Work** At their core, all Cardano validators function as boolean predicates - they evaluate to either `True` or `False`. A UTXO can only be spent when its validator returns `True`. If the validator returns `False` or fails with an error, the transaction is rejected. Thanks to Cardano's smart contract model, this simple Hello World contract can be repurposed for a secure escrow application. As long as our validator returns true because the conditions are met, our transaction will be accepted by the network and the funds will be unlocked. **Contract Implementation** Copy use aiken/collection/list use aiken/crypto.{VerificationKeyHash} use cardano/transaction.{OutputReference, Transaction} pub type Datum { owner: VerificationKeyHash, } pub type Redeemer { msg: ByteArray, } validator hello_aiken { spend( datum: Option, redeemer: Redeemer, _own_ref: OutputReference, self: Transaction, ) { expect Some(Datum { owner }) = datum // Condition 1: Check if message matches let must_say_hello = redeemer.msg == "Hello, World!" // Condition 2: Check if signed by owner let must_be_signed = list.has(self.extra_signatories, owner) // Both conditions must be true (boolean AND) must_say_hello && must_be_signed } else(_) { fail } } **Key Technical Concepts** Cardano's smart contracts operate through three essential components: 1. **Validator Script**: The on-chain logic that determines when funds can be spent (in our case, the deployed Hello World smart contract) 2. **Datum**: Data attached to UTXOs when funds are locked (in our case, the owner's public key hash) 3. **Redeemer**: Transaction-specific data provided when attempting to spend a UTXO (our "Hello, World!" message) The validator hash from the deployed smart contract is stored as `ESCROW_VALIDATOR_HASH` in the application's environment variables and used to: * Create the script address where funds are locked * Identify the correct validator during transaction building * Verify that funds are being spent from the correct contract [](https://dev.ada-anvil.io/guides/smart-contract/escrow#building-the-application) Building the Application ---------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow#prerequisites) Prerequisites Before starting, ensure you have: * Node.js 18+ installed * Basic familiarity with React and Next.js * A Cardano wallet with testnet ADA (Eternl, Lace, etc.) See the [testnet faucet](https://docs.cardano.org/cardano-testnets/tools/faucet) if you need testnet ADA. * An Anvil API key (for transaction building and submission) * A deployed Hello World smart contract with the validator hash. ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow#step-by-step-guide) Step-by-Step Guide This tutorial is divided into six progressive modules, each focusing on a specific aspect of the escrow application: 1. [**Project Setup**](https://dev.ada-anvil.io/guides/smart-contract/escrow/setup) * Initialize a Next.js project * Set up essential dependencies and project structure 2. [**Wallet Integration**](https://dev.ada-anvil.io/guides/smart-contract/escrow/wallet-integration) * Connect to Cardano wallets using the Weld library * Implement wallet connection and state management 3. [**Fund Locking**](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-locking) * Create the interface to lock funds in the smart contract * Build and submit lock transactions via Anvil API 4. [**Transaction Dashboard**](https://dev.ada-anvil.io/guides/smart-contract/escrow/transaction-dashboard) * Develop a dashboard to monitor transaction status * Implement transaction history storage and retrieval 5. [**Real-time Updates**](https://dev.ada-anvil.io/guides/smart-contract/escrow/realtime-updates) * Add real-time Cardano monitoring through webhooks * Update transaction status as blockchain state changes 6. [**Fund Unlocking**](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-unlocking) * Implement the unlock functionality to complete the escrow cycle * Create the redeemer with the specific message and handle signing ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow#best-practices) Best Practices * **Security**: Never expose API keys or private credentials in client-side code * **Testing**: Always test your application on testnet before deploying to mainnet * **Error Handling**: Implement comprehensive error handling for all blockchain operations * **Units**: Remember that 1 ADA = 1,000,000 Lovelace when calculating amounts [PreviousHello World Smart Contract](https://dev.ada-anvil.io/guides/smart-contract/hello-world-smart-contract) [NextSetup](https://dev.ada-anvil.io/guides/smart-contract/escrow/setup) Last updated 2 months ago Was this helpful? --- # Smart Contract Utilities | Anvil Development Agency Smart Contract Utilities provides endpoints for interacting with Plutus scripts (also known as validators). You can use these endpoints to derive addresses, apply parameters, and handle data serialization. Endpoint Description `GET /validators/{hash}/address` Derives the script address from a given script hash. `POST /validators/{hash}/apply-params` Applies parameters to a script to generate a new, parameterized script hash. `POST /validators/{hash}/parse` Parses a CBOR hex-encoded datum or redeemer into a human-readable JSON object. `POST /validators/{hash}/serialize` Serializes a JSON object into a CBOR hex-encoded datum or redeemer string. [](https://dev.ada-anvil.io/guides/smart-contract/smart-contract-utilities#available-endpoints) Available Endpoints ------------------------------------------------------------------------------------------------------------------------ ### [](https://dev.ada-anvil.io/guides/smart-contract/smart-contract-utilities#get-script-address) Get Script Address `**GET /validators/{hash}/address**` Derives the script address from a given script hash. * **Path Parameters**: * `hash` (string): The hex-encoded hash of the Plutus script. * **Query Parameters**: * `stake` (string, optional): A stake key hash to associate with the address. TypeScript Example: Get a script address from its hash[](https://dev.ada-anvil.io/guides/smart-contract/smart-contract-utilities#typescript-example-get-a-script-address-from-its-hash) Copy async function getScriptAddress(scriptHash: string) { const response = await fetch(`https://preprod.api.ada-anvil.app/v2/services/validators/${scriptHash}/address`, { method: 'GET', headers: { 'Content-Type': 'application/json', 'X-Api-Key': 'YOUR_API_KEY' } }); const { hex, bech32 } = await response.json(); return { hex, bech32 }; } // Sample output: // { // "hex": "61c9a6c4d47f5c8e...", // "bech32": "addr_test1wqsw5u8u7g5m6x0s0qqp9g3zgx9n9ahm0t4105keg3zt0swsgjmzg" // } ### [](https://dev.ada-anvil.io/guides/smart-contract/smart-contract-utilities#apply-parameters-to-script) Apply Parameters to Script `**POST /validators/{hash}/apply-params**` Applies a set of parameters to a parameterized script, returning the new script hash and compiled code. See [Hello World Example with Parameters](https://github.com/Cardano-Forge/anvil-api-examples/tree/main/smart-contracts/hello-world/aiken-hello-world-with-params) for an example of a smart contract that uses parameters. * **Path Parameters**: * `hash` (string): The hash of the original parameterized script. * **Body**: * `purpose` (string): The purpose of the script, which determines the context in which it's validated. Possible values are: * `"mint"`: For minting or burning assets. * `"spend"`: For spending transaction outputs from the script address. * `"withdraw"`: For withdrawing staking rewards. * `"publish"`: For publishing delegation certificates. * `"vote"`: For voting on governance proposals. * `"propose"`: For executing constitution guardrails when submitting governance proposals. * `params` (array): An array of parameters to apply to the script. e.g. owner key hash, or UTXO reference of assets on the smart contract. * `blueprint` (object, optional): Only required if the script is not yet known to Anvil. This is usually your `plutus.json` file from an Aiken build. See the [Blueprint Management](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/guides/smart-contract/blueprint-management.md) guide for more details. TypeScript Example: Apply parameters to a script[](https://dev.ada-anvil.io/guides/smart-contract/smart-contract-utilities#typescript-example-apply-parameters-to-a-script) Copy async function applyParameters(scriptHash: string, params: string[]) { const response = await fetch(`https://preprod.api.ada-anvil.app/v2/services/validators/${scriptHash}/apply-params`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Api-Key': 'YOUR_API_KEY' }, body: JSON.stringify({ hash: scriptHash, purpose: "spend", // or "mint", etc. params: params // e.g., [uniqueUtxoRef, ByteArray, Integer, etc...] }) }); const result = await response.json(); const { hash, compiledCode, addressBech32, addressHex, preloadedScript } = result; return { hash, compiledCode, addressBech32, addressHex, preloadedScript }; } // Sample output: // { // "hash": "72c6923a040389fa08c728fe41f0bb9be1722867aec4067fcc751cef", // "compiledCode": "59010e...", // "addressBech32": "addr_test1wpevdy36qspcn7sgcu50us0shwd7zu3gv7hvgpnle363emcu0m8ar", // "addressHex": "7072c6923a040389fa08c728fe41f0bb9be1722867aec4067fcc751cef", // "preloadedScript": { "type": "plutus", "blueprint": { /* ... */ } } // } ### [](https://dev.ada-anvil.io/guides/smart-contract/smart-contract-utilities#parse-plutus-data) Parse Plutus Data `**POST /validators/{hash}/parse**` Parses a hex-encoded Plutus data string (e.g., datum or redeemer) into a structured JSON object based on the script's schema. * **Path Parameters**: * `hash` (string): The hash of the script. The API uses this hash to fetch the validator's schema, which is required to correctly convert the data between JSON and its hex-encoded format. * **Body**: * `type` (string): The type of data to parse (`"datum"` or `"redeemer"`). * `purpose` (string): The purpose of the script, which determines the context in which it's validated. Possible values are: * `"mint"`: For minting or burning assets. * `"spend"`: For spending transaction outputs from the script address. * `"withdraw"`: For withdrawing staking rewards. * `"publish"`: For publishing delegation certificates. * `"vote"`: For voting on governance proposals. * `"propose"`: For executing constitution guardrails when submitting governance proposals. * `data` (object): An object containing the hex string: `{ "hex": "..." }`. * `addressFormat` (string, optional): The desired address format (`"bech32"` or `"raw"`). * `blueprint` (object, optional): The CIP-57 blueprint if the script is not yet known to Anvil. See the [Blueprint Management](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/guides/smart-contract/blueprint-management.md) guide for more details. TypeScript Example: Parse a datum from a transaction output[](https://dev.ada-anvil.io/guides/smart-contract/smart-contract-utilities#typescript-example-parse-a-datum-from-a-transaction-output) Copy async function parseDatum(scriptHash: string, datumHex: string) { const response = await fetch(`https://preprod.api.ada-anvil.app/v2/services/validators/${scriptHash}/parse`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Api-Key': 'YOUR_API_KEY' }, body: JSON.stringify({ type: "datum", purpose: "spend", // or "mint", etc., depending on the script data: { hex: datumHex } }) }); const result = await response.json(); // Returns the parsed datum as a JSON object return result; } // The API parses the hex string into a human-readable JSON object based on the validator's schema. // { // "owner": "30f4e824283240d2ca66f3e09b0b7adfc5d37816c072279b31f090a4" // } ### [](https://dev.ada-anvil.io/guides/smart-contract/smart-contract-utilities#serialize-plutus-data) Serialize Plutus Data `**POST /validators/{hash}/serialize**` Serializes a JSON object into a hex-encoded Plutus data string based on the script's schema. The opposite of parse. * **Path Parameters**: * `hash` (string): The hash of the script. The API uses this hash to fetch the validator's schema, which is required to correctly convert the data between JSON and its hex-encoded format. * **Body**: * `type` (string): The type of data to serialize (`"datum"` or `"redeemer"`). * `purpose` (string): The purpose of the script, which determines the context in which it's validated. Possible values are: * `"mint"`: For minting or burning assets. * `"spend"`: For spending transaction outputs from the script address. * `"withdraw"`: For withdrawing staking rewards. * `"publish"`: For publishing delegation certificates. * `"vote"`: For voting on governance proposals. * `"propose"`: For executing constitution guardrails when submitting governance proposals. * `data` (object): The JSON object to serialize. * `blueprint` (object, optional): The CIP-57 blueprint if the script is not yet known to Anvil. See the [Blueprint Management](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/guides/smart-contract/blueprint-management.md) guide for more details. TypeScript Example: Serialize a datum object into hex[](https://dev.ada-anvil.io/guides/smart-contract/smart-contract-utilities#typescript-example-serialize-a-datum-object-into-hex) Copy // The is the opposite of parse. It takes a JSON object and returns a hex string. async function serializeDatum(scriptHash: string, datumObject: unknown) { const response = await fetch(`https://preprod.api.ada-anvil.app/v2/services/validators/${scriptHash}/serialize`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Api-Key': 'YOUR_API_KEY' }, body: JSON.stringify({ type: "datum", purpose: "spend", // or "mint", etc. data: datumObject // A JSON object matching the datum schema defined in your blueprint. }) }); const result = await response.json(); // Returns the hex-encoded datum string return result; } // The API serializes the JSON object into a hex-encoded string based on the validator's schema. // Output: "d8799f581c30f4e824283240d2ca..." [PreviousBlueprint Management (CIP-57)](https://dev.ada-anvil.io/guides/smart-contract/blueprint-management) [NextValidators](https://dev.ada-anvil.io/guides/smart-contract/validators) Last updated 11 days ago Was this helpful? --- # Submit Transaction | Anvil Development Agency [](https://dev.ada-anvil.io/wayup/submit-tx#introduction) Introduction --------------------------------------------------------------------------- This guide demonstrates how to submit a signed transaction to the Wayup Marketplace using the API. This step comes after you've built and signed a transaction, such as for buying an NFT, making an offer, or creating a listing. [](https://dev.ada-anvil.io/wayup/submit-tx#requirements) Requirements --------------------------------------------------------------------------- * A transaction hex from a previous `/build-tx` call * A signature from your wallet after signing the transaction (optional). See [Sign Transactions](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/transaction/signing-transaction.md) [](https://dev.ada-anvil.io/wayup/submit-tx#api-request-structure) API Request Structure --------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/wayup/submit-tx#configuration) Configuration Copy // API endpoint for Wayup Marketplace const BASE_URL = "https://prod.api.ada-anvil.app/marketplace/api"; // Replace these with your actual signature and transaction hex // The signature comes from your wallet after signing the transaction hex // The transaction is the hex output from a previous build-tx call const SIGNATURE = "SIGNATURE"; const TRANSACTION = "TRANSACTION"; ### [](https://dev.ada-anvil.io/wayup/submit-tx#type-definitions) Type Definitions Copy interface SubmitPayload { signature: string; transaction: string; } [](https://dev.ada-anvil.io/wayup/submit-tx#implementation) Implementation ------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/wayup/submit-tx#step-1-prepare-the-submission-payload) Step 1: Prepare the Submission Payload First, prepare the payload with your signature and transaction hex: Copy // Step 1: Prepare the payload with signature and transaction console.log("Preparing submission payload..."); const payload = { signature: SIGNATURE.trim(), transaction: TRANSACTION.trim(), }; ### [](https://dev.ada-anvil.io/wayup/submit-tx#step-2-submit-the-transaction) Step 2: Submit the Transaction Next, submit the transaction to the API: Copy // Step 2: Submit the transaction console.log("Submitting signed transaction..."); const response = await fetch(`${BASE_URL}/submit`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), }); ### [](https://dev.ada-anvil.io/wayup/submit-tx#step-3-process-the-response) Step 3: Process the Response Finally, process the response from the API: Copy // Step 3: Process the response const result = await response.json(); console.log("Transaction submission result:", result); // Next step: Check the transaction status on chain explorers console.log("Check transaction status on Cardanoscan or similar explorer"); ### [](https://dev.ada-anvil.io/wayup/submit-tx#running-the-example) Running the Example Copy deno run --allow-net submit-tx.ts ### [](https://dev.ada-anvil.io/wayup/submit-tx#example-output) Example Output Copy Preparing submission payload... Submitting signed transaction... Transaction submission result: { result: { data: { txHash: "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" } } } Check transaction status on Cardanoscan or similar explorer [](https://dev.ada-anvil.io/wayup/submit-tx#the-complete-file) The Complete File ------------------------------------------------------------------------------------- submit-tx.ts[](https://dev.ada-anvil.io/wayup/submit-tx#submit-tx.ts) Copy // API endpoint for Wayup Marketplace const BASE_URL = "https://prod.api.ada-anvil.app/marketplace/api"; // Replace these with your actual signature and transaction hex // The signature comes from your wallet after signing the transaction hex // The transaction is the hex output from a previous build-tx call const SIGNATURE = "a10084582a82582a5840680ddbb903f321289e96ca816aeab0eb947b"; const TRANSACTION = "84aa00d9010283825820b808df86c4bb38072dac95bddb651"; // Step 1: Prepare the payload with signature and transaction console.log("Preparing submission payload..."); const payload = { signature: SIGNATURE.trim(), // Optional if TX is already signed transaction: TRANSACTION.trim(), }; // Step 2: Submit the transaction console.log("Submitting signed transaction..."); const response = await fetch(`${BASE_URL}/submit`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), }); // Step 3: Process the response const result = await response.json(); console.log("Transaction submission result:", result); // Next step: Check the transaction status on chain explorers console.log("Check transaction status on Cardanoscan or similar explorer"); // Run this with: deno run --allow-net submit-tx.ts [](https://dev.ada-anvil.io/wayup/submit-tx#best-practices) Best Practices ------------------------------------------------------------------------------- 1. **Transaction Verification**: Always verify the transaction hex before signing 2. **Signature Format**: Ensure the signature is properly formatted from your wallet 3. **Error Handling**: Check the response for any errors before assuming success 4. **Chain Validation**: Always verify the transaction was confirmed on the blockchain [](https://dev.ada-anvil.io/wayup/submit-tx#end-to-end-flow) End-to-End Flow --------------------------------------------------------------------------------- 1. Build a transaction using a specific endpoint (buy, create listing, make offer) 2. Sign the transaction with your wallet 3. Submit the signed transaction using this guide 4. Verify the transaction on a blockchain explorer [PreviousUnlist Listing](https://dev.ada-anvil.io/wayup/wayup/unlist-listing) [NextProfile Information](https://dev.ada-anvil.io/wayup/profile-information) Last updated 16 days ago Was this helpful? --- # Integration Flow | Anvil Development Agency This guide outlines the step-by-step process for integrating with the Tournament Builder developed for Forge Digital Ventures. For technical details of the architecture, see the [Overview](https://dev.ada-anvil.io/tournament-builder/overview) document. This workflow assumes you have already set up your development environment and have the necessary credentials to access the Anvil API. [](https://dev.ada-anvil.io/tournament-builder/integration-flow#id-1.-tournament-creation) 1\. Tournament Creation ----------------------------------------------------------------------------------------------------------------------- Step-by-Step Process [](https://dev.ada-anvil.io/tournament-builder/integration-flow#tab-step-by-step-process) Example API Flow [](https://dev.ada-anvil.io/tournament-builder/integration-flow#tab-example-api-flow) 1. **Prepare Tournament Configuration** * Define bracket size, entry fees, and payout structure * Identify administrator address * Gather UTXOs for transaction fees 2. **Create Tournament Transaction** * Call the [`/tournaments` endpoint](https://dev.ada-anvil.io/tournament-builder/tournament#create-tournament) with configuration * Receive transaction hex in response 3. **Sign and Submit Transaction** * Sign the transaction with appropriate keys * Submit the signed transaction (see [Transaction Overview](https://dev.ada-anvil.io/guides/transaction) for details) When successful, the tournament is created and an NFT is minted to represent it on-chain. The tournament is now ready to accept registrations. Copy // 1. Configure tournament parameters const tournamentConfig = { name: "Tournament Name", adminAddress: "addr1...", bracketSize: 16, entryFee: "10000000", // 10 ADA in lovelace payoutStructure: ["7000000", "2000000", "1000000"], utxos: ["1234...#0", "5678...#1"] }; // 2. Create tournament transaction const response = await fetch("/tournaments", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(tournamentConfig) }); const { transaction } = await response.json(); // 3. Sign and submit transaction // See Transaction Overview documentation for details: ../guides/transaction/README.md const signedTx = await signTransaction(transaction); const submitResponse = await fetch("/tournaments/submit-tx", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ transaction: signedTx }) }); const { txHash } = await submitResponse.json(); console.log("Tournament created with transaction:", txHash); [](https://dev.ada-anvil.io/tournament-builder/integration-flow#id-2.-registration-management) 2\. Registration Management ------------------------------------------------------------------------------------------------------------------------------- The Tournament Builder uses a two-phase registration process: 1. Participants self-register (creating registration UTXOs) 2. Tournament administrator collects these registrations and officially adds participants ### [](https://dev.ada-anvil.io/tournament-builder/integration-flow#phase-1-participants-register) Phase 1: Participants Register 1. **Participant Initiates Registration** * Call [`POST /tournaments/{id}/register`](https://dev.ada-anvil.io/tournament-builder/registration#register) with participant address * Include UTXOs covering entry fee * Submit resulting transaction * This creates a registration UTXO at the registration script address 2. **Verification** * Query [`GET /tournaments/{id}/registrations`](https://dev.ada-anvil.io/tournament-builder/registration#list-registrations) to confirm registration * Validate status and fee payment * Participants must monitor their registration status 3. **Optional: Registration Cancellation** * If needed, participants can cancel their registration before being added to the tournament * Call [POST /tournaments/{id}/unregister\`](https://dev.ada-anvil.io/tournament-builder/registration#cancel-registration) * The registration UTXO with the full entry fee amount is returned to the participant * This provides a complete refund mechanism for participants who change their mind ### [](https://dev.ada-anvil.io/tournament-builder/integration-flow#phase-2-tournament-administrator-adds-participants) Phase 2: Tournament Administrator Adds Participants 1. **Collect Registration Outputs** * Tournament administrator gathers valid registration output references * Uses [`GET /tournaments/{id}/registrations`](https://dev.ada-anvil.io/tournament-builder/registration#list-registrations) to find valid registrations 2. **Add Participants to Tournament** * Administrator calls [`POST /tournaments/{id}/participants`](https://dev.ada-anvil.io/tournament-builder/tournament#add-participants) with registration list * Only the tournament admin can perform this action * Administrator signs and submits transaction * This transaction consumes the registration UTXOs and updates the tournament datum * Participants are now officially part of the tournament ### [](https://dev.ada-anvil.io/tournament-builder/integration-flow#phase-3-tournament-settlement) Phase 3: Tournament Settlement 1. **Finalize Participant List** * Administrator reviews and confirms all participants 2. **Conduct Tournament** * Actual competition happens off-chain * Results are determined through competition 3. **Submit Results** * Tournament administrator submits final standings using the [`PATCH /tournaments/{id}/settle` endpoint](https://dev.ada-anvil.io/tournament-builder/tournament#tournament-settlement) * Smart contract distributes rewards according to payout structure defined at tournament creation * Each prize recipient receives their prize as a transaction output with the tournament ID as datum * For details on prize distribution and fees, see the [Fee Structure](https://dev.ada-anvil.io/tournament-builder/fee-structure) document [PreviousOverview](https://dev.ada-anvil.io/tournament-builder/overview) [NextTournament](https://dev.ada-anvil.io/tournament-builder/tournament) Last updated 1 month ago Was this helpful? --- # Registration | Anvil Development Agency The Tournament Builder developed for Forge Digital Ventures uses a two-phase registration process: 1. Participants first create registration records using the `/register` endpoint 2. Tournament administrators then officially add these participants to the tournament using the `/participants` endpoint [](https://dev.ada-anvil.io/tournament-builder/registration#creating-registration-records) Creating Registration Records ----------------------------------------------------------------------------------------------------------------------------- **Endpoint**: `POST /tournaments/{tournamentId}/register` Allows participants to register for a specific tournament. **This only creates a registration record and does not yet add the participant to the tournament.** **Request Parameters**: * `utxos` (string\[\], min 1): Array of UTXO strings to use for the transaction * `registererAddress` (string): Address of the registering participant, used for prize payouts * `changeAddress` (string, optional): Address to receive change UTXOs (defaults to registererAddress) * `tournamentId` (string): Tournament identifier * `contractVersion` (string, optional): Contract version for custom deployments * `contractTitle` (string, optional): Contract title for custom deployments **Response**: * `complete`: Hex-encoded transaction for signing The returned transaction must be signed and submitted through the `/tournaments/submit-tx` endpoint before it takes effect on the blockchain. **Technical Notes**: * Creates an output at the parameterized registration script address * The output must contain the required entry fee * The registration datum stores the participant's address * This address will be used for prize payouts. * After registration, participants must wait for tournament admin to add them **Example (Node.js with fetch)**: Copy async function createRegistration(tournamentId) { const apiUrl = `https://preprod.api.ada-anvil.app/v2/services/tournaments/${tournamentId}/register`; const requestData = { utxos: ["8282...", "8282...", "8282..."], // UTXOs must contain enough funds to cover the entry fee registererAddress: "addr_test1...", // Tournament participant address changeAddress: "addr_test1...", // Optional, defaults to registererAddress tournamentId: tournamentId, }; const response = await fetch(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(requestData), }); const data = await response.json(); console.log('Registration transaction:', data.complete); // The returned transaction must be signed and submitted (see Transaction Overview: ../guides/transaction/README.md) return data.complete; } [](https://dev.ada-anvil.io/tournament-builder/registration#cancelling-a-registration) Cancelling a Registration --------------------------------------------------------------------------------------------------------------------- **Endpoint**: `POST /tournaments/{tournamentId}/unregister` Allows participants to cancel their registration and recover their entry fee. **Request Parameters**: * `utxos` (string\[\], min 1): Array of UTXO strings to use for the transaction * `registererAddress` (string): Address of the registered participant * `changeAddress` (string, optional): Address to receive change UTXOs (defaults to registererAddress) * `tournamentId` (string): Tournament identifier * `outputRef` (string): Reference to the registration UTXO in format `txHash#index` * `contractTitle` (string, optional): Contract title for custom deployments * `contractVersion` (string, optional): Contract version for custom deployments **Response**: * `complete`: Hex-encoded transaction for signing The returned transaction must be signed and submitted through the `/tournaments/submit-tx` endpoint before it takes effect on the blockchain. **Technical Notes**: * Only the original registrant can cancel their registration * The "Cancel" redeemer is used to validate the transaction * The registration UTXO (including the entry fee) is returned to the participant * This provides a full refund of the entry fee to the canceling participant * Cancellation is only possible before the administrator has added the participant to the tournament **Example (Node.js with fetch)**: Copy async function cancelRegistration(tournamentId, outputRef) { const apiUrl = `https://preprod.api.ada-anvil.app/v2/services/tournaments/${tournamentId}/unregister`; const requestData = { registererAddress: "addr_test1...", tournamentId: tournamentId, outputRef: outputRef, changeAddress: "addr_test1...", // Optional, defaults to registererAddress utxos: ["8282...", "8282...", "8282..."] }; const response = await fetch(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(requestData), }); const data = await response.json(); console.log('Cancellation transaction:', data.complete); // The returned transaction must be signed and submitted (see Transaction Overview: ../guides/transaction/README.md) return data.complete; } [](https://dev.ada-anvil.io/tournament-builder/registration#retrieving-registrations) Retrieving Registrations ------------------------------------------------------------------------------------------------------------------- **Endpoint**: `GET /tournaments/{tournamentId}/registrations` Retrieves a list of all registrations for a specific tournament. **Request Parameters**: * `tournamentId` (string): Tournament identifier * `valid` (boolean, optional): Filter to only valid or invalid registrations * `limit` (number, optional): Max number of results to return (1-100, default: 100) * `page` (number, optional): Page number for pagination * `cursor` (number, optional): Alias for page, enables TRPC infinite queries * `contractTitle` (string, optional): Contract title for custom deployments * `contractVersion` (string, optional): Contract version for custom deployments **Response**: * `entryFeeLovelace` (string): The required entry fee amount in lovelace * `results`: Array of registration objects, each containing: * `address` (string): Registrant address * `outputRef` (string): Registration UTXO reference * `valid` (boolean): Whether the registration has sufficient funds * `paidFeesLovelace` (string): Amount of fees paid in lovelace **Technical Notes**: * Results can be filtered by validity (sufficient funds) * Results are paginated with a default limit of 100 registrations * Output refs uniquely identify each registration UTXO on the blockchain **Example (Node.js with fetch)**: Copy async function getRegistrations(tournamentId, validOnly = true) { const apiUrl = `https://preprod.api.ada-anvil.app/v2/services/tournaments/${tournamentId}/registrations?valid=${validOnly}&limit=50`; const response = await fetch(apiUrl, { method: 'GET', headers: { 'Content-Type': 'application/json', } }); const data = await response.json(); console.log(`Required entry fee: ${data.entryFeeLovelace} lovelace`); console.log(`Found ${data.results.length} registrations`); return data; } **Technical Notes**: * Returns both valid and invalid registrations (can be filtered with the `valid` parameter) * Invalid registrations may not have paid the correct entry fee **Example (Node.js with fetch)**: Copy async function getRegistrations(tournamentId, validOnly = false) { const apiUrl = `https://preprod.api.ada-anvil.app/v2/services/tournaments/${tournamentId}/registrations${validOnly ? '?valid=true' : ''}`; const response = await fetch(apiUrl, { method: 'GET', headers: { 'Content-Type': 'application/json', }, }); const data = await response.json(); console.log('Required entry fee (lovelace):', data.entryFeeLovelace); console.log('Registrations:', data.results); return data; } [PreviousTournament](https://dev.ada-anvil.io/tournament-builder/tournament) [NextSubmit](https://dev.ada-anvil.io/tournament-builder/submit) Last updated 1 month ago Was this helpful? --- # Buy NFT | Anvil Development Agency [](https://dev.ada-anvil.io/wayup/wayup/buy-nft#introduction) Introduction ------------------------------------------------------------------------------- This guide demonstrates how to find and purchase an NFT on the Wayup Marketplace using the API. The process involves two main steps: 1. Finding an NFT listing using the `get-collection-assets` endpoint. See [Get Collection Assets](https://dev.ada-anvil.io/wayup/collection-information/get-collection-assets) 2. Building a purchase transaction with the `build-tx` endpoint [](https://dev.ada-anvil.io/wayup/wayup/buy-nft#requirements) Requirements ------------------------------------------------------------------------------- * A Cardano wallet with sufficient ADA * UTXOs from your wallet to fund the transaction. See [Select UTXOs](https://dev.ada-anvil.io/guides/transaction/selecting-utxos#fetching-utxos-multiple-approaches) * Policy ID of the NFT collection you're interested in [](https://dev.ada-anvil.io/wayup/wayup/buy-nft#api-request-structure) API Request Structure ------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/wayup/wayup/buy-nft#configuration) Configuration Copy // API endpoint for Wayup Marketplace const BASE_URL = "https://prod.api.ada-anvil.app/marketplace/api"; // NFT Collection to purchase from const POLICY_ID = "6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01"; // Wallet address to receive change and NFT const BUYER_CHANGE_ADDRESS = "addr1qx33ycd2ymg02dxh6vnnf8dsk54l8ranr9cfjk9qrlj3309c69jc4n8h3uvnczncaqu2sm03pl99h99n75uvtl3mhv0q3z8s8m"; // UTXOs from your wallet to fund the transaction const BUYER_UTXOS: string[] = [\ "828258204baa167631a62343cc4f37f9313ee05d6149b1fda39fd563e12832c8dd49fac90182583901a31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1e821a001344eea1581c6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01a24f4646506f776572436f72657336323901504646506f776572436f7265733132343801",\ "828258204baa167631a62343cc4f37f9313ee05d6149b1fda39fd563e12832c8dd49fac90282583901a31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1e1a005b1fe1",\ "82825820daac1d3b80dcab8817c2f02f2d43ab2b33a4e74419679939eb8aa5f70b03f35c0182583901a31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1e821a007a089da1581c6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01a1504646506f776572436f7265733137323001"\ ]; ### [](https://dev.ada-anvil.io/wayup/wayup/buy-nft#type-definitions) Type Definitions Copy interface AssetResult { policyId: string; assetName: string; listing?: { txHashIndex: string; price: number; } | null; } [](https://dev.ada-anvil.io/wayup/wayup/buy-nft#implementation) Implementation ----------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/wayup/wayup/buy-nft#step-1-find-available-listings) Step 1: Find Available Listings First, query the API to find NFTs for sale, sorted by price: Copy // Step 1: Define the API request to find the cheapest listing const url = `${BASE_URL}/get-collection-assets?policyId=${POLICY_ID}&saleType=list&orderBy=priceAsc&limit=1`; // Step 2: Fetch the listing const response = await fetch(url); const json = await response.json(); const results: AssetResult[] = json?.results ?? []; const listing = results[0]; console.log(`Found asset: ${listing.assetName} | Price: ${listing.listing?.price} lovelace`); ### [](https://dev.ada-anvil.io/wayup/wayup/buy-nft#step-2-build-the-purchase-transaction) Step 2: Build the Purchase Transaction Next, construct a purchase transaction using the listing: Copy // Step 3: Create the purchase payload const buyPayload = { changeAddress: BUYER_CHANGE_ADDRESS, utxos: BUYER_UTXOS, buy: [\ {\ policyId: listing.policyId,\ txHashIndex: listing?.listing?.txHashIndex,\ \ // 1 ADA = 1_000_000 lovelace\ priceAda: listing?.listing?.price / 1_000_000,\ },\ ], }; // Step 4: Build the transaction const txResponse = await fetch(`${BASE_URL}/build-tx`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(buyPayload), }); const txResult = await txResponse.json(); console.log("Transaction:", txResult.transactions); ### [](https://dev.ada-anvil.io/wayup/wayup/buy-nft#running-the-example) Running the Example Copy deno run --allow-net purchase-nft-test.ts ### [](https://dev.ada-anvil.io/wayup/wayup/buy-nft#example-output) Example Output Copy Found asset: 4646506f776572436f7265733437 | Price: 5000000 lovelace { changeAddress: "addr1qx33ycd2ymg02dxh6vnnf8dsk54l8ranr9cfjk9qrlj3309c69jc4n8h3uvnczncaqu2sm03pl99h99n75uvtl3mhv0q3z8s8m", utxos: [\ "828258204baa167631a62343cc4f37f9313ee05d6149b1fda39fd563e12832c8dd49fac90182583901a31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1e821a001344eea1581c6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01a24f4646506f776572436f72657336323901504646506f776572436f7265733132343801",\ "828258204baa167631a62343cc4f37f9313ee05d6149b1fda39fd563e12832c8dd49fac90282583901a31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1e1a005b1fe1",\ "82825820daac1d3b80dcab8817c2f02f2d43ab2b33a4e74419679939eb8aa5f70b03f35c0182583901a31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1e821a007a089da1581c6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01a1504646506f776572436f7265733137323001"\ ], buy: [\ {\ policyId: "6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01",\ txHashIndex: "4baa167631a62343cc4f37f9313ee05d6149b1fda39fd563e12832c8dd49fac9#0",\ priceAda: 5\ }\ ] } Transaction: [\ "84aa00d90102838258204baa167631a62343cc4f37f9313ee05d6149b1fda39fd563e12832c8dd49fac9008258204baa167631a62343cc4f37f9313ee05d6149b1fda39fd563e12832c8dd49fac902825820daac1d3b80dcab8817c2f02f2d43ab2b33a4e74419679939eb8aa5f70b03f35c010184a30058393184cc25ea4c29951d40b443b95bbc5676bc425470f96376d1984af9ab2c967f4bd28944b06462e13c5e3f5d5fa6e03f8567569438cd833e6d011a0011a008028201d81858225820c7dc7a987b9367993b617821ad64c3506b9015de1744270000a97a8d84e56df0825839015288ee085dc108f6fdc262b9e0cdfa92663b302836830efc0c5b4fdf41ff84ecc10aae4f3ba8526ba742c739de9d513ace95e17170b1f04c1a000f424082583901a31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1e1a002dc6c082583901a31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1e821a00955df2a1581c6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01a24e4646506f776572436f726573343701504646506f776572436f7265733137323001021a000562fc031a09744dc407582010d70a7bf1ff1ed57b4ac55c6ed323880724390905b3f69b92615166c3ac96990b5820ff02484b059bcb0f3c47368c465fa38ebedabae6561eadf02f1eba621f963c470dd90102818258204baa167631a62343cc4f37f9313ee05d6149b1fda39fd563e12832c8dd49fac9021082583901a31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1e1a0043fdbf111a0017222212d90102818258201693c508b6132e89b932754d657d28b24068ff5ff1715fec36c010d4d6470b3d00a204d901029fd8799f9fd8799fd8799fd8799f581c5288ee085dc108f6fdc262b9e0cdfa92663b302836830efc0c5b4fdfffd8799fd8799fd8799f581c41ff84ecc10aae4f3ba8526ba742c739de9d513ace95e17170b1f04cffffffff1a000f4240ffd8799fd8799fd8799f581ca31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcffd8799fd8799fd8799f581cb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1effffffff1a002dc6c0ffff581ca31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcffff05a182000082d8799f00ff821a000377541a04c44bfcf5a11902a2a1636d7367715761797570205472616e73616374696f6e"\ ] [](https://dev.ada-anvil.io/wayup/wayup/buy-nft#the-complete-file) The Complete File ----------------------------------------------------------------------------------------- See our examples repository for the complete file: [buy-nft-test.ts](https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/wayup/buy-nft-test.ts) purchase-nft-test.ts[](https://dev.ada-anvil.io/wayup/wayup/buy-nft#purchase-nft-test.ts) Copy // API endpoint for Wayup Marketplace const BASE_URL = "https://prod.api.ada-anvil.app/marketplace/api"; // NFT Collection to purchase from const POLICY_ID = "POLICY_ID"; // Wallet address to receive change and NFT const BUYER_CHANGE_ADDRESS = "BUYER_CHANGE_ADDRESS"; // UTXOs from your wallet to fund the transaction const BUYER_UTXOS: string[] = [\ "BUYER_UTXOS"\ ]; interface AssetResult { policyId: string; assetName: string; listing?: { txHashIndex: string; price: number; } | null; } // Step 1: Define the API request to find the cheapest listing const url = `${BASE_URL}/get-collection-assets?policyId=${POLICY_ID}&saleType=list&orderBy=priceAsc&limit=1`; // Step 2: Fetch the listing const response = await fetch(url); const json = await response.json(); const results: AssetResult[] = json?.results ?? []; const listing = results[0]; console.log(`Found asset: ${listing.assetName} | Price: ${listing.listing?.price} lovelace`); // Step 3: Create the purchase payload const buyPayload = { changeAddress: BUYER_CHANGE_ADDRESS, utxos: BUYER_UTXOS, buy: [\ {\ policyId: listing.policyId,\ txHashIndex: listing?.listing?.txHashIndex,\ \ // 1 ADA = 1_000_000 lovelace\ priceAda: listing?.listing?.price / 1_000_000,\ },\ ], }; console.log(buyPayload); // Step 4: Build the transaction const txResponse = await fetch(`${BASE_URL}/build-tx`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(buyPayload), }); const txResult = await txResponse.json(); console.log("Transaction:", txResult.transactions); // Next steps: Sign the hex with your wallet and submit via /submit endpoint // Run this with: deno run --allow-net purchase-nft-test.ts [](https://dev.ada-anvil.io/wayup/wayup/buy-nft#next-steps) Next Steps --------------------------------------------------------------------------- After building the transaction: 1. Sign the transaction hex with your wallet. See [Sign Transactions](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/wayup/guides/transaction/signing-transaction.md) 2. Submit the signed transaction using the `/submit` endpoint. See [Submit Transactions](https://dev.ada-anvil.io/wayup/submit-tx) 3. Wait for confirmation on the blockchain [PreviousBuild Transaction](https://dev.ada-anvil.io/wayup/wayup) [NextMake Offer](https://dev.ada-anvil.io/wayup/wayup/create-offer) Last updated 16 days ago Was this helpful? --- # Update Listing | Anvil Development Agency [](https://dev.ada-anvil.io/wayup/wayup/update-listing#introduction) Introduction -------------------------------------------------------------------------------------- This guide demonstrates how to update an existing NFT listing's price on the Wayup Marketplace using the API. This allows sellers to adjust their asking prices without having to unlist and re-list their NFTs. [](https://dev.ada-anvil.io/wayup/wayup/update-listing#requirements) Requirements -------------------------------------------------------------------------------------- * Find your profile listings via the `get-profile-listings` endpoint. See [Get Profile Listings](https://dev.ada-anvil.io/wayup/profile-information/get-profile-listings) * The Policy ID of the NFT you've listed * The txHashIndex of your existing listing * UTXOs from your wallet to fund the transaction. See [Select UTXOs](https://dev.ada-anvil.io/guides/transaction/selecting-utxos#fetching-utxos-multiple-approaches) * Your change address to receive any change [](https://dev.ada-anvil.io/wayup/wayup/update-listing#api-request-structure) API Request Structure -------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/wayup/wayup/update-listing#configuration) Configuration Copy // API endpoint for Wayup Marketplace const BASE_URL = "https://prod.api.ada-anvil.app/marketplace/api"; // NFT listing to update const POLICY_ID = "6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01"; const TX_HASH_INDEX = "4baa167631a62343cc4f37f9313ee05d6149b1fda39fd563e12832c8dd49fac9#0"; // Format: txHash#outputIndex // Wallet address to receive change const SELLER_CHANGE_ADDRESS = "addr1qx33ycd2ymg02dxh6vnnf8dsk54l8ranr9cfjk9qrlj3309c69jc4n8h3uvnczncaqu2sm03pl99h99n75uvtl3mhv0q3z8s8m"; // UTXOs from your wallet to fund the transaction const SELLER_UTXOS = [\ "828258204baa167631a62343cc4f37f9313ee05d6149b1fda39fd563e12832c8dd49fac90182583901a31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1e821a001344eea1581c6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01a24f4646506f776572436f72657336323901504646506f776572436f7265733132343801",\ "828258204baa167631a62343cc4f37f9313ee05d6149b1fda39fd563e12832c8dd49fac90282583901a31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1e1a005b1fe1",\ "82825820daac1d3b80dcab8817c2f02f2d43ab2b33a4e74419679939eb8aa5f70b03f35c0182583901a31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1e821a007a089da1581c6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01a1504646506f776572436f7265733137323001"\ ]; // New listing price in ADA (minimum 5 ADA) const NEW_PRICE_ADA = 10; // Update to your desired price ### [](https://dev.ada-anvil.io/wayup/wayup/update-listing#type-definitions) Type Definitions Copy interface UpdateListingInput { policyId: string; txHashIndex: string; // Format: txHash#outputIndex newPriceAda: number; } interface BuildTxPayload { changeAddress: string; utxos: string[]; update: UpdateListingInput[]; } [](https://dev.ada-anvil.io/wayup/wayup/update-listing#implementation) Implementation ------------------------------------------------------------------------------------------ ### [](https://dev.ada-anvil.io/wayup/wayup/update-listing#step-1-identify-the-nft-listing) Step 1: Identify the NFT Listing First, identify the NFT listing you want to update: Copy // Step 1: Identify the NFT listing to update console.log(`Updating listing: Policy ID ${POLICY_ID}, TX Hash Index ${TX_HASH_INDEX}`); console.log(`New price will be: ${NEW_PRICE_ADA} ADA`); ### [](https://dev.ada-anvil.io/wayup/wayup/update-listing#step-2-prepare-the-update-payload) Step 2: Prepare the Update Payload Next, prepare the payload with your listing information and new price: Copy // Step 2: Prepare the payload for the build-tx endpoint const payload = { changeAddress: SELLER_CHANGE_ADDRESS, utxos: SELLER_UTXOS, update: [\ {\ policyId: POLICY_ID,\ txHashIndex: TX_HASH_INDEX,\ newPriceAda: NEW_PRICE_ADA\ }\ ] }; ### [](https://dev.ada-anvil.io/wayup/wayup/update-listing#step-3-build-the-transaction) Step 3: Build the Transaction Send the request to the build-tx endpoint: Copy // Step 3: Build the transaction console.log("Building update listing transaction..."); const response = await fetch(`${BASE_URL}/build-tx`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), }); ### [](https://dev.ada-anvil.io/wayup/wayup/update-listing#step-4-process-the-response) Step 4: Process the Response Process the response to get the transaction hex: Copy // Step 4: Process the response const result = await response.json(); console.log("Transaction:", result.transactions); // Next steps: Sign the transaction with your wallet and submit via /submit endpoint console.log("\nNext steps:"); console.log("1. Sign the transaction with your wallet"); console.log("2. Submit the signed transaction using the /submit endpoint"); ### [](https://dev.ada-anvil.io/wayup/wayup/update-listing#running-the-example) Running the Example Copy deno run --allow-net update-listing.ts ### [](https://dev.ada-anvil.io/wayup/wayup/update-listing#example-output) Example Output Copy Updating listing: Policy ID 6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01, TX Hash Index 4baa167631a62343cc4f37f9313ee05d6149b1fda39fd563e12832c8dd49fac9#0 New price will be: 10 ADA Building update listing transaction... Transaction: [\ "84ab00d90102828258204baa167631a62343cc4f37f9313ee05d6149b1fda39fd563e12832c8dd49fac900825820daac1d3b80dcab8817c2f02f2d43ab2b33a4e74419679939eb8aa5f70b03f35c01018283583911a76f0fb801a29f591e9871576508d85b0b5f3c38774f65032f58fdadb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1e821a00144178a1581c6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01a14e4646506f776572436f7265733437015820b6817cc908560a974136a66a14a33ca0827306a3559487eb34c3f3a0f58e568782583901a31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1e821a0074a212a1581c6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01a1504646506f776572436f7265733137323001021a0005668b031a0974514907582010d70a7bf1ff1ed57b4ac55c6ed323880724390905b3f69b92615166c3ac96990b58209337b517965948c514185958f26e8d55ca49ac21c946eb6f9b1e77d5a5b9c1960dd90102818258204baa167631a62343cc4f37f9313ee05d6149b1fda39fd563e12832c8dd49fac9020ed9010281581ca31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bc1082583901a31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1e1a0043f448111a00172b9912d90102818258201693c508b6132e89b932754d657d28b24068ff5ff1715fec36c010d4d6470b3d00a204d901029fd8799f9fd8799fd8799fd8799f581ca31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcffd8799fd8799fd8799f581cb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1effffffff1a007a1200ffd8799fd8799fd8799f581c5288ee085dc108f6fdc262b9e0cdfa92663b302836830efc0c5b4fdfffd8799fd8799fd8799f581c41ff84ecc10aae4f3ba8526ba742c739de9d513ace95e17170b1f04cffffffff1a000f4240ffff581cb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1effd8799f9fd8799fd8799fd8799f581c5288ee085dc108f6fdc262b9e0cdfa92663b302836830efc0c5b4fdfffd8799fd8799fd8799f581c41ff84ecc10aae4f3ba8526ba742c739de9d513ace95e17170b1f04cffffffff1a000f4240ffd8799fd8799fd8799f581ca31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcffd8799fd8799fd8799f581cb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1effffffff1a002dc6c0ffff581ca31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcffff05a182000082d87a80821a000205a91a02367e62f5a11902a2a1636d7367715761797570205472616e73616374696f6e"\ ] Next steps: 1. Sign the transaction with your wallet 2. Submit the signed transaction using the /submit endpoint [](https://dev.ada-anvil.io/wayup/wayup/update-listing#the-complete-file) The Complete File ------------------------------------------------------------------------------------------------ See our examples repository for the complete file: [update-listing.ts](https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/wayup/update-listing.ts) update-listing.ts[](https://dev.ada-anvil.io/wayup/wayup/update-listing#update-listing.ts) Copy // API endpoint for Wayup Marketplace const BASE_URL = "https://prod.api.ada-anvil.app/marketplace/api"; // NFT listing to update const POLICY_ID = "POLICY_ID"; const TX_HASH_INDEX = "TX_HASH_INDEX"; // Format: txHash#outputIndex // Wallet address to receive change const SELLER_CHANGE_ADDRESS = "SELLER_CHANGE_ADDRESS"; // UTXOs from your wallet to fund the transaction const SELLER_UTXOS = [\ "SELLER_UTXOS"\ ]; // New listing price in ADA (minimum 5 ADA) const NEW_PRICE_ADA = 10; // Update to your desired price // Step 1: Identify the NFT listing to update console.log(`Updating listing: Policy ID ${POLICY_ID}, TX Hash Index ${TX_HASH_INDEX}`); console.log(`New price will be: ${NEW_PRICE_ADA} ADA`); // Step 2: Prepare the payload for the build-tx endpoint const payload = { changeAddress: SELLER_CHANGE_ADDRESS, utxos: SELLER_UTXOS, update: [\ {\ policyId: POLICY_ID,\ txHashIndex: TX_HASH_INDEX,\ newPriceAda: NEW_PRICE_ADA\ }\ ] }; // Step 3: Build the transaction console.log("Building update listing transaction..."); const response = await fetch(`${BASE_URL}/build-tx`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), }); // Step 4: Process the response const result = await response.json(); console.log("Transaction:", result.transactions); // Next steps: Sign the transaction with your wallet and submit via /submit endpoint console.log("\nNext steps:"); console.log("1. Sign the transaction with your wallet"); console.log("2. Submit the signed transaction using the /submit endpoint"); // Run this with: deno run --allow-net update-listing.ts [](https://dev.ada-anvil.io/wayup/wayup/update-listing#best-practices) Best Practices ------------------------------------------------------------------------------------------ 1. **Verify Listing Ownership**: Always ensure you are the owner of the listing you're updating. 2. **Price Validation**: The new price must be different from the current price and should be a minimum of 5 ADA. 3. **Transaction Confirmation**: After submitting the signed transaction, verify it was confirmed on the blockchain. 4. **Listing Status**: Check that your listing appears with the updated price using the `get-collection-assets` endpoint. [](https://dev.ada-anvil.io/wayup/wayup/update-listing#end-to-end-flow) End-to-End Flow -------------------------------------------------------------------------------------------- 1. Identify the NFT listing to update (Policy ID and txHashIndex) 2. Build an update transaction via `/build-tx` with the `update` property 3. Sign the transaction with your wallet. See [Signing Transactions](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/transaction/signing-transaction.md) 4. Submit the transaction via `/submit`. See [Submit Transactions](https://dev.ada-anvil.io/wayup/submit-tx) 5. Wait for confirmation; your NFT will be listed with the new price on the marketplace [PreviousCreate Listing](https://dev.ada-anvil.io/wayup/wayup/create-listing) [NextUnlist Listing](https://dev.ada-anvil.io/wayup/wayup/unlist-listing) Last updated 16 days ago Was this helpful? --- # Create Listing | Anvil Development Agency [](https://dev.ada-anvil.io/wayup/wayup/create-listing#introduction) Introduction -------------------------------------------------------------------------------------- This guide demonstrates how to list an NFT for sale on the Wayup Marketplace using the API. Listing an NFT requires that you own the NFT and have it in your wallet UTXOs. [](https://dev.ada-anvil.io/wayup/wayup/create-listing#requirements) Requirements -------------------------------------------------------------------------------------- * A Cardano wallet containing the NFT you want to list. * UTXOs from your wallet containing the NFT. See [Get UTXOs](https://dev.ada-anvil.io/guides/transaction/selecting-utxos#fetching-utxos-multiple-approaches) for more information. * Policy ID and asset name of your NFT. [](https://dev.ada-anvil.io/wayup/wayup/create-listing#api-request-structure) API Request Structure -------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/wayup/wayup/create-listing#configuration) Configuration Copy // API endpoint for Wayup Marketplace const NFT_BASE_URL = "https://prod.api.ada-anvil.app/marketplace/api"; // NFT to list for sale const NFT_POLICY_ID = "6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01"; const NFT_ASSET_NAME_HEX = "4646506f776572436f72657331363630"; // Wallet address to receive change const SELLER_CHANGE_ADDRESS = "addr1qx33ycd2ymg02dxh6vnnf8dsk54l8ranr9cfjk9qrlj3309c69jc4n8h3uvnczncaqu2sm03pl99h99n75uvtl3mhv0q3z8s8m"; // UTXOs from your wallet containing the NFT to list const SELLER_UTXOS: string[] = [\ "82825820fa7a8f907051db7783be036684481fdce2a5fbcf19f6e5ea5f9f09128288dd1f0382583901a31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1e821a0092b832a1581c6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01a2504646506f776572436f7265733136363001504646506f776572436f7265733137323001",\ "82825820fd43f693f0cd4c690ca3adde613262fc41530e62b3cacc906a0056603f5514ae0082583901a31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1e821a0085b174a1581c6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01a34e4646506f776572436f7265733437014f4646506f776572436f72657336323901504646506f776572436f7265733132343801"\ ]; // Listing price in ADA (minimum 5 ADA) const LISTING_PRICE_ADA = 25; ### [](https://dev.ada-anvil.io/wayup/wayup/create-listing#type-definitions) Type Definitions Copy interface ListingPayload { changeAddress: string; utxos: string[]; create: Array<{ assets: { policyId: string; assetName: string; }; priceAda: number; }>; } [](https://dev.ada-anvil.io/wayup/wayup/create-listing#implementation) Implementation ------------------------------------------------------------------------------------------ ### [](https://dev.ada-anvil.io/wayup/wayup/create-listing#step-1-identify-your-nft) Step 1: Identify Your NFT First, identify the NFT you want to list by its policy ID and asset name: Copy // Step 1: Identify the NFT to list console.log(`NFT to list: Policy ID ${NFT_POLICY_ID}, Asset Name ${NFT_ASSET_NAME_HEX}`); ### [](https://dev.ada-anvil.io/wayup/wayup/create-listing#step-2-create-the-listing-payload) Step 2: Create the Listing Payload Next, create the payload for the listing transaction: Copy // Step 2: Create the listing payload const listingPayload = { changeAddress: SELLER_CHANGE_ADDRESS, utxos: SELLER_UTXOS, create: [\ {\ assets: {\ policyId: NFT_POLICY_ID,\ assetName: NFT_ASSET_NAME_HEX\ },\ priceAda: LISTING_PRICE_ADA,\ },\ ], }; console.log(`Creating listing for ${LISTING_PRICE_ADA} ADA`); ### [](https://dev.ada-anvil.io/wayup/wayup/create-listing#step-3-build-the-transaction) Step 3: Build the Transaction Use the `build-tx` endpoint to create the transaction: Copy // Step 3: Build the transaction const response = await fetch(`${NFT_BASE_URL}/build-tx`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(listingPayload), }); const result = await response.json(); console.log("Transaction:", result.transactions); ### [](https://dev.ada-anvil.io/wayup/wayup/create-listing#running-the-example) Running the Example Copy deno run --allow-net create-listing.ts ### [](https://dev.ada-anvil.io/wayup/wayup/create-listing#example-output) Example Output Copy NFT to list: Policy ID 6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01, Asset Name 4646506f776572436f72657331363630 Creating listing for 25 ADA Transaction: [\ "84a900d9010281825820fa7a8f907051db7783be036684481fdce2a5fbcf19f6e5ea5f9f09128288dd1f03018283583911a76f0fb801a29f591e9871576508d85b0b5f3c38774f65032f58fdadb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1e821a00146324a1581c6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01a1504646506f776572436f726573313636300158202aae2382a3cb6a2a215aa112f7b642f41fcc49028c033be8b848381974cbd7b482583901a31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1e821a007a089da1581c6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01a1504646506f776572436f7265733137323001021a00044c71031a09744ed807582010d70a7bf1ff1ed57b4ac55c6ed323880724390905b3f69b92615166c3ac96990b5820846fd5d66b92548cd1e5f9eb7e33d830240d81c5a76930959f86013d150edbab0dd9010281825820fd43f693f0cd4c690ca3adde613262fc41530e62b3cacc906a0056603f5514ae001082583901a31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1e821a007042aaa1581c6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01a34e4646506f776572436f7265733437014f4646506f776572436f72657336323901504646506f776572436f7265733132343801111a00156ecaa104d901029fd8799f9fd8799fd8799fd8799f581ca31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcffd8799fd8799fd8799f581cb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1effffffff1a015b2330ffd8799fd8799fd8799f581c5288ee085dc108f6fdc262b9e0cdfa92663b302836830efc0c5b4fdfffd8799fd8799fd8799f581c41ff84ecc10aae4f3ba8526ba742c739de9d513ace95e17170b1f04cffffffff1a001312d0ffff581cb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1efffff5a11902a2a1636d7367715761797570205472616e73616374696f6e"\ ] [](https://dev.ada-anvil.io/wayup/wayup/create-listing#the-complete-file) The Complete File ------------------------------------------------------------------------------------------------ See our examples repository for the complete file: [create-listing.ts](https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/wayup/create-listing.ts) create-listing.ts[](https://dev.ada-anvil.io/wayup/wayup/create-listing#create-listing.ts) Copy // API endpoint for Wayup Marketplace const NFT_BASE_URL = "https://prod.api.ada-anvil.app/marketplace/api"; // NFT to list for sale const NFT_POLICY_ID = "POLICY_ID"; const NFT_ASSET_NAME_HEX = "ASSET_NAME_HEX"; // Wallet address to receive change const SELLER_CHANGE_ADDRESS = "SELLER_CHANGE_ADDRESS"; // UTXOs from your wallet containing the NFT to list const SELLER_UTXOS: string[] = [\ "SELLER_UTXOS"\ ]; // Listing price in ADA (minimum 5 ADA) const LISTING_PRICE_ADA = 25; // Step 1: Identify the NFT to list console.log(`NFT to list: Policy ID ${NFT_POLICY_ID}, Asset Name ${NFT_ASSET_NAME_HEX}`); // Step 2: Create the listing payload const listingPayload = { changeAddress: SELLER_CHANGE_ADDRESS, utxos: SELLER_UTXOS, create: [\ {\ policyId: NFT_POLICY_ID,\ assetName: NFT_ASSET_NAME_HEX,\ priceAda: LISTING_PRICE_ADA,\ },\ ], }; console.log(`Creating listing for ${LISTING_PRICE_ADA} ADA`); // Step 3: Build the transaction const response = await fetch(`${NFT_BASE_URL}/build-tx`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(listingPayload), }); const result = await response.json(); console.log("Transaction:", result.transactions); // Next steps: Sign the hex with your wallet and submit via /submit endpoint // Run this with: deno run --allow-net create-listing.ts [](https://dev.ada-anvil.io/wayup/wayup/create-listing#next-steps) Next Steps ---------------------------------------------------------------------------------- After building the transaction: 1. Sign the transaction hex with your wallet 2. Submit the signed transaction using the `/submit` endpoint 3. Wait for confirmation on the blockchain 4. Your NFT will be visible as listed on the Wayup Marketplace [](https://dev.ada-anvil.io/wayup/wayup/create-listing#best-practices) Best Practices ------------------------------------------------------------------------------------------ 1. **Pricing Strategy**: Research the collection's floor price before setting your listing price 2. **Wallet Balance**: Ensure your wallet has sufficient ADA for transaction fees 3. **UTXO Verification**: Make sure your UTXOs actually contain the NFT you're trying to list 4. **Asset Verification**: Double-check the policy ID and asset name before listing [](https://dev.ada-anvil.io/wayup/wayup/create-listing#end-to-end-flow) End-to-End Flow -------------------------------------------------------------------------------------------- 1. Confirm you own the NFT you want to sell 2. Build a listing transaction via `/build-tx` with the `create` property 3. Sign the transaction with your wallet. See [Signing Transactions](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/transaction/signing-transaction.md) 4. Submit the transaction via `/submit`. See [Submit Transactions](https://dev.ada-anvil.io/wayup/submit-tx) 5. Wait for confirmation; your NFT will be listed on the Wayup Marketplace [PreviousMake Offer](https://dev.ada-anvil.io/wayup/wayup/create-offer) [NextUpdate Listing](https://dev.ada-anvil.io/wayup/wayup/update-listing) Last updated 16 days ago Was this helpful? --- # Build Transaction | Anvil Development Agency Welcome! This documentation contains hands-on guides and scripts that show how to interact with the Wayup Marketplace backend. You can buy, view, and manage NFTs that are listed for sale almost anywhere on Cardano (JPGStore, Wayup, spacebudz, etc.). You can also list your own NFTs for sale directly on Wayup! * * * [](https://dev.ada-anvil.io/wayup/wayup#the-power-of-build-tx) The Power of `/build-tx` -------------------------------------------------------------------------------------------- The heart of the Wayup Marketplace API is the `**/build-tx**` endpoint. This single, composable endpoint can construct complex Cardano transactions for virtually any NFT marketplace operation: * **Buy** NFTs from listings * **List** your own NFTs for sale * **Create offers** on specific NFTs * **Accept offers** from buyers (documentation coming soon) * **Create collection offers** on any NFT in a collection (documentation coming soon) * **Unlist Listings** NFTs you previously listed * **Update Listings** listing prices ### [](https://dev.ada-anvil.io/wayup/wayup#multi-action-transactions) Multi-Action Transactions What makes `/build-tx` truly powerful is its ability to **combine multiple actions in a single transaction**. For example: Copy { "changeAddress": "addr1...", "utxos": ["8282...", "8282..."], "buy": [\ { "policyId": "policyId", "txHashIndex": "txHashIndex", "priceAda": 50 }\ { "policyId": "policyId", "txHashIndex": "txHashIndex", "priceAda": 30 }\ ], "create": [\ { "policyId": "policyId", "assetName": "assetName", "priceAda": 75 }\ ], "createOffer": [\ { "policyId": "policyId", "assetName": "assetName", "priceAda": 25 }\ ] } This single call would: 1. Buy an NFT listing 2. Buy another NFT listing 3. List another NFT for sale 4. Place an offer on a third NFT All within the same Cardano transaction! ### [](https://dev.ada-anvil.io/wayup/wayup#available-actions) Available Actions The endpoint accepts these action arrays (all optional): Action Type Description `buy` Purchase listed NFTs `create` List NFTs for sale `createOffer` Make offers on specific NFTs `unlist` Remove NFTs from sale `update` Update listing prices ### [](https://dev.ada-anvil.io/wayup/wayup#distribution-and-transactions) Distribution & Transactions The response includes both the raw transaction(s) and a distribution map showing which actions are included in each transaction: Copy { "transactions": ["tx_hex_1", "tx_hex_2"], "distribution": [\ { "buy": [0], "create": [0, 1] },\ { "createOffer": [0] }\ ] } This means: * Transaction 1 covers the first buy action and both create actions * Transaction 2 handles the offer creation * * * [](https://dev.ada-anvil.io/wayup/wayup#documentation-pages) Documentation Pages ------------------------------------------------------------------------------------- Each documentation page focuses on a specific flow: ### [](https://dev.ada-anvil.io/wayup/wayup#build-transaction) Build Transaction * [**Buy NFT**](https://dev.ada-anvil.io/wayup/wayup/buy-nft) : Buy listed NFTs * [**List NFTs**](https://dev.ada-anvil.io/wayup/wayup/create-listing) : List your NFTs for sale * [**Unlist NFTs**](https://dev.ada-anvil.io/wayup/wayup/unlist-listing) : Remove your NFTs from sale * [**Update Listings**](https://dev.ada-anvil.io/wayup/wayup/update-listing) : Update your listings * [**Create Offer**](https://dev.ada-anvil.io/wayup/wayup/create-offer) : Make offers on specific NFTs ### [](https://dev.ada-anvil.io/wayup/wayup#submit-transaction) Submit Transaction * [**Submit Transaction**](https://dev.ada-anvil.io/wayup/submit-tx) : Submit signed transactions ### [](https://dev.ada-anvil.io/wayup/wayup#profile-information) Profile Information * [**Get Profile Listings**](https://dev.ada-anvil.io/wayup/profile-information/get-profile-listings) : Retrieve all active listings for a specific wallet address * [**Get Profile Offers**](https://dev.ada-anvil.io/wayup/profile-information/get-profile-offers) : Retrieve all active offers for a specific wallet address ### [](https://dev.ada-anvil.io/wayup/wayup#collection-information) Collection Information * [**Get Collection Assets**](https://dev.ada-anvil.io/wayup/collection-information/get-collection-assets) : Find and filter NFTs in collections [](https://dev.ada-anvil.io/wayup/wayup#examples) Examples --------------------------------------------------------------- Each documentation page is accompanied by example code demonstrating practical usage of the relevant endpoints. We provide various examples through: * Simple code snippets embedded directly in documentation * Full standalone example files Check the specific documentation pages for detailed examples related to each operation. * * * [](https://dev.ada-anvil.io/wayup/wayup#faqs) FAQs ------------------------------------------------------- **Q: Which network does** `**prod.api.ada-anvil.app**` **target?** Mainnet. **Q: Where do I get policy IDs?** They're visible in Cardano explorers or in your wallet. See [Finding Policy IDs](https://dev.ada-anvil.io/guides/nft-and-ft/native-scripts/policy-units#where-to-find-your-policy-id-and-asset-name) **Q: Can I combine different action types in one transaction?** Yes! The `/build-tx` endpoint is designed for composability. Mix and match actions as needed. **Q: What if my transaction needs multiple signatures?** The `/submit` endpoint accepts an optional `signature` field. For multi-sig, collect all signatures first. [PreviousWeb3 Authentication](https://dev.ada-anvil.io/developer-tools/cip8-auth-guide) [NextBuy NFT](https://dev.ada-anvil.io/wayup/wayup/buy-nft) Last updated 11 days ago Was this helpful? --- # Setup | Anvil Development Agency [](https://dev.ada-anvil.io/guides/smart-contract/escrow/setup#introduction) Introduction ---------------------------------------------------------------------------------------------- In this first part, we'll create a Next.js application as the foundation for our Cardano escrow application. We'll focus on setting up the initial project structure and essential dependencies needed for the wallet integration we'll implement in Part 2. [](https://dev.ada-anvil.io/guides/smart-contract/escrow/setup#prerequisites) Prerequisites ------------------------------------------------------------------------------------------------ Before you begin, ensure you have: * Node.js 18+ installed * Basic familiarity with React and Next.js * A code editor (like VS Code) [](https://dev.ada-anvil.io/guides/smart-contract/escrow/setup#steps) Steps -------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/setup#id-1.-create-a-new-next.js-project) 1\. Create a new Next.js project Start by creating a new Next.js application using create-next-app: Copy npx create-next-app@latest cardano-smart-escrow cd cardano-smart-escrow When prompted for options, select the following: * TypeScript: Yes * ESLint: Yes * Tailwind CSS: Yes * `src/` directory: Yes * App Router: Yes * Use TurboPack: (Optional) * Custom Import Alias (@/\*): No ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/setup#id-2.-install-dependencies) 2\. Install Dependencies For now, we'll only install the essential dependencies needed for our wallet integration: Copy npm install @ada-anvil/weld @tanstack/react-query better-sqlite3 npm install --save-dev @types/better-sqlite3 This installs: * `@ada-anvil/weld`: For Cardano wallet integration. This allows you to connect to a CIP-30 compatible browser wallet (e.g., Eternl, Lace, etc.) * `@tanstack/react-query`: For data fetching and caching * `better-sqlite3`: For local database storage ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/setup#id-3.-environment-variables) 3\. Environment Variables Create a `.env.example` file at the root of your project and populate it with the following: Copy # Anvil API ANVIL_API_ENDPOINT=https://preprod.api.ada-anvil.app/v2/services ANVIL_API_KEY=YOUR_ANVIL_API_KEY # Your Smart Contract Hash (See Blueprint Management Guide) ESCROW_VALIDATOR_HASH=YOUR_ESCROW_VALIDATOR_HASH # Database SQLITE_DB_PATH=./data/escrow.db # Webhook (handled in Step 5) WEBHOOK_SECRET=YOUR_WEBHOOK_SECRET Copy `.env.example` to `.env.local` and fill in your actual values. Don't worry about the actual `WEBHOOK_SECRET` value for now, we'll fill it in later. Make sure to never commit `.env.local`—add it to your `.gitignore`. ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/setup#id-4.-project-structure) 4\. Project Structure Below is the complete directory structure you'll build throughout this guide series. This provides a roadmap of what we'll be creating: Copy cardano-smart-escrow/ ├── src/ │ ├── app/ │ │ ├── api/ # API Routes │ │ │ ├── escrow/ # Escrow API endpoints │ │ │ │ ├── lock/ # Fund locking endpoint │ │ │ │ │ └── route.ts │ │ │ │ ├── submit/ # Transaction submission │ │ │ │ │ └── route.ts │ │ │ │ ├── transactions/ # Transaction listing │ │ │ │ │ └── route.ts │ │ │ │ └── unlock/ # Fund unlocking endpoint │ │ │ │ └── route.ts │ │ │ └── webhooks/ # External service handlers │ │ │ └── blockfrost/ # Blockchain event notifications │ │ │ └── route.ts │ │ ├── globals.css # Global styles │ │ ├── layout.tsx # Main app layout │ │ └── page.tsx # Home page │ ├── components/ # React components │ │ ├── LockFundsForm.tsx # Form for locking ADA │ │ ├── MyTransactions.tsx # Transaction history display │ │ ├── ReactQueryProvider.tsx # Data fetching provider │ │ ├── WalletConnector.tsx # Wallet connection UI │ │ └── WeldProvider.tsx # Wallet provider setup │ ├── hooks/ # Custom React hooks │ │ ├── useAmountSlider.ts # ADA amount input slider │ │ └── useTransactions.ts # Transaction data management │ └── lib/ # Utility functions and types │ ├── anvil-api.ts # Anvil API integration │ ├── db.ts # SQLite database functions │ └── types.ts # TypeScript type definitions ├── .env.example # Example configuration └── .env.local # Local config (gitignored) ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/setup#id-5.-update-global-styles) 5\. Update Global Styles Update the global CSS file with some basic styles we'll use throughout the application `src/globals.css`: The CSS details are not important for the guide. This just helps with the styling of the components we'll build. Copy @import "tailwindcss"; :root { --background: #ffffff; --foreground: #171717; } @theme inline { --color-background: var(--background); --color-foreground: var(--foreground); --font-sans: var(--font-geist-sans); --font-mono: var(--font-geist-mono); } @media (prefers-color-scheme: dark) { :root { --background: #0a0a0a86; --foreground: #ededed; } } body { background: var(--background); color: var(--foreground); font-family: Arial, Helvetica, sans-serif; } @layer components { .button-primary { @apply border-2 border-neutral-800 rounded-2xl px-4 py-2 font-bold shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] transition-all hover:translate-x-1 hover:translate-y-1 hover:shadow-none bg-white hover:bg-gray-100 text-black; } .section-card { @apply border-2 border-neutral-800 rounded-2xl p-4 mb-4 shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] bg-white; } /* Custom slider styles for cross-browser compatibility */ .slider-thumb { @apply appearance-none; } /* Webkit browsers (Chrome, Safari) */ .slider-thumb::-webkit-slider-thumb { @apply appearance-none w-4 h-4 bg-black rounded-full cursor-pointer; } /* Firefox */ .slider-thumb::-moz-range-thumb { @apply appearance-none w-4 h-4 bg-black rounded-full cursor-pointer border-0; } /* Microsoft Edge */ .slider-thumb::-ms-thumb { @apply appearance-none w-4 h-4 bg-black rounded-full cursor-pointer; } } ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/setup#id-6.-configure-root-layout) 6\. Configure Root Layout Create a basic root layout component `src/app/layout.tsx`: Copy // src/app/layout.tsx import './globals.css'; import type { Metadata } from 'next'; export const metadata: Metadata = { title: 'Cardano Escrow', description: 'Lock and unlock funds securely on the Cardano blockchain', }; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( {children} ); } ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/setup#id-7.-create-basic-home-page) 7\. Create Basic Home Page Create a simple home page as a placeholder `src/app/page.tsx`: Copy // src/app/page.tsx export default function Page() { return (

Cardano Escrow

{/* Wallet connector will go here in Part 2 */} {/* Fund locking will go here in Part 3 */} {/* Transaction dashboard will go here in Part 4 */}
); } [](https://dev.ada-anvil.io/guides/smart-contract/escrow/setup#testing-your-setup) Testing Your Setup ---------------------------------------------------------------------------------------------------------- Let's make sure your basic setup is working correctly: 1. Start your development server: Copy npm run dev 1. Navigate to http://localhost:3000 in your browser 2. You should see the placeholder home page with the title "Cardano Escrow" Congratulations! You've completed Part 1 of the guide. You now have a basic Next.js project set up for our Cardano escrow application. [PreviousEnd-to-End Next.js Escrow Guide](https://dev.ada-anvil.io/guides/smart-contract/escrow) [NextWallet Integration](https://dev.ada-anvil.io/guides/smart-contract/escrow/wallet-integration) Last updated 2 months ago Was this helpful? --- # Wallet Integration | Anvil Development Agency [](https://dev.ada-anvil.io/guides/smart-contract/escrow/wallet-integration#introduction) Introduction ----------------------------------------------------------------------------------------------------------- In this part, we'll implement wallet connectivity using [Weld](https://github.com/Cardano-Forge/weld) , a universal wallet connector library that provides a simple, consistent interface for connecting to Cardano wallets. Weld handles all the complexity of wallet discovery, connection management, and state persistence, allowing our application to interact with various wallet providers (Eternl, Lace, etc.) through a single API. [](https://dev.ada-anvil.io/guides/smart-contract/escrow/wallet-integration#implementation-steps) Implementation Steps --------------------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/wallet-integration#id-1.-create-the-weld-provider) 1\. Create the Weld Provider First, let's create a provider component that will initialize the Weld library and make wallet functionality available throughout our application `src/components/WeldProvider.tsx`: Copy // src/components/WeldProvider.tsx "use client"; import { WeldProvider, type WeldProviderProps } from "@ada-anvil/weld/react"; export function ClientWeldProvider({ children, lastConnectedWallet, }: { children: React.ReactNode; lastConnectedWallet?: NonNullable< WeldProviderProps["wallet"] >["tryToReconnectTo"]; }) { return ( {children} ); } ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/wallet-integration#id-2.-update-root-layout) 2\. Update Root Layout Now we need to update the root layout to include our Weld provider `src/app/layout.tsx`: Copy import type { Metadata } from "next"; import { Geist, Geist_Mono } from "next/font/google"; import "./globals.css"; import { ClientWeldProvider } from "@/components/WeldProvider"; import { cookies } from "next/headers"; import { STORAGE_KEYS } from "@ada-anvil/weld/server"; const geistSans = Geist({ variable: "--font-geist-sans", subsets: ["latin"], }); const geistMono = Geist_Mono({ variable: "--font-geist-mono", subsets: ["latin"], }); export const metadata: Metadata = { title: "Cardano Escrow", description: "Escrow solution for Cardano blockchain", }; export default async function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { // Retrieve wallet connection state from cookies for SSR const cookieStore = await cookies(); const wallet = cookieStore.get(STORAGE_KEYS.connectedWallet)?.value; const changeAddress = cookieStore.get(STORAGE_KEYS.connectedChange)?.value; const stakeAddress = cookieStore.get(STORAGE_KEYS.connectedStake)?.value; const lastConnectedWallet = wallet ? { wallet, changeAddress, stakeAddress } : undefined; return ( {children} ); } ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/wallet-integration#id-3.-create-the-wallet-connector-component) 3\. Create the Wallet Connector Component Now, let's create a component for users to connect their wallets. It will: 1. Use Weld's `useWallet` hook to interact with the user's wallet 2. Display a connect button when no wallet is connected 3. Show wallet information (address and balance) when connected 4. Provide a disconnect button for connected wallets 5. Format the wallet's lovelace balance to show readable ADA amounts Create the `WalletConnector` component `src/components/WalletConnector.tsx`: Copy // src/components/WalletConnector.tsx "use client"; import { useWallet, useExtensions } from "@ada-anvil/weld/react"; import { SUPPORTED_WALLETS } from "@ada-anvil/weld"; import { useState } from "react"; // Component to display wallet information const WalletInfo = ({ label, value }: { label: string; value: string }) => (
{label} {value}
); // Format wallet address to show abbreviated form const formatAddress = (address?: string) => { if (!address) return ""; return `${address.slice(0, 8)}...${address.slice(-8)}`; }; /** * WalletConnector component handles wallet connection functionality * - Detects available Cardano wallets * - Allows users to connect/disconnect their wallet * - Displays wallet information when connected */ export default function WalletConnector() { const wallet = useWallet(); const { supportedMap: installedWallets, isLoading } = useExtensions( "supportedMap", "isLoading" ); // Filter to only show available wallets that are installed const availableWallets = SUPPORTED_WALLETS.filter((w) => installedWallets.has(w.key), ); const [selectedWallet, setSelectedWallet] = useState(""); // Handle wallet selection from dropdown const handleWalletSelection = (walletKey: string) => { setSelectedWallet(walletKey); }; // Connect to selected wallet const handleConnect = async (walletKey?: string) => { if (!walletKey) return; try { await wallet.connectAsync(walletKey); } catch (error) { console.error("Failed to connect wallet:", error); } }; return (

Wallet

{wallet.isConnected ? ( // Connected state - show wallet info and disconnect button <> ) : isLoading ? ( // Loading state
Detecting wallet extensions...
) : ( // Disconnected state - show wallet selector and connect button
)}
); } ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/wallet-integration#id-3.-update-home-page) 3\. Update Home Page Update the home page to include the wallet connector component `src/app/page.tsx`: Copy // src/app/page.tsx import WalletConnector from "@/components/WalletConnector"; export default function Page() { return (

Cardano Escrow

); } [](https://dev.ada-anvil.io/guides/smart-contract/escrow/wallet-integration#understanding-weld-integration) Understanding Weld Integration ----------------------------------------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/wallet-integration#the-weld-provider) The Weld Provider The `WeldProvider` component creates a reactive context that makes wallet functionality available throughout your application: * **Universal API**: Provides a consistent interface regardless of wallet implementation * **Network Configuration**: Automatically configures for testnet (preview/preprod) or mainnet * **State Management**: Handles connection state and persistence across page refreshes ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/wallet-integration#the-usewallet-hook) The useWallet Hook The `useWallet` hook exposes wallet functionality with smart TypeScript type inference: * **Connection State**: `isConnected` works as a type guard for other properties * **Wallet Data**: Access addresses, balances, and wallet metadata * **Actions**: Methods for connecting, disconnecting, and signing transactions * **Reactive Updates**: Properties update automatically when wallet state changes ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/wallet-integration#key-concepts) Key Concepts * **Wallet Discovery**: Weld detects all CIP-30 compatible wallets installed in the browser * **Address Formats**: Works with Bech32 addresses (human-readable format starting with `addr_`) * **Balance Units**: Converts between lovelace (smallest unit) and ADA (1 ADA = 1,000,000 lovelace) * **Connection Persistence**: Maintains wallet connection across page reloads [](https://dev.ada-anvil.io/guides/smart-contract/escrow/wallet-integration#testing-your-wallet-integration) Testing Your Wallet Integration ------------------------------------------------------------------------------------------------------------------------------------------------- To test your wallet integration: 1. Start your development server: Copy npm run dev 1. Visit http://localhost:3000 in your browser 2. Make sure you have a CIP-30 compatible wallet extension installed * Fully-tested wallets include: Eternl, Lace, Flint, GeroWallet, Typhon, and Vespr 3. Click the "Connect Wallet" button 4. Select your wallet from the popup and approve the connection 5. Verify that your address and balance are displayed correctly [](https://dev.ada-anvil.io/guides/smart-contract/escrow/wallet-integration#troubleshooting) Troubleshooting ----------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/wallet-integration#wallet-not-detected) Wallet Not Detected If your wallet isn't detected: * Verify the wallet extension is installed, unlocked and refreshed after installation * Confirm that your wallet implements the CIP-30 standard * Try clearing browser cache or restarting your browser ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/wallet-integration#network-mismatch) Network Mismatch If you see connection errors: * Ensure your wallet is set to the same network as your application (Preview/Preprod) * Remember that wallets require explicit testnet mode activation * Check the wallet's network selector in its settings menu For development, you'll need to enable testnet mode in your wallet. This is typically found in the wallet's settings or network preferences. Congratulations! You've completed Part 2 of the guide. Your application can now connect to Cardano wallets and display the user's address and balance. [PreviousSetup](https://dev.ada-anvil.io/guides/smart-contract/escrow/setup) [NextFund Locking](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-locking) Last updated 2 months ago Was this helpful? --- # Unlist Listing | Anvil Development Agency [](https://dev.ada-anvil.io/wayup/wayup/unlist-listing#introduction) Introduction -------------------------------------------------------------------------------------- This guide demonstrates how to remove (unlist) an existing NFT listing from the Wayup Marketplace using the API. This returns the NFT to the seller's wallet and removes it from sale on the marketplace. [](https://dev.ada-anvil.io/wayup/wayup/unlist-listing#requirements) Requirements -------------------------------------------------------------------------------------- * The Policy ID of the NFT you've listed * The txHashIndex of your existing listing * UTXOs from your wallet to fund the transaction * Your change address to receive the unlisted NFT and any change [](https://dev.ada-anvil.io/wayup/wayup/unlist-listing#api-request-structure) API Request Structure -------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/wayup/wayup/unlist-listing#configuration) Configuration Copy // API endpoint for Wayup Marketplace const BASE_URL = "https://prod.api.ada-anvil.app/marketplace/api"; // NFT listing to unlist const POLICY_ID = "6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01"; const TX_HASH_INDEX = "4baa167631a62343cc4f37f9313ee05d6149b1fda39fd563e12832c8dd49fac9#0"; // Format: txHash#outputIndex // Wallet address to receive change and the unlisted NFT const SELLER_ADDRESS = "addr1qx33ycd2ymg02dxh6vnnf8dsk54l8ranr9cfjk9qrlj3309c69jc4n8h3uvnczncaqu2sm03pl99h99n75uvtl3mhv0q3z8s8m"; // UTXOs from your wallet to fund the transaction const SELLER_UTXOS = [\ "828258204baa167631a62343cc4f37f9313ee05d6149b1fda39fd563e12832c8dd49fac90182583901a31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1e821a001344eea1581c6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01a24f4646506f776572436f72657336323901504646506f776572436f7265733132343801",\ "828258204baa167631a62343cc4f37f9313ee05d6149b1fda39fd563e12832c8dd49fac90282583901a31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1e1a005b1fe1",\ "82825820daac1d3b80dcab8817c2f02f2d43ab2b33a4e74419679939eb8aa5f70b03f35c0182583901a31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1e821a007a089da1581c6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01a1504646506f776572436f7265733137323001"\ ]; ### [](https://dev.ada-anvil.io/wayup/wayup/unlist-listing#type-definitions) Type Definitions Copy interface UnlistInput { policyId: string; txHashIndex: string; // Format: txHash#outputIndex } interface BuildTxPayload { changeAddress: string; utxos: string[]; unlist: UnlistInput[]; } [](https://dev.ada-anvil.io/wayup/wayup/unlist-listing#implementation) Implementation ------------------------------------------------------------------------------------------ ### [](https://dev.ada-anvil.io/wayup/wayup/unlist-listing#step-1-identify-the-nft-listing-to-unlist) Step 1: Identify the NFT Listing to Unlist First, identify the NFT listing you want to remove: Copy // Step 1: Identify the NFT listing to unlist console.log(`Unlisting NFT: Policy ID ${POLICY_ID}, TX Hash Index ${TX_HASH_INDEX}`); ### [](https://dev.ada-anvil.io/wayup/wayup/unlist-listing#step-2-prepare-the-unlist-payload) Step 2: Prepare the Unlist Payload Next, prepare the payload with your listing information: Copy // Step 2: Prepare the payload for the build-tx endpoint const payload = { changeAddress: SELLER_ADDRESS, utxos: SELLER_UTXOS, unlist: [\ {\ policyId: POLICY_ID,\ txHashIndex: TX_HASH_INDEX\ }\ ] }; ### [](https://dev.ada-anvil.io/wayup/wayup/unlist-listing#step-3-build-the-transaction) Step 3: Build the Transaction Send the request to the build-tx endpoint: Copy // Step 3: Build the transaction console.log("Building unlist transaction..."); const response = await fetch(`${BASE_URL}/build-tx`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), }); ### [](https://dev.ada-anvil.io/wayup/wayup/unlist-listing#step-4-process-the-response) Step 4: Process the Response Process the response to get the transaction hex: Copy // Step 4: Process the response const result = await response.json(); console.log("Transaction:", result.transactions); // Next steps: Sign the transaction with your wallet and submit via /submit endpoint console.log("\nNext steps:"); console.log("1. Sign the transaction with your wallet"); console.log("2. Submit the signed transaction using the /submit endpoint"); console.log("3. After transaction confirms, your NFT will be returned to your wallet"); ### [](https://dev.ada-anvil.io/wayup/wayup/unlist-listing#running-the-example) Running the Example Copy deno run --allow-net unlist-listing.ts ### [](https://dev.ada-anvil.io/wayup/wayup/unlist-listing#example-output) Example Output Copy Unlisting NFT: Policy ID 6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01, TX Hash Index 4baa167631a62343cc4f37f9313ee05d6149b1fda39fd563e12832c8dd49fac9#0 Building unlist transaction... Transaction: [\ "84ab00d90102828258204baa167631a62343cc4f37f9313ee05d6149b1fda39fd563e12832c8dd49fac900825820daac1d3b80dcab8817c2f02f2d43ab2b33a4e74419679939eb8aa5f70b03f35c01018182583901a31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1e821a008931eaa1581c6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01a24e4646506f776572436f726573343701504646506f776572436f7265733137323001021a0005182b031a0974500607582010d70a7bf1ff1ed57b4ac55c6ed323880724390905b3f69b92615166c3ac96990b5820ccc11c6ff4ed2cb25a6973e55baff16c1630418670c77cba16ac163e0f4698aa0dd90102818258204baa167631a62343cc4f37f9313ee05d6149b1fda39fd563e12832c8dd49fac9020ed9010281581ca31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bc1082583901a31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1e1a00446df8111a0016b1e912d90102818258201693c508b6132e89b932754d657d28b24068ff5ff1715fec36c010d4d6470b3d00a204d901029fd8799f9fd8799fd8799fd8799f581c5288ee085dc108f6fdc262b9e0cdfa92663b302836830efc0c5b4fdfffd8799fd8799fd8799f581c41ff84ecc10aae4f3ba8526ba742c739de9d513ace95e17170b1f04cffffffff1a000f4240ffd8799fd8799fd8799f581ca31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcffd8799fd8799fd8799f581cb8d1658accf78f193c0a78e838a86df10fca5b94b3f538c5fe3bbb1effffffff1a002dc6c0ffff581ca31261aa26d0f534d7d327349db0b52bf38fb319709958a01fe518bcffff05a182000082d87a80821a000205a91a02367e62f5a11902a2a1636d7367715761797570205472616e73616374696f6e"\ ] Next steps: 1. Sign the transaction with your wallet 2. Submit the signed transaction using the /submit endpoint 3. After transaction confirms, your NFT will be returned to your wallet [](https://dev.ada-anvil.io/wayup/wayup/unlist-listing#the-complete-file) The Complete File ------------------------------------------------------------------------------------------------ See our examples repository for the complete file: [unlist-listing.ts](https://github.com/Cardano-Forge/anvil-api-examples/blob/main/documentation-references/wayup/unlist-listing.ts) unlist-listing.ts[](https://dev.ada-anvil.io/wayup/wayup/unlist-listing#unlist-listing.ts) Copy // API endpoint for Wayup Marketplace const BASE_URL = "https://prod.api.ada-anvil.app/marketplace/api"; // NFT listing to unlist const POLICY_ID = "POLICY_ID"; const TX_HASH_INDEX = "TX_HASH_INDEX"; // Format: txHash#outputIndex // Wallet address to receive change and the unlisted NFT const SELLER_ADDRESS = "SELLER_ADDRESS"; // UTXOs from your wallet to fund the transaction const SELLER_UTXOS = [\ "SELLER_UTXOS"\ ]; // Step 1: Identify the NFT listing to unlist console.log(`Unlisting NFT: Policy ID ${POLICY_ID}, TX Hash Index ${TX_HASH_INDEX}`); // Step 2: Prepare the payload for the build-tx endpoint const payload = { changeAddress: SELLER_ADDRESS, utxos: SELLER_UTXOS, unlist: [\ {\ policyId: POLICY_ID,\ txHashIndex: TX_HASH_INDEX\ }\ ] }; // Step 3: Build the transaction console.log("Building unlist transaction..."); const response = await fetch(`${BASE_URL}/build-tx`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), }); // Step 4: Process the response const result = await response.json(); console.log("Transaction:", result.transactions); // Next steps: Sign the transaction with your wallet and submit via /submit endpoint console.log("\nNext steps:"); console.log("1. Sign the transaction with your wallet"); console.log("2. Submit the signed transaction using the /submit endpoint"); console.log("3. After transaction confirms, your NFT will be returned to your wallet"); // Run this with: deno run --allow-net unlist-listing.ts [](https://dev.ada-anvil.io/wayup/wayup/unlist-listing#best-practices) Best Practices ------------------------------------------------------------------------------------------ 1. **Verify Listing Ownership**: Always ensure you are the owner of the listing you're unlisting. 2. **Transaction Confirmation**: After submitting the signed transaction, verify it was confirmed on the blockchain. 3. **NFT Return Verification**: Check your wallet to confirm the NFT has been returned after the transaction confirms. 4. **Listing Status**: Verify that the NFT no longer appears in your active listings using the `get-profile-listings` endpoint with the appropriate filters. [](https://dev.ada-anvil.io/wayup/wayup/unlist-listing#end-to-end-flow) End-to-End Flow -------------------------------------------------------------------------------------------- 1. Identify the NFT listing to unlist (Policy ID and txHashIndex) 2. Build an unlist transaction via `/build-tx` with the `unlist` property 3. Sign the transaction with your wallet. See [Signing Transactions](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/transaction/signing-transaction.md) 4. Submit the transaction via `/submit`. See [Submit Transactions](https://dev.ada-anvil.io/wayup/submit-tx) 5. Wait for confirmation; your NFT will be returned to your wallet address 6. Verify the NFT is in your wallet and no longer listed on the marketplace [PreviousUpdate Listing](https://dev.ada-anvil.io/wayup/wayup/update-listing) [NextSubmit Transaction](https://dev.ada-anvil.io/wayup/submit-tx) Last updated 16 days ago Was this helpful? --- # Profile Listings | Anvil Development Agency [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-listings#introduction) Introduction ---------------------------------------------------------------------------------------------------------- This guide demonstrates how to retrieve all active listings for a specific wallet address on the Wayup Marketplace. This endpoint is useful for building user profiles, dashboards, or portfolio trackers that show what NFTs a user currently has listed for sale. [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-listings#requirements) Requirements ---------------------------------------------------------------------------------------------------------- * A valid Cardano wallet address [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-listings#api-request-structure) API Request Structure ---------------------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-listings#configuration) Configuration Copy // API endpoint for Wayup Marketplace const BASE_URL = "https://prod.api.ada-anvil.app/marketplace/api"; // Wallet address to query listings for const WALLET_ADDRESS = "addr1qx33ycd2ymg02dxh6vnnf8dsk54l8ranr9cfjk9qrlj3309c69jc4n8h3uvnczncaqu2sm03pl99h99n75uvtl3mhv0q3z8s8m"; ### [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-listings#type-definitions) Type Definitions Copy interface ProfileListingsRequest { address: string; limit?: number; cursor?: string; policyId?: string; term?: string; } interface AssetResult { name: string; policyId: string; assetName: string; media: string; listing: { txHashIndex: string; price: string; priceCurrency: string; scriptHash: string; bundleSize?: number; isProcessing: boolean; type: string; version: string; fees: { service: { pct: number; }; royalty: { minLovelace: number; pct: number; addr: string; } | null; }; }; collection?: { policyId: string; name: string; image?: string; royaltyPct?: number; royaltyAddress?: string; }; // Additional fields omitted for brevity } interface ProfileListingsResponse { results: AssetResult[]; pageState: string | null; } [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-listings#implementation) Implementation -------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-listings#step-1-build-the-request-url) Step 1: Build the Request URL Build the URL with the required parameters: Copy // Step 1: Build the request URL //Optional parameters const PAGE_LIMIT = 20; // Number of results to return const PAGE_CURSOR = null; // Pagination cursor (leave as null for first page) const POLICY_ID = null; // Optional: filter by policy ID const SEARCH_TERM = null; // Optional: search term for filtering by name const requestUrl = `${BASE_URL}/get-profile-listings?address=${WALLET_ADDRESS}`; // If you want to include additional parameters: // const requestUrl = `${BASE_URL}/get-profile-listings?address=${WALLET_ADDRESS}&limit=${PAGE_LIMIT}${PAGE_CURSOR ? `&cursor=${PAGE_CURSOR}` : ''}${POLICY_ID ? `&policyId=${POLICY_ID}` : ''}${SEARCH_TERM ? `&term=${encodeURIComponent(SEARCH_TERM)}` : ''}`; ### [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-listings#step-2-execute-the-request) Step 2: Execute the Request Send the request to the API: Copy // Step 2: Execute the request console.log("Fetching user's NFT listings..."); const response = await fetch(requestUrl); ### [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-listings#step-3-process-the-response) Step 3: Process the Response Parse the API response: Copy // Step 3: Process the response const result = await response.json(); const { results, pageState } = result; ### [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-listings#step-4-display-the-results) Step 4: Display the Results Process and display the listings data: Copy // Step 4: Display the results console.log(`Found ${results.length} listings for address ${WALLET_ADDRESS}`); console.log("Listings:"); results.forEach((item, index) => { console.log(`\n--- Listing ${index + 1} ---`); console.log(`Name: ${item.name}`); console.log(`Policy ID: ${item.policyId}`); console.log(`Asset Name: ${item.assetName}`); console.log(`Price: ${Number(item.listing.price) / 1000000} ADA`); console.log(`Tx Hash Index: ${item.listing.txHashIndex}`); console.log("\nListing Marketplace: " + item.listing.type); }); ### [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-listings#step-5-handle-pagination) Step 5: Handle Pagination Handle pagination for large result sets: Copy // Step 5: Handle pagination if (pageState) { console.log(`\nMore results available. Use this cursor for the next page: ${pageState}`); } ### [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-listings#running-the-example) Running the Example Copy deno run --allow-net get-profile-listings.ts ### [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-listings#example-output) Example Output Copy Fetching user's NFT listings... Found 2 listings for address addr1qx33ycd2ymg02dxh6vnnf8dsk54l8ranr9cfjk9qrlj3309c69jc4n8h3uvnczncaqu2sm03pl99h99n75uvtl3mhv0q3z8s8m Listings: --- Listing 1 --- Name: Serene Power Core Policy ID: 6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01 Asset Name: 4646506f776572436f72657331363630 Price: 25 ADA Tx Hash Index: daac1d3b80dcab8817c2f02f2d43ab2b33a4e74419679939eb8aa5f70b03f35c#0 Listing Marketplace: wayup --- Listing 2 --- Name: Shimmering Power Core Policy ID: 6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01 Asset Name: 4646506f776572436f7265733437 Price: 5 ADA Tx Hash Index: 4baa167631a62343cc4f37f9313ee05d6149b1fda39fd563e12832c8dd49fac9#0 Listing Marketplace: jpgstore [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-listings#the-complete-file) The Complete File -------------------------------------------------------------------------------------------------------------------- get-profile-listings.ts[](https://dev.ada-anvil.io/wayup/profile-information/get-profile-listings#get-profile-listings.ts) Copy // API endpoint for Wayup Marketplace const BASE_URL = "https://prod.api.ada-anvil.app/marketplace/api"; // Wallet address to query listings for const WALLET_ADDRESS = "addr1qx33ycd2ymg02dxh6vnnf8dsk54l8ranr9cfjk9qrlj3309c69jc4n8h3uvnczncaqu2sm03pl99h99n75uvtl3mhv0q3z8s8m"; // Optional parameters const PAGE_LIMIT = 20; // Number of results to return const PAGE_CURSOR = null; // Pagination cursor (leave as null for first page) const POLICY_ID = null; // Optional: filter by policy ID const SEARCH_TERM = null; // Optional: search term for filtering by name // Step 1: Build the request URL const requestUrl = `${BASE_URL}/get-profile-listings?address=${WALLET_ADDRESS}`; // If you want to include additional parameters: // const requestUrl = `${BASE_URL}/get-profile-listings?address=${WALLET_ADDRESS}&limit=${PAGE_LIMIT}${PAGE_CURSOR ? `&cursor=${PAGE_CURSOR}` : ''}${POLICY_ID ? `&policyId=${POLICY_ID}` : ''}${SEARCH_TERM ? `&term=${encodeURIComponent(SEARCH_TERM)}` : ''}`; // Step 2: Execute the request console.log("Fetching user's NFT listings..."); const response = await fetch(requestUrl); // Step 3: Process the response const result = await response.json(); const { results, pageState } = result; // Step 5: Display the results console.log(`Found ${results.length} listings for address ${WALLET_ADDRESS}`); console.log("Listings:"); results.forEach((item, index) => { console.log(`\n--- Listing ${index + 1} ---`); console.log(`Name: ${item.name}`); console.log(`Policy ID: ${item.policyId}`); console.log(`Asset Name: ${item.assetName}`); console.log(`Price: ${Number(item.listing.price) / 1000000} ADA`); console.log(`Tx Hash Index: ${item.listing.txHashIndex}`); console.log("\nListing Marketplace: " + item.listing.type); }); // Step 6: Handle pagination if (pageState) { console.log(`\nMore results available. Use this cursor for the next page: ${pageState}`); } // Run this with: deno run --allow-net get-profile-listings.ts [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-listings#best-practices) Best Practices -------------------------------------------------------------------------------------------------------------- 1. **Input Validation**: Always validate the wallet address before making the API call. 2. **Error Handling**: Implement proper error handling for network issues or invalid responses. 3. **Pagination**: Always handle pagination for users with many listings. 4. **Filtering**: Use the policyId parameter when you want to focus on a specific collection. 5. **Search**: The term parameter is useful for searching NFTs by name. [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-listings#end-to-end-flow) End-to-End Flow ---------------------------------------------------------------------------------------------------------------- 1. Obtain a wallet address from your user 2. Query the `/get-profile-listings` endpoint with the wallet address 3. Display the listing results (name, image, price, etc.) 4. Implement pagination using the returned pageState if there are more results 5. Allow filtering by collection using the policyId parameter 6. Enable searching by name using the term parameter [PreviousProfile Information](https://dev.ada-anvil.io/wayup/profile-information) [NextProfile Offers](https://dev.ada-anvil.io/wayup/profile-information/get-profile-offers) Last updated 1 month ago Was this helpful? --- # Transaction Dashboard | Anvil Development Agency [](https://dev.ada-anvil.io/guides/smart-contract/escrow/transaction-dashboard#introduction) Introduction -------------------------------------------------------------------------------------------------------------- In this part, we'll build a transaction dashboard that displays the user's transaction history and status. This will allow users to monitor their locked funds and see when transactions are confirmed on the blockchain. We'll also set up a database to persistently store transaction data. [](https://dev.ada-anvil.io/guides/smart-contract/escrow/transaction-dashboard#database-configuration) Database Configuration ---------------------------------------------------------------------------------------------------------------------------------- In previous parts, we built a wallet connector and fund locking functionality, but we didn't store transaction information. Let's add database support now to track transactions. ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/transaction-dashboard#id-1.-create-data-directory) 1\. Create Data Directory Create a directory for the SQLite database. Add a `data` directory to the root folder. This will be used to store the database file `escrow.db`. ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/transaction-dashboard#id-2.-set-up-the-database) 2\. Set Up the Database Create a database utility file `src/lib/db.ts`: Copy // src/lib/db.ts import sqlite from 'better-sqlite3'; import path from 'path'; import { TransactionStatus } from './types'; const dbPath = process.env.SQLITE_DB_PATH || './data/escrow.db'; const db = sqlite(dbPath); // Initialize database tables db.exec(` CREATE TABLE IF NOT EXISTS wallets ( address TEXT PRIMARY KEY ); CREATE TABLE IF NOT EXISTS transactions ( txHash TEXT PRIMARY KEY, wallet TEXT NOT NULL, amount INTEGER NOT NULL, status TEXT NOT NULL, timestamp INTEGER NOT NULL, FOREIGN KEY (wallet) REFERENCES wallets(address) ); `); export function upsertWallet(address: string) { db.prepare(`INSERT OR IGNORE INTO wallets(address) VALUES (?)`).run(address); } export function upsertTx( txHash: string, wallet: string, amount: number, status: TransactionStatus ) { db.prepare( `INSERT OR REPLACE INTO transactions( txHash, wallet, amount, status, timestamp ) VALUES (?, ?, ?, ?, ?)` ).run(txHash, wallet, amount, status, Date.now()); } export function getTxsByWallet(wallet: string) { return db .prepare(`SELECT * FROM transactions WHERE wallet = ? ORDER BY timestamp DESC`) .all(wallet); } export function updateTxStatus( txHash: string, status: TransactionStatus ) { db.prepare( `UPDATE transactions SET status = ?, timestamp = ? WHERE txHash = ?` ).run(status, Date.now(), txHash); } export function getTxsByHash(txHash: string) { return db .prepare(`SELECT * FROM transactions WHERE txHash = ?`) .get(txHash); } [](https://dev.ada-anvil.io/guides/smart-contract/escrow/transaction-dashboard#react-query-setup) React Query Setup ------------------------------------------------------------------------------------------------------------------------ Now let's set up React Query for data fetching `src/components/ReactQueryProvider.tsx`: ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/transaction-dashboard#id-1.-create-the-react-query-provider) 1\. Create the React Query Provider Copy // src/components/ReactQueryProvider.tsx "use client"; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { ReactNode } from 'react'; const queryClient = new QueryClient(); export default function ReactQueryProvider({ children }: { children: ReactNode }) { return ( {children} ); } ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/transaction-dashboard#id-2.-update-root-layout) 2\. Update Root Layout Update the root layout to include the React Query provider `src/app/layout.tsx`: Copy // src/app/layout.tsx import './globals.css'; import type { Metadata } from 'next'; import CustomWeldProvider from '@/components/WeldProvider'; import ReactQueryProvider from '@/components/ReactQueryProvider'; export const metadata: Metadata = { title: 'Cardano Escrow', description: 'Lock and unlock funds securely on the Cardano blockchain', }; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( {children} ); } [](https://dev.ada-anvil.io/guides/smart-contract/escrow/transaction-dashboard#transaction-api-endpoint) Transaction API Endpoint -------------------------------------------------------------------------------------------------------------------------------------- Let's create an API endpoint to fetch transactions for a specific wallet `src/app/api/escrow/transactions/route.ts`: Copy import { NextRequest, NextResponse } from 'next/server'; import { getTxsByWallet } from '@/lib/db'; export async function GET(request: NextRequest) { const url = new URL(request.url); const wallet = url.searchParams.get('wallet'); // Validate wallet parameter if (!wallet) { return NextResponse.json( { error: 'Missing wallet parameter' }, { status: 400 } ); } try { const txs = getTxsByWallet(wallet); return NextResponse.json(txs); } catch (error: unknown) { console.error('Error fetching transactions:', error); const message = error instanceof Error ? error.message : String(error); return NextResponse.json( { error: message }, { status: 500 } ); } } [](https://dev.ada-anvil.io/guides/smart-contract/escrow/transaction-dashboard#update-submit-transaction-endpoint) Update Submit Transaction Endpoint ---------------------------------------------------------------------------------------------------------------------------------------------------------- Let's update the submit endpoint to store transaction data in the database `src/app/api/escrow/submit/route.ts`: Copy // src/app/api/escrow/submit/route.ts import { NextRequest, NextResponse } from 'next/server'; import { submitTransaction } from '@/lib/anvil-api'; import { TX_STATUS } from '@/lib/types'; import { upsertWallet, updateTxStatus } from '@/lib/db'; export async function POST(request: NextRequest) { try { const body = await request.json(); // Add changeAddress from the request body const { complete, signature, changeAddress } = body; // Validate inputs if (!complete || !signature || !changeAddress) { return NextResponse.json( { error: 'Missing complete transaction or signature' }, { status: 400 } ); } const result = await submitTransaction(signature, complete); // Normally we would have this as PENDING, but since we're simulating a confirmation // we set it to CONFIRMED. When we get live updates from the blockchain, we'll update // the status to PENDING. updateTxStatus(result.txHash, TX_STATUS.CONFIRMED); return NextResponse.json({ txHash: result.txHash }); } catch (error: unknown) { console.error('Error submitting transaction:', error); const message = error instanceof Error ? error.message : String(error); return NextResponse.json( { error: message || 'Failed to submit transaction' }, { status: 500 } ); } } [](https://dev.ada-anvil.io/guides/smart-contract/escrow/transaction-dashboard#update-transaction-hooks) Update Transaction Hooks -------------------------------------------------------------------------------------------------------------------------------------- Add the following functions to `src/hooks/useTransactions.ts`: 1. fetchTransactions - Fetches transactions from the API 2. useTransactionsByWallet - Retrieves transactions for a specific wallet Copy // src/hooks/useTransactions.ts "use client"; import { useQuery, useQueryClient } from '@tanstack/react-query'; // ... other imports // Function to fetch transactions from our API async function fetchTransactions(wallet: string): Promise { if (!wallet) return []; const response = await fetch(`/api/escrow/transactions?wallet=${encodeURIComponent(wallet)}`); if (!response.ok) { const error = await response.json(); throw new Error(error.error || 'Failed to fetch transactions'); } return response.json(); } // Hook to get transactions by wallet export function useTransactionsByWallet(wallet?: string) { return useQuery({ queryKey: ['transactions', wallet], queryFn: () => fetchTransactions(wallet || ''), enabled: !!wallet, // Only run query if wallet is provided }); } [](https://dev.ada-anvil.io/guides/smart-contract/escrow/transaction-dashboard#create-the-mytransactions-component) Create the MyTransactions Component ------------------------------------------------------------------------------------------------------------------------------------------------------------ Let's create a component to display transaction history `src/components/MyTransactions.tsx`: Copy "use client"; import { useWallet } from '@ada-anvil/weld/react'; import { useTransactionsByWallet } from '@/hooks/useTransactions'; import { Transaction, TransactionStatus, TX_STATUS } from '@/lib/types'; const formatTxHash = (hash: string): string => { return `${hash.substring(0, 8)}...${hash.substring(hash.length - 4)}`; }; const formatDate = (timestamp: number): string => { const date = new Date(timestamp); return `${date.toLocaleDateString()} ${date.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}`; }; const formatAmount = (lovelaceAmount: number): string => { return (lovelaceAmount / 1_000_000).toFixed(2); }; const STATUS_STYLES: Record = { [TX_STATUS.PENDING]: 'bg-yellow-100 text-yellow-800', [TX_STATUS.SIGN_LOCK]: 'bg-yellow-100 text-yellow-800', [TX_STATUS.SIGN_UNLOCK]: 'bg-yellow-100 text-yellow-800', [TX_STATUS.CONFIRMED]: 'bg-green-100 text-green-800', [TX_STATUS.UNLOCKED]: 'bg-blue-100 text-blue-800', }; const STATUS_LABELS: Record = { [TX_STATUS.PENDING]: 'Pending', [TX_STATUS.SIGN_LOCK]: 'Sign to Lock', [TX_STATUS.SIGN_UNLOCK]: 'Sign to Unlock', [TX_STATUS.CONFIRMED]: 'Confirmed', [TX_STATUS.UNLOCKED]: 'Unlocked', }; const STATUS_DETAILS: Record = { [TX_STATUS.PENDING]: 'Waiting for Cardano blockchain confirmation...', [TX_STATUS.SIGN_LOCK]: 'Transaction needs to be signed to lock funds', [TX_STATUS.SIGN_UNLOCK]: 'Transaction needs to be signed to unlock funds', [TX_STATUS.CONFIRMED]: 'Transaction confirmed on the Cardano blockchain', [TX_STATUS.UNLOCKED]: 'Funds have been successfully unlocked', }; export default function MyTransactions() { const wallet = useWallet(); const address = wallet.changeAddressBech32; const { data: transactions = [], error, isLoading } = useTransactionsByWallet(address); return (

My Transactions

{/* Handle different states */} {!address && (

Please connect your wallet to view your transactions.

)} {address && error && (

Error loading transactions

{(error as Error).message}

)} {address && isLoading && (

Loading your transactions...

)} {/* Display transactions if no errors, not loading, and have an active Weld wallet address*/} {address && !error && !isLoading && ( <> {/* Display empty state if no transactions */} {transactions.length === 0 ? (

No transactions found. Lock some funds to get started.

) : ( // Display transactions table {transactions.map((transaction: Transaction) => { const isPending = transaction.status === TX_STATUS.PENDING; return ( ); })}
Transaction Amount Status Date Action
{formatTxHash(transaction.txHash)} {formatAmount(transaction.amount)} ADA {isPending ? ( {STATUS_LABELS[transaction.status]} ) : ( {STATUS_LABELS[transaction.status] || transaction.status} )} {formatDate(transaction.timestamp)} {/* Unlock button will go here.*/} -
)} )}
); } [](https://dev.ada-anvil.io/guides/smart-contract/escrow/transaction-dashboard#update-the-home-page) Update the Home Page ------------------------------------------------------------------------------------------------------------------------------ Finally, update the home page to include the MyTransactions component `src/app/page.tsx`: Copy // src/app/page.tsx import WalletConnector from '@/components/WalletConnector'; import LockFundsForm from '@/components/LockFundsForm'; import MyTransactions from '@/components/MyTransactions'; export default function Home() { return (

Cardano Escrow

); } [](https://dev.ada-anvil.io/guides/smart-contract/escrow/transaction-dashboard#understanding-the-transaction-dashboard) Understanding the Transaction Dashboard -------------------------------------------------------------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/transaction-dashboard#data-flow) Data Flow Here's how data flows through our application: 1. **Database Storage**: When a transaction is created, it's stored in the SQLite database 2. **API Endpoint**: The `/api/escrow/transactions` endpoint fetches transactions for a specific wallet 3. **React Query**: Manages data fetching, caching, and periodic refreshing 4. **MyTransactions Component**: Displays transaction data to the user [](https://dev.ada-anvil.io/guides/smart-contract/escrow/transaction-dashboard#testing-your-transaction-dashboard) Testing Your Transaction Dashboard ---------------------------------------------------------------------------------------------------------------------------------------------------------- To test the transaction dashboard: 1. Start your development server: Copy npm run dev 1. Navigate to http://localhost:3000 in your browser 2. Connect your wallet 3. Use the LockFundsForm to lock some funds 4. Observe the transaction appearing in the MyTransactions component. 5. Notice that the transaction sits in the `Pending` state. In the next part, we'll implement webhook notifications for immediate updates when enough confirmations are received. [](https://dev.ada-anvil.io/guides/smart-contract/escrow/transaction-dashboard#troubleshooting) Troubleshooting -------------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/transaction-dashboard#database-issues) Database Issues If you encounter database-related errors: * Ensure the `data` directory exists and has proper permissions * Check that the `SQLITE_DB_PATH` environment variable is set correctly * If using Windows, you might need to install build tools with `npm install --global --production windows-build-tools` ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/transaction-dashboard#transaction-data-not-showing) Transaction Data Not Showing If transactions don't appear in the dashboard: * Verify that your wallet is correctly connected * Check the browser console for API errors * Make sure you're using the same wallet address that was used to create the transactions Congratulations! You've completed Part 4 of the guide. Your application now has a fully functioning transaction dashboard to monitor locked funds. [PreviousFund Locking](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-locking) [NextReal-time Updates](https://dev.ada-anvil.io/guides/smart-contract/escrow/realtime-updates) Last updated 2 months ago Was this helpful? --- # Fee Structure | Anvil Development Agency The Tournament Builder, developed for Forge Digital Ventures, involves several types of fees: [](https://dev.ada-anvil.io/tournament-builder/fee-structure#protocol-fees) Protocol Fees ---------------------------------------------------------------------------------------------- Fixed fees charged for using the tournament builder: 1. **Creation Fee**: `3_000_000 lovelace (3 ADA)` - Charged at tournament creation * Sent to Anvil fee address during tournament creation 2. **Settlement Fee**: `2_000_000 lovelace (2 ADA)` - Charged during tournament settlement * Sent to Forge Digital Ventures fee address during tournament settlement [](https://dev.ada-anvil.io/tournament-builder/fee-structure#participant-fees) Participant Fees ---------------------------------------------------------------------------------------------------- Fees charged per participant: 1. **Per-Participant Protocol Fee**: `1_000_000 lovelace (1 ADA)` - Charged for each participant during settlement * Calculated as `entryFee * bracketSize` and sent to Forge Digital Ventures fee address during settlement 2. **Tournament Entry Fee**: Custom amount defined by tournament administrator * Optional, can be set to zero for free tournaments [](https://dev.ada-anvil.io/tournament-builder/fee-structure#fee-calculation-concepts) Fee Calculation Concepts -------------------------------------------------------------------------------------------------------------------- The tournament builder manages fees through several key mechanisms: ### [](https://dev.ada-anvil.io/tournament-builder/fee-structure#total-entry-fee) Total Entry Fee The total entry fee for each participant is the sum of: * Protocol per-participant fee (1 ADA) * Optional tournament-specific entry fee defined by the administrator ### [](https://dev.ada-anvil.io/tournament-builder/fee-structure#tournament-balance-requirements) Tournament Balance Requirements The tournament builder enforces a minimum locked amount in the tournament UTXO using the following formula: Copy lockedAmount = totalPayout + heldFees - unpaidEntryFees Where: * `totalPayout`: Sum of all payout structure amounts and fixed payouts * `heldFees`: Protocol fees that will be paid at settlement time * `unpaidEntryFees`: Entry fees that haven't been collected yet (for empty slots) This ensures the tournament has sufficient funds to cover all prizes and fees when it's settled, even if not all slots are filled yet. ### [](https://dev.ada-anvil.io/tournament-builder/fee-structure#example-fee-calculation) Example Fee Calculation Small Tournament [](https://dev.ada-anvil.io/tournament-builder/fee-structure#tab-small-tournament) Large Tournament [](https://dev.ada-anvil.io/tournament-builder/fee-structure#tab-large-tournament) Free Tournament [](https://dev.ada-anvil.io/tournament-builder/fee-structure#tab-free-tournament) For a tournament with: * 16 participants * 10 ADA entry fee The minimum locked value in the tournament UTXO would be: Copy Creation Fee + Settlement Fee + (Per-Participant Fee × Bracket Size) + (Entry Fee × Bracket Size) = 3 ADA + 2 ADA + (1 ADA × 16) + (10 ADA × 16) = 3 ADA + 2 ADA + 16 ADA + 160 ADA = 181 ADA (or 181,000,000 lovelace) For a tournament with: * 64 participants * 5 ADA entry fee The minimum locked value in the tournament UTXO would be: Copy Creation Fee + Settlement Fee + (Per-Participant Fee × Bracket Size) + (Entry Fee × Bracket Size) = 3 ADA + 2 ADA + (1 ADA × 64) + (5 ADA × 64) = 3 ADA + 2 ADA + 64 ADA + 320 ADA = 389 ADA (or 389,000,000 lovelace) For a tournament with: * 32 participants * 0 ADA entry fee (free tournament) The minimum locked value in the tournament UTXO would be: Copy Creation Fee + Settlement Fee + (Per-Participant Fee × Bracket Size) + (Entry Fee × Bracket Size) = 3 ADA + 2 ADA + (1 ADA × 32) + (0 ADA × 32) = 3 ADA + 2 ADA + 32 ADA + 0 ADA = 37 ADA (or 37,000,000 lovelace) ### [](https://dev.ada-anvil.io/tournament-builder/fee-structure#prize-distribution) Prize Distribution The total prize pool is calculated from: * Based on the payout structure defined in the tournament configuration * Any fixed payouts to specific addresses. Fixed payouts are not directly related to the tournament participants. When a tournament concludes, prizes are distributed according to this configuration, and protocol fees are automatically paid. These mechanisms ensure the sustainability of the protocol while allowing tournament creators to define their own economic structure. [](https://dev.ada-anvil.io/tournament-builder/fee-structure#fee-refund-process) Fee Refund Process -------------------------------------------------------------------------------------------------------- If a participant cancels their registration before being added to the tournament by the administrator: * The complete entry fee amount (including any tournament-specific fees) is returned to the participant * This is handled through the registration validator's 'Cancel' action * Cancellation is only possible during Phase 1 of the registration process, before the participant has been added to the tournament This refund mechanism provides financial protection for participants who need to withdraw from a tournament. [](https://dev.ada-anvil.io/tournament-builder/fee-structure#related-documentation) Related Documentation -------------------------------------------------------------------------------------------------------------- * For implementation details of registration and cancellation, see the [Registration Management API](https://dev.ada-anvil.io/tournament-builder/registration) * For a complete workflow guide, see the [Integration Flow](https://dev.ada-anvil.io/tournament-builder/integration-flow) [PreviousSubmit](https://dev.ada-anvil.io/tournament-builder/submit) Last updated 1 month ago Was this helpful? --- # Profile Offers | Anvil Development Agency [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-offers#introduction) Introduction -------------------------------------------------------------------------------------------------------- This guide demonstrates how to retrieve all active offers made by a specific wallet address on the Wayup Marketplace. This endpoint is useful for building user profiles, dashboards, or portfolio trackers that show what offers a user currently has placed on NFTs. [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-offers#requirements) Requirements -------------------------------------------------------------------------------------------------------- * A valid Cardano wallet address [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-offers#api-request-structure) API Request Structure -------------------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-offers#configuration) Configuration Copy // API endpoint for Wayup Marketplace const BASE_URL = "https://prod.api.ada-anvil.app/marketplace/api"; // Wallet address to query offers for const WALLET_ADDRESS = "addr1qx33ycd2ymg02dxh6vnnf8dsk54l8ranr9cfjk9qrlj3309c69jc4n8h3uvnczncaqu2sm03pl99h99n75uvtl3mhv0q3z8s8m"; ### [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-offers#type-definitions) Type Definitions Copy interface ProfileOffersRequest { address: string; limit?: number; cursor?: string; } interface AssetWithUnit { policyId: string; name: string; assetName: string; onChainAssetName: string; image: string; media?: string; unit: string; } interface Offer { policyId: string; assetName: string; offerAmount: string; txHash: string; outputIndex: number; ownerAddress: string; ownerStakeKeyhash: string; createdAt: string; asset?: AssetWithUnit | null; } interface ProfileOffersResponse { results: Offer[]; pageState: string | null; } [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-offers#implementation) Implementation ------------------------------------------------------------------------------------------------------------ ### [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-offers#step-1-prepare-the-request-parameters) Step 1: Prepare the Request Parameters First, prepare the parameters for your API request: #### [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-offers#available-parameters) Available Parameters Parameter Type Required Description `address` `string` Yes Wallet address to query offers for `limit` `number` No Max items per page. Default `10`. `cursor` `string` No Pagination cursor from a previous response. Use it only when requesting **additional pages**; omit it for the first page or if you only need one page. ### [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-offers#step-1-build-the-request-url) Step 1: Build the Request URL First, build the URL with the required parameters: Copy // Optional parameters const PAGE_LIMIT = 20; // Number of results to return const PAGE_CURSOR = null; // Pagination cursor (leave as null for first page) const requestUrl = `${BASE_URL}/get-profile-offers?address=${WALLET_ADDRESS}`; // If you want to include additional parameters: // const requestUrl = `${BASE_URL}/get-profile-offers?address=${WALLET_ADDRESS}&limit=${PAGE_LIMIT}${PAGE_CURSOR ? `&cursor=${PAGE_CURSOR}` : ''}`; ### [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-offers#step-2-execute-the-request) Step 2: Execute the Request Send the request to the API: Copy console.log("Fetching user's NFT offers..."); const response = await fetch(requestUrl); ### [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-offers#step-3-process-the-response) Step 3: Process the Response Parse the API response: Copy // Step 3: Process the response const result = await response.json(); const { results, pageState } = result; ### [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-offers#step-4-display-the-results) Step 4: Display the Results Process and display the offers data: Copy // Step 5: Display the results console.log(`Found ${results.length} offers made by address ${WALLET_ADDRESS}`); console.log("Offers:"); results.forEach((item, index) => { console.log(`\n--- Offer ${index + 1} ---`); console.log(`Policy ID: ${item.policyId}`); console.log(`Asset Name: ${item.assetName}`); console.log(`Offer Amount: ${Number(item.offerAmount) / 1000000} ADA`); console.log(`Transaction Hash: ${item.txHash}`); console.log(`Output Index: ${item.outputIndex}`); if (item.asset) { console.log("\nAsset Details:"); console.log(`Name: ${item.asset.name}`); console.log(`Media: ${item.asset.media || "No media available"}`); } else { console.log("\nNo asset details available"); } }); ### [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-offers#step-5-handle-pagination) Step 5: Handle Pagination Handle pagination for large result sets: Copy // Step 6: Handle pagination if (pageState) { console.log(`\nMore results available. Use this cursor for the next page: ${pageState}`); } ### [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-offers#running-the-example) Running the Example Copy deno run --allow-net get-profile-offers.ts ### [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-offers#example-output) Example Output Copy Fetching user's NFT offers... Found 1 offers made by address addr1qx33ycd2ymg02dxh6vnnf8dsk54l8ranr9cfjk9qrlj3309c69jc4n8h3uvnczncaqu2sm03pl99h99n75uvtl3mhv0q3z8s8m Offers: --- Offer 1 --- Policy ID: 6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01 Asset Name: 4646506f776572436f72657331333239 Offer Amount: 5 ADA Transaction Hash: 85d0e5f0f7fc0e958679b410e389d61dd2d7d362ce78003879ee9d2135d23e83 Output Index: 0 Asset Details: Name: Shimmering Power Core Media: https://img-proxy-caching.prod.api.ada-anvil.app/6fb0ce0d80bce539333b0b16f4a29a0d40c786249f86850d3a36fa01/assets/QmXoDGy7Si7kYuqWn7mkUEBitzbvYbWpEgX6dn8TDdapyt?size=card&signature=DIatXw7SnNbYmivTF8rlTQXdRL-_LO4HUMWWidDNhyU [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-offers#the-complete-file) The Complete File ------------------------------------------------------------------------------------------------------------------ get-profile-offers.ts[](https://dev.ada-anvil.io/wayup/profile-information/get-profile-offers#get-profile-offers.ts) Copy // API endpoint for Wayup Marketplace const BASE_URL = "https://prod.api.ada-anvil.app/marketplace/api"; // Wallet address to query offers for const WALLET_ADDRESS = "addr1qx33ycd2ymg02dxh6vnnf8dsk54l8ranr9cfjk9qrlj3309c69jc4n8h3uvnczncaqu2sm03pl99h99n75uvtl3mhv0q3z8s8m"; // Optional parameters const PAGE_LIMIT = 20; // Number of results to return const PAGE_CURSOR = null; // Pagination cursor (leave as null for first page) // Step 1: Build the request URL const requestUrl = `${BASE_URL}/get-profile-offers?address=${WALLET_ADDRESS}`; // Step 2: Execute the request console.log("Fetching user's NFT offers..."); const response = await fetch(requestUrl); // Step 3: Process the response const result = await response.json(); // Access the results directly as they're at the top level of the response const { results, pageState } = result; // Step 4: Display the results console.log(`Found ${results.length} offers made by address ${WALLET_ADDRESS}`); console.log("Offers:"); results.forEach((item, index) => { console.log(`\n--- Offer ${index + 1} ---`); console.log(`Policy ID: ${item.policyId}`); console.log(`Asset Name: ${item.assetName}`); // Offer price is returned in lovelace (1 ADA = 1,000,000 lovelace) console.log(`Offer Amount: ${Number(item.price) / 1_000_000} ADA`); // txHashIndex contains both the transaction hash and the output index separated by '#' const [txHash, outputIndex] = item.txHashIndex.split("#"); console.log(`Transaction Hash: ${txHash}`); console.log(`Output Index: ${outputIndex}`); if (item.asset) { console.log("\nAsset Details:"); console.log(`Name: ${item.asset.name}`); console.log(`Media: ${item.asset.media?.src || item.asset.image || "No media available"}`); } else { console.log("\nNo asset details available"); } }); // Step 5: Handle pagination if (pageState) { console.log(`\nMore results available. Use this cursor for the next page: ${pageState}`); } // Run this with: deno run --allow-net get-profile-offers.ts [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-offers#best-practices) Best Practices ------------------------------------------------------------------------------------------------------------ 1. **Input Validation**: Always validate the wallet address before making the API call. 2. **Error Handling**: Implement proper error handling for network issues or invalid responses. 3. **Pagination**: Always handle pagination for users with many offers. 4. **Asset Details**: Be prepared to handle cases where asset details might not be available. 5. **Offer Status**: Remember that these are only active offers; expired or accepted offers will not appear. [](https://dev.ada-anvil.io/wayup/profile-information/get-profile-offers#end-to-end-flow) End-to-End Flow -------------------------------------------------------------------------------------------------------------- 1. Obtain a wallet address from your user 2. Query the `/get-profile-offers` endpoint with the wallet address 3. Display the offer results (policy ID, asset name, offer amount, etc.) 4. Display asset details when available (name, media) 5. Implement pagination using the returned pageState if there are more results 6. Provide options to cancel offers or view the NFTs being offered for [PreviousProfile Listings](https://dev.ada-anvil.io/wayup/profile-information/get-profile-listings) [NextCollection Information](https://dev.ada-anvil.io/wayup/collection-information) Last updated 1 month ago Was this helpful? --- # Tournament | Anvil Development Agency [](https://dev.ada-anvil.io/tournament-builder/tournament#tournament-creation) Tournament Creation ------------------------------------------------------------------------------------------------------- **Endpoint**: `POST /tournaments` Creates a new tournament with the specified configuration parameters. **Request Parameters**: * `name` (string): Tournament display name * `adminAddress` (string): Address with administrative control * `changeAddress` (string, optional): Address to receive change UTXOs (defaults to adminAddress) * `bracketSize` (number): Number of participants in the tournament * `entryFee` (string|number|bigint, optional): Fee for participants (in lovelace) * `payoutStructure` (string\[\]|number\[\]|bigint\[\]): Array defining prize distribution (in lovelace) Even for percentage-based payouts, values for `payoutStructure` must be pre-calculated as absolute lovelace amounts. The contract does not perform percentage calculations; clients must convert desired percentages to exact lovelace values. Example: For a 70%/20%/10% split of 100 ADA, you would provide `["70_000_000", "20_000_000", "10_000_000"]` * `fixedPayouts` (\[string, string|number|bigint\]\[\], optional): Optional specific reward allocations to addresses * `utxos` (string\[\], min 1): Array of UTXO strings to use for the transaction * `contractTitle` (string, optional): Contract title for custom deployments * `contractVersion` (string, optional): Contract version for custom deployments **Response**: * `complete`: Hex-encoded transaction for signing The returned transaction must be signed and submitted through the `/tournaments/submit-tx` endpoint before it takes effect on the blockchain. **Technical Notes**: * The tournament ID is derived from the first UTXO in the transaction * An NFT is minted representing the tournament * The tournament datum contains both configuration and current state **Example (Node.js with fetch)**: Copy async function createTournament() { const apiUrl = 'https://preprod.api.ada-anvil.app/v2/services/tournaments'; const requestData = { name: "Tournament Name", adminAddress: "addr_test1...", bracketSize: 8, entryFee: "5_000_000", // 5 ADA in lovelace payoutStructure: ["30_000_000", "15_000_000", "5_000_000"], // First, second, third place prizes fixedPayouts: [\ ["addr_test1...", "1_000_000"]\ ], utxos: ["8282...", "8282...", "8282..."] // UTXOs to consume in the transaction }; const response = await fetch(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(requestData), }); const data = await response.json(); console.log('Tournament creation transaction:', data.complete); // The returned transaction must be signed and submitted (see Transaction Overview: ../guides/transaction/README.md) return data.complete; } [](https://dev.ada-anvil.io/tournament-builder/tournament#adding-participants-to-tournament) Adding Participants to Tournament ----------------------------------------------------------------------------------------------------------------------------------- **Endpoint**: `POST /tournaments/{tournamentId}/participants` Enables tournament administrators to collect valid registrations and officially add participants to the tournament. **Request Parameters**: * `adminAddress` (string): Tournament administrator address * `tournamentId` (string): Tournament identifier * `changeAddress` (string, optional): Address to receive change UTXOs (defaults to adminAddress) * `registrations` (object\[\], min 1): Array of registration objects, each containing: * `address` (string): Participant address * `outputRef` (string): Registration UTXO reference in format `txHash#index` * `utxos` (string\[\], min 1): Array of UTXO strings to use for the transaction * `contractTitle` (string, optional): Contract title for custom deployments * `contractVersion` (string, optional): Contract version for custom deployments **Response**: * `complete`: Hex-encoded transaction for signing The returned transaction must be signed and submitted through the `/tournaments/submit-tx` endpoint before it takes effect on the blockchain. **Technical Notes**: * Only the tournament admin can add registered participants to the tournament * Multiple participants can be added in a single transaction * The transaction updates the tournament datum with new participants * Registration UTXOs are consumed in the process **Example (Node.js with fetch)**: Copy async function addParticipantsToTournament(tournamentId) { const apiUrl = `https://preprod.api.ada-anvil.app/v2/services/tournaments/${tournamentId}/participants`; const requestData = { adminAddress: "addr_test1...", changeAddress: "addr_test1...", // Optional, defaults to adminAddress registrations: [\ {\ address: "addr_test12...",\ outputRef: "registrationOutputRef1"\ },\ {\ address: "addr_test13...",\ outputRef: "registrationOutputRef2"\ }\ ], utxos: ["8282...", "8282...", "8282..."] // UTXOs to consume in the transaction }; const response = await fetch(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(requestData), }); const data = await response.json(); console.log('Add participants transaction:', data.complete); // The returned transaction must be signed and submitted (see Transaction Overview: ../guides/transaction/README.md) return data.complete; } [](https://dev.ada-anvil.io/tournament-builder/tournament#retrieving-tournaments) Retrieving Tournaments ------------------------------------------------------------------------------------------------------------- **Endpoint**: `GET /tournaments` Returns a list of all available tournaments. **Request Parameters**: * `limit` (number, optional): Number of tournaments to retrieve (default: 100, max: 100) * `pageState` (string, optional): Pagination token for fetching additional results * `cursor` (string, optional): Alias for pageState, enables TRPC infinite queries * `contractTitle` (string, optional): Contract title for custom deployments * `contractVersion` (string, optional): Contract version for custom deployments **Response**: * `results`: Array of tournament objects with their configuration and state * `pageState`: Optional pagination token for fetching the next page of results **Technical Notes**: * Results are paginated with a default limit of 100 tournaments * The response includes both tournament configuration and current state * Tournament information is retrieved from the on-chain NFT records * Invalid registrations may be added to a tournament, but any missing fees will be covered by the tournament administrator when confirming the registration **Example (Node.js with fetch)**: Copy async function getTournaments(limit = 50, pageState = null) { let apiUrl = `https://preprod.api.ada-anvil.app/v2/services/tournaments?limit=${limit}`; if (pageState) { apiUrl += `&pageState=${encodeURIComponent(pageState)}`; } const response = await fetch(apiUrl, { method: 'GET', headers: { 'Content-Type': 'application/json', } }); const data = await response.json(); console.log(`Retrieved ${data.results.length} tournaments`); // For pagination, use the returned pageState token if (data.pageState) { console.log('More tournaments available, use pageState for pagination'); } return data; } [](https://dev.ada-anvil.io/tournament-builder/tournament#tournament-settlement) Tournament Settlement ----------------------------------------------------------------------------------------------------------- **Endpoint**: `PATCH /tournaments/{tournamentId}/settle` Completes a tournament by distributing prizes according to the specified results. **Request Parameters**: * `adminAddress` (string): Administrator address (must match tournament creator) * `changeAddress` (string, optional): Address to receive change UTXOs (defaults to adminAddress) * `tournamentId` (string): Tournament identifier * `results` (string\[\]): Array of participant addresses in order of their placement (1st place first, 2nd place second, etc.) This `results` list doesn't have to be exhaustive, but at least all winning participants (configured in the original `payoutStructure`) must be specified. * `utxos` (string\[\], min 1): Array of UTXO strings to use for the transaction * `contractTitle` (string, optional): Contract title for custom deployments * `contractVersion` (string, optional): Contract version for custom deployments **Response**: * `complete`: Hex-encoded transaction for signing The returned transaction must be signed and submitted through the `/tournaments/submit-tx` endpoint before it takes effect on the blockchain. **Technical Notes**: * Only the tournament admin can settle a tournament * Tournament must be fully filled (all participant slots must be occupied) * Prizes are distributed according to the tournament's payout structure * The tournament NFT is burned during settlement * Prize UTXOs include tournament ID as a unique tag in the datum to implement the [unique outputs pattern](https://aiken-lang.org/fundamentals/common-design-patterns#unique-outputs) , preventing double-satisfaction vulnerabilities **Example (Node.js with fetch)**: Copy async function settleTournament(tournamentId, results) { const apiUrl = `https://preprod.api.ada-anvil.app/v2/services/tournaments/${tournamentId}/settle`; const requestData = { adminAddress: "addr_test1...", tournamentId: tournamentId, changeAddress: "addr_test1...", // Optional, defaults to adminAddress results: [\ "addr_test1winner...", // 1st place (gets highest prize)\ "addr_test1second...", // 2nd place\ "addr_test1third..." // 3rd place (and so on)\ ], utxos: ["8282...", "8282...", "8282..."] // UTXOs to consume in the transaction }; const response = await fetch(apiUrl, { method: 'PATCH', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(requestData), }); const data = await response.json(); console.log('Settlement transaction:', data.complete); // The returned transaction must be signed and submitted (see Transaction Overview: ../guides/transaction/README.md) return data.complete; } [PreviousIntegration Flow](https://dev.ada-anvil.io/tournament-builder/integration-flow) [NextRegistration](https://dev.ada-anvil.io/tournament-builder/registration) Last updated 1 month ago Was this helpful? --- # Hello World Smart Contract | Anvil Development Agency 2025-02-21: Missing steps for lock and unlock [](https://dev.ada-anvil.io/guides/smart-contract/hello-world-smart-contract#requirements) Requirements ------------------------------------------------------------------------------------------------------------ * A Cup of Coffee * The Aiken Hello World Example (You need to update the smart contract name to avoid getting the `already exists` message from Anvil API.) * The Smart Contract name should be in this format: `your_org/hello-world` * See the official documentation: [Aiken Hello World](https://aiken-lang.org/example--hello-world/basics) * An Anvil API Key (you can use the one provided in this document to access `preprod`) * `customer.json` with 100ADA, you can use the CLI tool to generate the wallet, then send funds from the faucet to it. ([Wallet CLI](https://dev.ada-anvil.io/developer-tools/wallet-cli) ) * * * [](https://dev.ada-anvil.io/guides/smart-contract/hello-world-smart-contract#objectives) Objectives -------------------------------------------------------------------------------------------------------- Interact with any Smart Contracts using Anvil API **Objectives** * Upload Blueprint * Deploy Smart Contract On-Chain * Update/Delete Blueprint * Hello World Lock & Unlock ADA * Get a `jsonSchema` from your blueprint to get types for the language you use. * See below for the **code** in one file (`full.ts`) * * * [](https://dev.ada-anvil.io/guides/smart-contract/hello-world-smart-contract#aiken-smart-contract) Aiken Smart Contract ---------------------------------------------------------------------------------------------------------------------------- Smart Contract - Update Name[](https://dev.ada-anvil.io/guides/smart-contract/hello-world-smart-contract#smart-contract-update-name) **aiken.toml** Copy name = "your-org/your-sc-name" version = "0.0.0" compiler = "v1.1.10" plutus = "v3" license = "Apache-2.0" description = "Aiken contracts for project 'your-org/your-sc-name'" [repository] user = "your-org" project = "your-sc-name" platform = "github" [[dependencies]] name = "aiken-lang/stdlib" version = "v2.2.0" source = "github" [config] [](https://dev.ada-anvil.io/guides/smart-contract/hello-world-smart-contract#helper-functions) Helper Functions -------------------------------------------------------------------------------------------------------------------- Function to extract `validator hashes` from a `blueprint` Copy function getValidators(validators: typeof blueprint.validators) { return [...validators.reduce((a, b) => a.set(b.hash, b) && a, new Map()).values()]; } * * * [](https://dev.ada-anvil.io/guides/smart-contract/hello-world-smart-contract#prerequisites) Prerequisites -------------------------------------------------------------------------------------------------------------- You need to load your **Blueprint** like this: Copy import blueprint from "./hello-world/plutus.json" with {type: "json"}; The **wallet used** in this guide is loaded like this: > You also need the CSL imports and Buffer for that to work, it is only the case when you are using the wallet in the backend. Copy import { Buffer } from "node:buffer"; import { FixedTransaction, PrivateKey } from "npm:@emurgo/[email protected]"; // only required due to signing in the backend. import customer from "./customer.json" with {type: "json"}; // Optional, this guide is using this approach. > You can use any wallet, like the one in your browser or with weld, but to limit the external steps and streamline the flow. This guide uses the wallet directly in the backend. You can take a look at the [signing transaction document](https://dev.ada-anvil.io/guides/transaction/signing-transaction) > to see how to use the browser wallet. The **api key** used is this one: Copy const X_API_KEY = "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9"; * * * [](https://dev.ada-anvil.io/guides/smart-contract/hello-world-smart-contract#payload) Payload -------------------------------------------------------------------------------------------------- **To upload your validator on Anvil Backend** > Benefit of doing so, will reduce the complexity and steps to interact with your smart contract. as it will be store directly in the backend. Your API Key is link with your Blueprint and will let you update and delete it if needed. **Deploy the Smart Contract On-Chain** Copy const payload = { changeAddress: "addr_test...", message: "Smart contract deployed using anvil API", outputs: getValidators(blueprint.validators).map(validator => ({ address: scriptAddresses[validator.hash], datum: { type: "script", hash: validator.hash } })) } * `scriptAddresses`: * **Key** = Validator Hash * **Value** = Address of the Smart Contract (_Enterprise Address_) * * * [](https://dev.ada-anvil.io/guides/smart-contract/hello-world-smart-contract#steps) Steps ---------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/smart-contract/hello-world-smart-contract#step-1-upload-blueprint) Step 1 - Upload Blueprint Upload your blueprint (`plutus.json`) to Anvil Backend #### [](https://dev.ada-anvil.io/guides/smart-contract/hello-world-smart-contract#create-blueprint) Create Blueprint Copy const blueprintRegistration = await fetch("https://preprod.api.ada-anvil.app/v2/services/blueprints", { method: "POST", headers: { "Content-Type": "application/json", "x-api-key": X_API_KEY }, body: JSON.stringify({ blueprint }) }); const uploadedBlueprint = await blueprintRegistration.json(); console.debug(JSON.stringify(uploadedBlueprint, null, 2)) const { scriptAddresses } = uploadedBlueprint; * `scriptAddresses`: Needed for Step #2 > This API Call is an `upsert`, so if you forget the `scriptAddresses` or need to update your blueprint, you can reuse the same call. > **Tip**: Once registered, if you need to update the name without changing the smart contract code or logic, you can safely delete the blueprint using the former name and then re-upload it. ### [](https://dev.ada-anvil.io/guides/smart-contract/hello-world-smart-contract#step-2-deploy-smart-contract) Step 2 - Deploy Smart Contract > You need a valid Cardano wallet with some ADA on it (100ADA should be more than enough) [Wallet CLI](https://dev.ada-anvil.io/developer-tools/wallet-cli) Deploy the smart contract on-chain in order to get the reference UTXO #### [](https://dev.ada-anvil.io/guides/smart-contract/hello-world-smart-contract#payload-to-build-the-transaction) Payload to build the transaction: Copy const contract = { changeAddress: customer.base_address_preprod, message: "Smart contract deployed using anvil API", outputs: getValidators(blueprint.validators).map(validator => ({ address: scriptAddresses[validator.hash], datum: { type: "script", hash: validator.hash, } })), }; #### [](https://dev.ada-anvil.io/guides/smart-contract/hello-world-smart-contract#create-the-transaction) Create the transaction Copy const contractDeployed = await fetch( `https://preprod.api.ada-anvil.app/v2/services/transactions/build`, { method: "POST", headers: { "Content-Type": "application/json", "x-api-key": X_API_KEY }, body: JSON.stringify(contract), } ); const contractToDeployTransaction = await contractDeployed.json(); console.log(JSON.stringify(contractToDeployTransaction)); #### [](https://dev.ada-anvil.io/guides/smart-contract/hello-world-smart-contract#sign-and-submit-the-transaction) Sign and Submit the transaction > The signature process uses CSL for simplicity. For more information, see the [Signing a Transaction](https://dev.ada-anvil.io/guides/transaction/signing-transaction) > guide. Copy // Sign the transaction using CSL. const txToSubmitOnChain = FixedTransaction.from_bytes( Buffer.from(contractToDeployTransaction.complete, "hex") ); txToSubmitOnChain.sign_and_add_vkey_signature( PrivateKey.from_bech32(customer.skey) ); const urlSubmit = "https://preprod.api.ada-anvil.app/v2/services/transactions/submit"; const submitted = await fetch(urlSubmit, { method: "POST", body: JSON.stringify({ signatures: [], // no signature required as it is part of the `txToSubmitOnChain`. transaction: txToSubmitOnChain.to_hex(), }), headers: { "Content-Type": "application/json", "X-Api-Key": X_API_KEY, }, }); const response = await submitted.json(); console.debug(response); const { txHash } = response; ### [](https://dev.ada-anvil.io/guides/smart-contract/hello-world-smart-contract#step-3-link-blueprint-and-utxo) Step 3 - Link Blueprint and UTXO Update blueprint to link references to deployed validators. Copy const response = await fetch(`https://preprod.api.ada-anvil.app/v2/services/blueprints`, { method: "POST", headers: { "Content-Type": "application/json", "x-api-key": X_API_KEY }, body: JSON.stringify({ blueprint, refs: getValidators(blueprint.validators).reduce((a, b, index) => { a[b.hash] = { txHash, index }; return a; }, {} as Record), }) }); const updatedBlueprint = await response.json() console.log(updatedBlueprint); ### [](https://dev.ada-anvil.io/guides/smart-contract/hello-world-smart-contract#conclusion) Conclusion From here you have uploaded your blueprint and linked the reference UTXO on-chain and in Anvil Backend. The next step is to interact with the `hello-world` Smart Contract (Lock & Unlock ADA) * * * [](https://dev.ada-anvil.io/guides/smart-contract/hello-world-smart-contract#full-example) Full Example ------------------------------------------------------------------------------------------------------------ full.ts[](https://dev.ada-anvil.io/guides/smart-contract/hello-world-smart-contract#full.ts) Copy import { Buffer } from "node:buffer"; import { FixedTransaction, PrivateKey } from "npm:@emurgo/[email protected]"; // only required due to signing in the backend. import blueprint from "./plutus.json" with {type: "json"}; import customer from "./customer.json" with {type: "json"}; const X_API_KEY = "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9"; const API_ENDPOINT = "https://preprod.api.ada-anvil.app/v2/services" const headers = { "x-api-key": X_API_KEY, "Content-Type": "application/json", }; function getValidators(validators: typeof blueprint.validators) { return [...validators.reduce((a, b) => a.set(b.hash, b) && a, new Map()).values()]; } const blueprintRegistration = await fetch(`${API_ENDPOINT}/blueprints`, { method: "POST", headers, body: JSON.stringify({ blueprint }) }); const uploadedBlueprint = await blueprintRegistration.json(); console.debug("uploadedBlueprint", JSON.stringify(uploadedBlueprint, null, 2)) const { scriptAddresses } = uploadedBlueprint; const contract = { changeAddress: customer.base_address_preprod, message: "Smart contract deployed using anvil API", outputs: getValidators(blueprint.validators).map(validator => ({ address: scriptAddresses[validator.hash], datum: { type: "script", hash: validator.hash, } })), }; const contractDeployed = await fetch( `${API_ENDPOINT}/transactions/build`, { method: "POST", headers, body: JSON.stringify(contract), } ); const contractToDeployTransaction = await contractDeployed.json(); console.log("contractToDeployTransaction", JSON.stringify(contractToDeployTransaction)); // Sign the transaction using CSL. const txToSubmitOnChain = FixedTransaction.from_bytes( Buffer.from(contractToDeployTransaction.complete, "hex") ); txToSubmitOnChain.sign_and_add_vkey_signature( PrivateKey.from_bech32(customer.skey) ); console.log(txToSubmitOnChain.transaction_hash().to_hex()); const urlSubmit = `${API_ENDPOINT}/transactions/submit`; const submitted = await fetch(urlSubmit, { method: "POST", body: JSON.stringify({ signatures: [], // no signature required as it is part of the `txToSubmitOnChain`. transaction: txToSubmitOnChain.to_hex(), }), headers, }); const response = await submitted.json(); console.debug("response", response); const { txHash } = response; const linkBlueprintAndTxHash = await fetch(`${API_ENDPOINT}/blueprints`, { method: "POST", headers, body: JSON.stringify({ blueprint, refs: getValidators(blueprint.validators).reduce((a, b, index) => { a[b.hash] = { txHash, index }; return a; }, {} as Record), }) }); const updatedBlueprint = await linkBlueprintAndTxHash.json() console.log("updatedBlueprint", updatedBlueprint); * * * [](https://dev.ada-anvil.io/guides/smart-contract/hello-world-smart-contract#usage-hello-world-lock-unlock) Usage (Hello World Lock/Unlock) ------------------------------------------------------------------------------------------------------------------------------------------------ ### [](https://dev.ada-anvil.io/guides/smart-contract/hello-world-smart-contract#lock-funds) Lock Funds #### [](https://dev.ada-anvil.io/guides/smart-contract/hello-world-smart-contract#payload-1) Payload TODO: Explain the JSON below Copy const input = { changeAddress: customer.base_address_preprod, message: "Locking my fortune using anvil API", outputs: [\ {\ address: await getScriptAddr(hash), // script address of the first validator\ lovelace: 2_000_000, // 1 ADA = 1_000_000 Lovelace\ datum: {\ type: "inline",\ value: {\ owner: customer.key_hash // Only the Customer will be able to unlock the funds\ },\ shape: {\ validatorHash: hash,\ purpose: "spend"\ }\ }\ }\ ], }; ### [](https://dev.ada-anvil.io/guides/smart-contract/hello-world-smart-contract#unlock-funds) Unlock Funds #### [](https://dev.ada-anvil.io/guides/smart-contract/hello-world-smart-contract#payload-2) Payload TODO: Explain the JSON below Copy const input = { changeAddress: customer.base_address_preprod, message: "Unlock my fortune using anvil API", scriptInteractions: [\ {\ hash,\ purpose: "spend",\ // This Output ref and index is the UTXO locked at the previous step.\ outputRef: {\ txHash: "7ac8f6922d51ffe2980e73c57c985b3906795440b5a565cc8806899dc88b110e", // ACTION: Replace with your txHash\ index: 0, // ACTION: Replace with you index\ },\ redeemer: { // Aka. Smart contract Parameters\ type: "json",\ value: {\ msg: Buffer.from("Bonjour Monde!!", "utf8").toString("hex") // ACTION: Replace with your String\ }\ }\ }\ ], requiredSigners: [customer.key_hash], // Aka. extra_signatories }; ### [](https://dev.ada-anvil.io/guides/smart-contract/hello-world-smart-contract#full-example-lock-fund) Full Example - Lock Fund lock.ts[](https://dev.ada-anvil.io/guides/smart-contract/hello-world-smart-contract#lock.ts) Copy import { Buffer } from "node:buffer"; import { FixedTransaction, PrivateKey } from "npm:@emurgo/[email protected]"; import blueprint from "./hello-world/plutus.json" with {type: "json"}; // NOTE: You only need the hash. import customer from "./customer.json" with {type: "json"}; const X_API_KEY = "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9"; // Save the hash in your database or backend. // You are gonna need this hash everytime you need to interact with the smart contract. // In this example we are loading it from the blueprint, but you do not need the blueprint. const hash = blueprint.validators[0].hash; async function getScriptAddr(hash: string): Promise { const response = await fetch(`https://preprod.api.ada-anvil.app/v2/services/validators/${hash}/address`, { method: "GET", headers: { "x-api-key": X_API_KEY }, }); const scriptAddress = await response.json(); console.debug("scriptAddress", scriptAddress) return scriptAddress.hex as string; } // Send 2 ADA to the Smart Contract (Lock ADA) const input = { changeAddress: customer.base_address_preprod, message: "Locking my fortune using anvil API", outputs: [\ {\ address: await getScriptAddr(hash), // script address of the first validator\ lovelace: 2_000_000, // 1 ADA = 1_000_000 Lovelace\ datum: {\ type: "inline",\ value: {\ owner: customer.key_hash // Only the Customer will be able to unlock the funds\ },\ shape: {\ validatorHash: hash,\ purpose: "spend"\ }\ }\ }\ ], }; const response = await fetch( `https://preprod.api.ada-anvil.app/v2/services/transactions/build`, { method: "POST", headers: { "Content-Type": "application/json", "x-api-key": X_API_KEY }, body: JSON.stringify(input), } ); const result = await response.json() console.log("result", result); const txToSign = result.complete; // Sign transaction directly in the backend const txToSubmitOnChain = FixedTransaction.from_bytes( Buffer.from(txToSign, "hex") ); // This sign the tx and add vkeys to the txToSubmitOnChain, so in submit we don't need to provide signatures txToSubmitOnChain.sign_and_add_vkey_signature( PrivateKey.from_bech32(customer.skey) ); const urlSubmit = "https://preprod.api.ada-anvil.app/v2/services/transactions/submit"; const submitted = await fetch(urlSubmit, { method: "POST", body: JSON.stringify({ signatures: [], transaction: txToSubmitOnChain.to_hex(), }), headers: { "Content-Type": "application/json", "X-Api-Key": X_API_KEY, }, }); const submittedResponse = await submitted.json(); console.debug("submittedResponse", submittedResponse); ### [](https://dev.ada-anvil.io/guides/smart-contract/hello-world-smart-contract#full-example-unlock-fund) Full Example - Unlock Fund unlock.ts[](https://dev.ada-anvil.io/guides/smart-contract/hello-world-smart-contract#unlock.ts) Copy import { Buffer } from "node:buffer"; import { FixedTransaction, PrivateKey } from "npm:@emurgo/[email protected]"; // update utilities doc. import blueprint from "./hello-world/plutus.json" with {type: "json"}; // NOTE: You only need the hash. import customer from "./customer.json" with {type: "json"}; const X_API_KEY = "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9"; // Save the hash in your database or backend. // You are gonna need this hash everytime you need to interact with the smart contract. // In this example we are loading it from the blueprint, but you do not need the blueprint. const hash = blueprint.validators[0].hash; const input = { changeAddress: customer.base_address_preprod, message: "Unlock my fortune using anvil API", scriptInteractions: [\ {\ hash,\ purpose: "spend",\ // This Output ref and index is the UTXO locked at the previous step.\ outputRef: {\ txHash: "7ac8f6922d51ffe2980e73c57c985b3906795440b5a565cc8806899dc88b110e", // ACTION: Replace with your txHash\ index: 0, // ACTION: Replace with you index\ },\ redeemer: { // Aka. Smart contract Parameters\ type: "json",\ value: {\ msg: Buffer.from("Bonjour Monde!!", "utf8").toString("hex") // ACTION: Replace with your String\ }\ }\ }\ ], requiredSigners: [customer.key_hash], // Aka. extra_signatories }; const response = await fetch( `https://preprod.api.ada-anvil.app/v2/services/transactions/build`, { method: "POST", headers: { "Content-Type": "application/json", "x-api-key": X_API_KEY }, body: JSON.stringify(input), } ); const result = await response.json() console.log("result", result); const txToSign = result.complete; // Sign transaction directly in the backend const txToSubmitOnChain = FixedTransaction.from_bytes( Buffer.from(txToSign, "hex") ); // This sign the tx and add vkeys to the txToSubmitOnChain, so in submit we don't need to provide signautres txToSubmitOnChain.sign_and_add_vkey_signature( PrivateKey.from_bech32(customer.skey) ); const urlSubmit = "https://preprod.api.ada-anvil.app/v2/services/transactions/submit"; const submitted = await fetch(urlSubmit, { method: "POST", body: JSON.stringify({ signatures: [], // no signature required as it is part of the `txToSubmitOnChain`. transaction: txToSubmitOnChain.to_hex(), }), headers: { "Content-Type": "application/json", "X-Api-Key": X_API_KEY, }, }) const submittedResponse = await submitted.json(); console.debug("submittedResponse", submittedResponse); * * * [PreviousSpend Validator](https://dev.ada-anvil.io/guides/smart-contract/validators/spend-validator) [NextEnd-to-End Next.js Escrow Guide](https://dev.ada-anvil.io/guides/smart-contract/escrow) Last updated 11 days ago Was this helpful? --- # Fund Unlocking | Anvil Development Agency [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-unlocking#introduction) Introduction ------------------------------------------------------------------------------------------------------- In this final part, we'll implement the fund unlocking functionality to complete the escrow lifecycle. This will allow users to retrieve their funds from the escrow contract once they're ready. The unlocking process requires signature verification to ensure that only the rightful owner can access the funds. [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-unlocking#understanding-the-fund-unlocking-process) Understanding the Fund Unlocking Process --------------------------------------------------------------------------------------------------------------------------------------------------------------- The fund unlocking process involves these steps: 1. **Transaction Verification**: The system checks that the transaction exists and is in the CONFIRMED(i.e. Locked) state 2. **Ownership Validation**: The request must come from the same wallet that originally locked the funds 3. **Transaction Building**: The application builds an unlock transaction that spends the locked UTXO 4. **Transaction Signing**: The user signs the transaction with their wallet 5. **Transaction Submission**: The signed transaction is submitted to the blockchain 6. **Status Update**: The transaction status is updated to UNLOCKED in the database [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-unlocking#understanding-the-smart-contract-validation) Understanding the Smart Contract Validation --------------------------------------------------------------------------------------------------------------------------------------------------------------------- Our escrow application uses the Hello Aiken smart contract with two unlock conditions: 1. **Redeemer Message Validation**: The redeemer must contain the exact message "Hello, World!" (hex-encoded) 2. **Owner Signature Verification**: The transaction must be signed by the owner specified in the datum The code implements these requirements through: Copy redeemer: { type: "json", value: { // The Hello, World! message must match what's expected in the smart contract // The Hello, World message is hardcoded in the smart contract msg: Buffer.from("Hello, World!", "utf8").toString("hex"), }, }, // Ensure the transaction includes the owner's signature requiredSigners: [signerKeyHash], This dual validation ensures only the rightful owner can unlock their funds, while demonstrating the flexibility of Cardano's eUTXO model for custom validation logic. [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-unlocking#implementation-steps) Implementation Steps ----------------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-unlocking#id-1.-add-unlock-functions-to-the-anvil-api-module) 1\. Add Unlock Functions to the Anvil API Module First, let's add the necessary functions to the Anvil API module for unlocking funds. Add `unlockFunds` to your existing `src/lib/anvil-api.ts` file: Copy // Interface for unlock funds parameters export interface UnlockFundsParams { txHash: string; changeAddress: string; ownerKeyHash: string; // Key hash for requiredSigners unlockReason?: string; } // Interface for the unlock funds response export interface UnlockFundsResponse { complete?: string; error?: string; } /** * Unlock funds from the escrow smart contract * This creates a transaction that spends the UTXO with the specified redeemer */ export async function unlockFunds( params: UnlockFundsParams ): Promise { try { // Get the validator hash from environment const validatorHash = process.env.ESCROW_VALIDATOR_HASH; if (!validatorHash) { throw new Error('Escrow validator hash not found'); } // Derive owner payment key hash for requiredSigners const signerKeyHash = await getAddressKeyHash(params.changeAddress); const input = { changeAddress: params.changeAddress, message: params.unlockReason || 'Unlocking funds using Anvil API', scriptInteractions: [\ {\ hash: validatorHash,\ purpose: 'spend',\ outputRef: {\ txHash: params.txHash,\ // Output index of the UTXO to spend. Anvil stores the outputs in the same order (starting at 0) as we originally locked them.\ // Our demo application locks them one at a time, so the index will always be 0.\ index: 0, \ },\ redeemer: {\ type: "json",\ value: {\ msg: Buffer.from("Hello, World!", "utf8").toString("hex"),\ },\ },\ },\ ], requiredSigners: [signerKeyHash], }; // Build the transaction using our generic fetch utility const result = await fetchApi<{ complete: string }>( `/transactions/build`, { method: 'POST', headers: getHeaders(), body: JSON.stringify(input), }, 'build unlock transaction' ); return { complete: result.complete }; } catch (error: unknown) { return { error: handleApiError('unlock funds', error) }; } } ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-unlocking#id-2.-create-the-unlock-api-endpoint) 2\. Create the Unlock API Endpoint Create an API endpoint for unlocking funds `src/app/api/escrow/unlock/route.ts`: Copy import { NextRequest, NextResponse } from 'next/server'; import { unlockFunds } from '@/lib/anvil-api'; import { upsertWallet, upsertTx } from '@/lib/db'; import { TX_STATUS } from '@/lib/types'; export async function POST(request: NextRequest) { const body = await request.json(); const { txHash, changeAddress, ownerKeyHash, amount } = body; if (!txHash || !changeAddress || !ownerKeyHash || amount == null) { return NextResponse.json( { error: 'Missing txHash, changeAddress, ownerKeyHash, or amount' }, { status: 400 } ); } try { const { complete, error } = await unlockFunds({ txHash, changeAddress, ownerKeyHash }); if (error || !complete) { return NextResponse.json( { error: error || 'Failed to build unlock transaction' }, { status: 500 } ); } upsertWallet(changeAddress); upsertTx(txHash, changeAddress, Number(amount), TX_STATUS.PENDING); // Return built transaction for client-side signing and submission return NextResponse.json({ complete }); } catch (err: unknown) { console.error('Error unlocking funds:', err); const message = err instanceof Error ? err.message : String(err); return NextResponse.json( { error: message }, { status: 500 } ); } } ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-unlocking#id-3.-update-the-submit-transaction-endpoint) 3\. Update the Submit Transaction Endpoint Update the submit endpoint to handle unlock transactions. This is the updated `src/app/api/escrow/submit/route.ts` file: Copy import { NextRequest, NextResponse } from 'next/server'; import { submitTransaction } from '@/lib/anvil-api'; import { updateTxStatus } from '@/lib/db'; import { TX_STATUS } from '@/lib/types'; export async function POST(request: NextRequest) { try { const body = await request.json(); const { complete, signature, type, originalTxHash } = body; // Validate inputs if (!complete || !signature || !(type === TX_STATUS.SIGN_LOCK || type === TX_STATUS.SIGN_UNLOCK)) { return NextResponse.json( { error: 'Missing complete transaction or signature' }, { status: 400 } ); } // For unlock transactions, originalTxHash is required if (type === TX_STATUS.SIGN_UNLOCK && !originalTxHash) { return NextResponse.json( { error: 'Missing originalTxHash for unlock transaction' }, { status: 400 } ); } // Submit the signed transaction to the blockchain const result = await submitTransaction(signature, complete); // Mark transaction as pending in DB // For lock transactions, update the new txHash // For unlock transactions, update the original txHash const txHashToUpdate = type === TX_STATUS.SIGN_UNLOCK ? originalTxHash : result.txHash; updateTxStatus(txHashToUpdate, type === TX_STATUS.SIGN_LOCK ? TX_STATUS.PENDING : TX_STATUS.UNLOCKED); return NextResponse.json({ txHash: result.txHash }); } catch (error: unknown) { console.error('Error submitting transaction:', error); const message = error instanceof Error ? error.message : String(error); return NextResponse.json( { error: message || 'Failed to submit transaction' }, { status: 500 } ); } } ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-unlocking#id-4.-update-the-transaction-operations-hook) 4\. Update the Transaction Operations Hook Update the transaction operations hook to include unlock functionality. This is the updated `src/hooks/useTransactions.ts` file: useTransactions.ts[](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-unlocking#usetransactions.ts) Copy "use client"; import { useQuery, useQueryClient } from '@tanstack/react-query'; import { Transaction, TransactionStatus, TX_STATUS } from '@/lib/types'; import { useCallback, useState, useEffect } from 'react'; /** * Hook to manage transaction data with smart polling that automatically * activates only when pending transactions are detected * @param wallet - Wallet address to fetch transactions for * @returns Transaction data with loading and error states */ export function usePollingTransactions(wallet?: string) { const [hasPendingTx, setHasPendingTx] = useState(false); const query = useQuery({ queryKey: ['transactions', wallet], queryFn: async () => { if (!wallet) throw new Error("Wallet is required"); const response = await fetch(`/api/escrow/transactions?wallet=${wallet}`); return response.json(); }, enabled: !!wallet, refetchInterval: wallet && hasPendingTx ? 5000 : false, }); useEffect(() => { if (query.data) { const isPending = query.data.some(tx => tx.status === TX_STATUS.PENDING); if (isPending !== hasPendingTx) { setHasPendingTx(isPending); } } }, [query.data, hasPendingTx]); return query; } /** * Hook to update transaction status in the cache * @param wallet - Wallet address to update transactions for * @returns Function to update transaction status */ export function useTransactionUpdater(wallet?: string) { const queryClient = useQueryClient(); return useCallback( (txHash: string, newStatus: TransactionStatus, newTransaction?: Partial) => { if (!wallet) return; // Update cache optimistically queryClient.setQueryData( ['transactions', wallet], old => { if (!old) return []; // Update existing transaction or add new one const existingTx = old.find(tx => tx.txHash === txHash); if (existingTx) { return old.map(tx => tx.txHash === txHash ? { ...tx, status: newStatus } : tx ); } // Create and add new transaction if provided if (newTransaction) { const newTx: Transaction = { txHash, wallet: wallet, amount: newTransaction.amount || 0, status: newStatus, timestamp: newTransaction.timestamp || Date.now(), }; return [newTx, ...old]; } return old; } ); // Refresh data from server queryClient.invalidateQueries({ queryKey: ['transactions', wallet] }); }, [queryClient, wallet] ); } type CardanoWallet = { changeAddressHex?: string; handler?: { signTx: (txComplete: string, witnessOnly: boolean) => Promise; }; }; /** * API response types */ interface TransactionResponse { txHash: string; error?: string; } interface BuildTransactionResponse { complete?: string; error?: string; } const apiPost = async (endpoint: string, payload: Record): Promise => { const response = await fetch(`/api/escrow/${endpoint}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }); const data = await response.json(); if (data.error) { throw new Error(data.error); } return data as T; }; /** * Hook to manage transaction operations (locking and unlocking funds) * @param wallet - The wallet instance from useWallet() from Weld * @param address - The wallet address to use for transactions * @returns Functions: * - lockFunds (/escrow/lock + Weld signTx + /escrow/submit): Lock funds in escrow * - unlockFunds (/escrow/unlock + Weld signTx + /escrow/submit): Unlock funds from escrow */ export function useTransactionOperations(wallet: CardanoWallet, address?: string) { const [processing, setProcessing] = useState(null); const [error, setError] = useState(null); const updateTransaction = useTransactionUpdater(address); const buildUnlockTransaction = async (txHash: string, amount: number) => { if (!address || !wallet?.changeAddressHex) return null; const data = await apiPost('unlock', { txHash, changeAddress: address, ownerKeyHash: wallet.changeAddressHex, amount }); if (!data.complete) { throw new Error('Build failed'); } return data.complete; }; const buildLockTransaction = async (lovelaceAmount: number) => { if (!address || !wallet?.changeAddressHex) return null; const LOVELACE_PER_ADA = 1_000_000; const data = await apiPost('lock', { changeAddress: address, amount: lovelaceAmount, ownerKeyHash: wallet.changeAddressHex, message: `Locking ${lovelaceAmount / LOVELACE_PER_ADA} ADA in escrow` }); if (!data.complete) { throw new Error('Build failed'); } return data.complete; }; const signTransaction = async (txComplete: string): Promise => { const signed = await wallet?.handler?.signTx(txComplete, true); if (!signed) { throw new Error('Signing failed'); } return signed; }; const submitUnlockTransaction = async (signed: string, txComplete: string, originalTxHash: string): Promise => { return apiPost('submit', { signature: signed, complete: txComplete, type: TX_STATUS.SIGN_UNLOCK, originalTxHash }); }; const submitLockTransaction = async (signedTx: string, txComplete: string, lovelaceAmount: number): Promise => { return apiPost('submit', { signature: signedTx, complete: txComplete, changeAddress: address, amount: lovelaceAmount, type: TX_STATUS.SIGN_LOCK }); }; const lockFunds = async (adaAmount: number) => { if (!address) return null; const LOVELACE_PER_ADA = 1_000_000; setError(null); setProcessing('lock'); try { // Convert ADA to Lovelace (smallest unit) const lovelaceAmount = adaAmount * LOVELACE_PER_ADA; // Build → Sign → Submit pattern const txComplete = await buildLockTransaction(lovelaceAmount); if (!txComplete) throw new Error('Failed to build transaction'); const signedTx = await signTransaction(txComplete); const result = await submitLockTransaction(signedTx, txComplete, lovelaceAmount); // Update transaction status in cache if (result.txHash) { updateTransaction(result.txHash, TX_STATUS.PENDING, { amount: lovelaceAmount, timestamp: Date.now() }); } return result; } catch (err: unknown) { const msg = err instanceof Error ? err.message : String(err); setError(msg); throw err; } finally { setProcessing(null); } }; const unlockFunds = async (txHash: string, amount: number) => { if (!address) return; setError(null); setProcessing(txHash); try { // Build → Sign → Submit pattern const txComplete = await buildUnlockTransaction(txHash, amount); if (!txComplete) throw new Error('Failed to build transaction'); const signedTx = await signTransaction(txComplete); await submitUnlockTransaction(signedTx, txComplete, txHash); // Update transaction status in cache updateTransaction(txHash, TX_STATUS.UNLOCKED); } catch (err: unknown) { const msg = err instanceof Error ? err.message : String(err); setError(msg); throw err; } finally { setProcessing(null); } }; return { lockFunds, unlockFunds, processing, error }; } ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-unlocking#id-5.-update-the-mytransactions-component) 5\. Update the MyTransactions Component Finally, update the MyTransactions component `src/components/MyTransactions.tsx` to include unlock buttons for confirmed transactions: * Import the `useTransactionOperations` hook. * Add the Unlock Button to the table row. * Add click handler function that uses `unlockFunds` from `useTransactionOperations` hook. * Add loading state for the unlock button. * Add error state for the unlock button. This is the updated MyTransactions component: MyTransactions.tsx[](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-unlocking#mytransactions.tsx) Copy "use client"; import { useWallet } from '@ada-anvil/weld/react'; import { usePollingTransactions, useTransactionOperations } from '@/hooks/useTransactions'; import { Transaction, TransactionStatus, TX_STATUS } from '@/lib/types'; const formatTxHash = (hash: string): string => { return `${hash.substring(0, 8)}...${hash.substring(hash.length - 4)}`; }; const formatDate = (timestamp: number): string => { const date = new Date(timestamp); return `${date.toLocaleDateString()} ${date.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}`; }; const formatAmount = (lovelaceAmount: number): string => { const LOVELACE_PER_ADA = 1_000_000; return (lovelaceAmount / LOVELACE_PER_ADA).toFixed(2); }; const STATUS_STYLES: Record = { [TX_STATUS.PENDING]: 'bg-yellow-100 text-yellow-800', [TX_STATUS.SIGN_LOCK]: 'bg-yellow-100 text-yellow-800', [TX_STATUS.SIGN_UNLOCK]: 'bg-yellow-100 text-yellow-800', [TX_STATUS.CONFIRMED]: 'bg-green-100 text-green-800', [TX_STATUS.UNLOCKED]: 'bg-blue-100 text-blue-800', }; const STATUS_LABELS: Record = { [TX_STATUS.PENDING]: 'Pending', [TX_STATUS.SIGN_LOCK]: 'Sign to Lock', [TX_STATUS.SIGN_UNLOCK]: 'Sign to Unlock', [TX_STATUS.CONFIRMED]: 'Confirmed', [TX_STATUS.UNLOCKED]: 'Unlocked', }; const STATUS_DETAILS: Record = { [TX_STATUS.PENDING]: 'Waiting for Cardano blockchain confirmation...', [TX_STATUS.SIGN_LOCK]: 'Transaction needs to be signed to lock funds', [TX_STATUS.SIGN_UNLOCK]: 'Transaction needs to be signed to unlock funds', [TX_STATUS.CONFIRMED]: 'Transaction confirmed on the Cardano blockchain', [TX_STATUS.UNLOCKED]: 'Funds have been successfully unlocked', }; export default function MyTransactions() { const wallet = useWallet(); const address = wallet.changeAddressBech32; const { data: transactions = [], error, isLoading } = usePollingTransactions(address); const { unlockFunds, processing: unlocking, error: unlockError } = useTransactionOperations(wallet, address); const handleUnlock = (txHash: string, amount: number) => { if (!address) return; unlockFunds(txHash, amount).catch(err => { console.debug('Transaction unlock error:', err); }); }; return (

My Transactions

{/* Handle different states */} {!address && (

Please connect your wallet to view your transactions.

)} {address && error && (

Error loading transactions

{(error as Error).message}

)} {address && isLoading && (

Loading your transactions...

)} {/* Display transactions if no errors, not loading, and have an active Weld wallet address*/} {address && !error && !isLoading && ( <> {/* Display any unlock errors */} {unlockError && (
{unlockError}
)} {/* Display empty state if no transactions */} {transactions.length === 0 ? (

No transactions found. Lock some funds to get started.

) : ( // Display transactions table {transactions.map((transaction: Transaction) => { const isPending = transaction.status === TX_STATUS.PENDING; return ( ); })}
Transaction Amount Status Date Action
{formatTxHash(transaction.txHash)} {formatAmount(transaction.amount)} ADA {isPending ? ( {STATUS_LABELS[transaction.status]} ) : ( {STATUS_LABELS[transaction.status] || transaction.status} )} {formatDate(transaction.timestamp)} {/* Unlock button, only visible for confirmed transactions */} {transaction.status === TX_STATUS.CONFIRMED ? ( ) : ( - )}
)} )}
); } [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-unlocking#security-features-of-the-unlocking-process) Security Features of the Unlocking Process ------------------------------------------------------------------------------------------------------------------------------------------------------------------- Several security measures are implemented in the unlocking process: 1. **Status Verification**: Only transactions in the CONFIRMED state can be unlocked 2. **Address Validation**: The transaction is validated against the owner's address 3. **Smart Contract Validation**: The escrow script verifies that the unlocking is performed by the original owner [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-unlocking#testing-the-complete-escrow-cycle) Testing the Complete Escrow Cycle ------------------------------------------------------------------------------------------------------------------------------------------------- Let's test the full escrow cycle: 1. Start your development server: Copy npm run dev 1. Navigate to http://localhost:3000 in your browser 2. Connect your wallet 3. Lock funds using the lock funds form from Part 3 4. Wait for the transaction to be confirmed (You'll see the status change in the MyTransactions component) 5. Click the "Unlock Funds" button on the confirmed transaction 6. Sign the transaction with your wallet 7. Observe the transaction status changing to UNLOCKED 8. Verify that the funds have been returned to your wallet (minus network fees) [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-unlocking#troubleshooting) Troubleshooting ------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-unlocking#transaction-not-unlockable) Transaction Not Unlockable If you can't unlock a transaction: 1. Verify that the transaction status is CONFIRMED (only confirmed transactions can be unlocked) 2. Ensure you're using the same wallet that created the transaction 3. Check that the network has processed the original transaction (check explorers like cardanoscan.io) ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-unlocking#signing-failures) Signing Failures If signing the unlock transaction fails: 1. Make sure your wallet is unlocked and connected 2. Check that your wallet has sufficient balance for the transaction fee 3. Try disconnecting and reconnecting your wallet [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-unlocking#enhancements-for-a-production-application) Enhancements for a Production Application ----------------------------------------------------------------------------------------------------------------------------------------------------------------- For a production-ready escrow application, consider these enhancements: 1. **Time-locked Escrow**: Add support for time-based unlocking conditions 2. **Multi-signature Escrow**: Implement escrow that requires approval from multiple parties 3. **Improved Error Handling**: Add more detailed error messages and recovery options 4. **Transaction Monitoring**: Implement more robust transaction status monitoring 5. **Sign Lock/Unlock**: Add support for signing lock and unlock transactions after users leave the page. 6. **Enhanced Security**: Add additional verification steps for high-value transactions Congratulations! You've completed all six parts of the Cardano Escrow guide. Your application now offers a complete escrow solution using the Cardano blockchain and Anvil API. [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-unlocking#conclusion) Conclusion --------------------------------------------------------------------------------------------------- In this guide series, you've built a fully functional Cardano escrow application that allows users to: 1. Connect their Cardano wallets 2. Lock funds in an escrow contract 3. Monitor transaction status in real-time 4. Unlock funds when they're ready You've learned how to: * Integrate with Cardano wallets using Weld * Build and submit transactions using the Anvil API * Store and track transaction data in a SQLite database * Implement real-time updates with webhooks * Create a complete fund locking and unlocking cycle This application provides a foundation for building more complex Cardano applications that leverage smart contracts for secure and transparent financial interactions. [PreviousReal-time Updates](https://dev.ada-anvil.io/guides/smart-contract/escrow/realtime-updates) [NextUtility Functions](https://dev.ada-anvil.io/developer-tools/utility-functions) Last updated 2 months ago Was this helpful? --- # Real-time Updates | Anvil Development Agency [](https://dev.ada-anvil.io/guides/smart-contract/escrow/realtime-updates#introduction) Introduction --------------------------------------------------------------------------------------------------------- In this section, we'll enhance our transaction list by implementing real-time updates using Blockfrost webhooks. This will provide a much better user experience by showing immediate updates when transactions are confirmed on the blockchain. However note that in order to implement this in a production application, you will need to implement proper rollback protection and handle lost transactions. [](https://dev.ada-anvil.io/guides/smart-contract/escrow/realtime-updates#webhook-overview) Webhook Overview ----------------------------------------------------------------------------------------------------------------- Webhooks provide a mechanism for receiving real-time notifications about blockchain events. Instead of constantly polling for transaction confirmation updates, our application will receive instant notifications when transactions are confirmed (Based on the confirmation threshold set in the webhook configuration) on the Cardano blockchain. Our hybrid approach uses: 1. **Initial polling**: For pending transactions when they're first created 2. **Webhooks**: For confirmation notifications when the blockchain confirms transactions Key advantages of this hybrid approach: * **Real-time Updates**: Instant notifications when transactions are confirmed, without waiting for the next poll cycle * **Reduced API Usage**: Significantly fewer polling requests compared to a pure polling solution * **Better User Experience**: More immediate UI updates for confirmed transactions * **Server Resource Efficiency**: Lower overall load on both client and server [](https://dev.ada-anvil.io/guides/smart-contract/escrow/realtime-updates#implementation-steps) Implementation Steps ------------------------------------------------------------------------------------------------------------------------- 1. Install ngrok (a tunneling service) and start your development server at `http://localhost:3000` 2. Set up Blockfrost webhook and have them send notifications to your ngrok URL with the endpoint `/api/webhooks/blockfrost` 3. Implement Next.js route handler for webhook endpoint `src/app/api/webhooks/blockfrost/route.ts` 4. Handle webhook events in the endpoint ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/realtime-updates#id-1.-install-and-configure-ngrok) 1\. Install and Configure ngrok Since Blockfrost webhooks require a public URL to send notifications to, first we'll use ngrok to expose your local development server. #### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/realtime-updates#installing-ngrok) Installing ngrok 1. Sign up for a free ngrok account at [dashboard.ngrok.com](https://dashboard.ngrok.com/) 2. Install ngrok using npm: Copy npm install -g ngrok 1. Configure ngrok with your auth token (found in your ngrok dashboard): ![Ngrok Auth Token](https://dev.ada-anvil.io/~gitbook/image?url=https%3A%2F%2F2837083171-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fi5SfFhLFp4rKhwQyZN9d%252Fuploads%252Fgit-blob-3ba235ec53cbcf3c401f111c6887018b058214ba%252Fngrok-auth-token.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=4818f1dc&sv=2) Ngrok Auth Token Copy ngrok config add-authtoken YOUR_AUTH_TOKEN 1. Start your Next.js development server if it's not already running: Copy npm run dev 1. In a separate terminal, start ngrok to expose your local server: Copy ngrok http 3000 #### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/realtime-updates#after-starting-ngrok) After Starting ngrok 1. Once ngrok is running, you'll see a terminal display with connection information 2. Look for the 'Forwarding' URL that looks like `https://xxxx-xxx-xxx-xxx-xxx.ngrok-free.app` 3. Copy this URL - you'll need it for configuring your Blockfrost webhook 4. Keep this terminal window open as long as you need the tunnel active ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/realtime-updates#id-2.-set-up-blockfrost-webhook) 2\. Set Up Blockfrost Webhook 1. Create a Blockfrost account at [blockfrost.io](https://blockfrost.io/) 2. Navigate to the Webhooks section in your Blockfrost dashboard 3. Click the 'Create webhook' button to create a new webhook 4. Configure your webhook with these settings: ![Blockfrost Webhook Configuration Interface](https://dev.ada-anvil.io/~gitbook/image?url=https%3A%2F%2F2837083171-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fi5SfFhLFp4rKhwQyZN9d%252Fuploads%252Fgit-blob-1737cbec6e3afc73e7734919fc8936e4e5a98a7d%252Fblockfrost-webhook-config.png%3Falt%3Dmedia&width=768&dpr=4&quality=100&sign=bbb641a6&sv=2) Blockfrost Webhook Configuration Interface 1. **Webhook name**: Give it a descriptive name (e.g., 'my-app-escrow-lock-trigger-ngrok') 2. **Endpoint URL**: Enter your ngrok URL followed by your webhook endpoint path (e.g., 'https://xxxx-xxx-xxx-xxx-xxx.ngrok-free.app/api/webhook/blockfrost') 3. **Network**: Select 'Cardano preprod' for testnet development 4. **Status**: Ensure it's enabled 5. **Trigger**: Select 'Transaction' 6. **Required confirmations**: Set to your desired confirmation threshold (typically 1-3 for testing) 7. **Trigger conditions**: * Set condition type to 'recipient' * Set operator to 'equal to' * Enter your escrow script address (this can be found in the script deployment data or derived from the validator hash using the Anvil API) 8. Click 'Save webhook' to finalize the configuration > **Important**: Save the 'Auth token' provided by Blockfrost, and update your local `.env.local` file with your `BLOCKFROST_WEBHOOK_SECRET`. ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/realtime-updates#id-3.-create-the-webhook-endpoint) 3\. Create the Webhook Endpoint Let's create the webhook endpoint `src/app/api/webhooks/blockfrost/route.ts` that will receive notifications from Blockfrost: Copy // src/app/api/webhooks/blockfrost/route.ts import { NextResponse } from "next/server"; import { upsertWallet, upsertTx } from "@/lib/db"; import { TX_STATUS } from "@/lib/types"; export async function POST(request: Request) { try { const body = await request.json(); for (const event of body.payload) { const wallet = event.inputs?.[0]?.address; const txHash = event.tx.hash; const amount = event.outputs?.[0]?.amount.find( (a: { unit: string; quantity: string }) => a.unit === "lovelace" )?.quantity; if (wallet && amount) { upsertWallet(wallet); upsertTx(txHash, wallet, Number(amount), TX_STATUS.CONFIRMED); } } } catch (err) { console.error("Failed to handle webhook:", err); } return NextResponse.json({ ok: true }); } ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/realtime-updates#id-5.-update-your-blockfrostwebhook-url) 5\. Update Your BlockfrostWebhook URL Now that your webhook endpoint is set up and ngrok is running: 1. Return to the Blockfrost dashboard 2. Update your webhook with the new ngrok URL: `https://[your-ngrok-url]/api/webhooks/blockfrost` 3. Make sure you press 'Save Webhook' to save the changes ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/realtime-updates#id-6.-webhook-payload-structure) 6\. Webhook Payload Structure Blockfrost will send notifications with a payload structure like this: Copy { "id": "webhook-id", "webhook_id": "your-webhook-id", "created": 1625558414, "api_version": 0, "type": "transaction", "payload": [\ {\ "tx": {\ "hash": "transaction_hash_here"\ },\ "inputs": [\ {\ "address": "wallet_address_here"\ }\ ],\ "outputs": [\ {\ "amount": [\ {\ "unit": "lovelace",\ "quantity": "amount_in_lovelace"\ }\ ]\ }\ ]\ }\ ] } ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/realtime-updates#id-7.-smart-polling-implementation) 7\. Smart Polling Implementation Let's implement a smart polling mechanism in `src/hooks/useTransactions.ts` that only activates when there are pending transactions. This approach conserves resources while still providing timely updates: Add the React `useEffect` hook and the `usePollingTransactions` hook to the `useTransactions` hook: Copy // src/hooks/useTransactions.ts // Import React useEffect hook import { useEffect, useState } from "react"; /** * Hook to manage transaction data with polling that automatically * activates only when pending transactions are detected * @param wallet - Wallet address to fetch transactions for * @returns Transaction data with loading and error states */ export function usePollingTransactions(wallet?: string) { // Track if we have any pending transactions that need polling const [hasPendingTx, setHasPendingTx] = useState(false); const query = useQuery({ queryKey: ['transactions', wallet], queryFn: async () => { if (!wallet) throw new Error("Wallet is required"); const response = await fetch(`/api/escrow/transactions?wallet=${wallet}`); return response.json(); }, enabled: !!wallet, // Only activate polling when wallet is connected AND we have pending transactions refetchInterval: wallet && hasPendingTx ? 5000 : false, }); // Monitor transaction data for pending status changes useEffect(() => { if (query.data) { // Check if any transactions have PENDING status const isPending = query.data.some(tx => tx.status === TX_STATUS.PENDING); // Only update state if the pending status changed // This prevents unnecessary re-renders if (isPending !== hasPendingTx) { setHasPendingTx(isPending); } } }, [query.data, hasPendingTx]); return query; } ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/realtime-updates#id-7.-update-mytransactions-component) 7\. Update MyTransactions Component Finally, we need to update our `MyTransactions` component `src/components/MyTransactions.tsx` to use the new polling hook instead of the regular transactions hook: Copy // src/components/MyTransactions.tsx "use client"; // Update this import to use the new polling hook instead import { usePollingTransactions } from '@/hooks/useTransactions'; import { useWallet } from '@ada-anvil/weld/react'; import { Transaction, TransactionStatus, TX_STATUS } from '@/lib/types'; // Rest of component code remains the same... export default function MyTransactions() { const wallet = useWallet(); const address = wallet.changeAddressBech32; // Replace useTransactionsByWallet with usePollingTransactions const { data: transactions = [], error, isLoading } = usePollingTransactions(address); // Rest of component remains the same... } This change enables smart polling that automatically activates when there are pending transactions, and disables when all transactions are confirmed, providing real-time updates while conserving resources. ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/realtime-updates#id-8.-test-the-webhook-integration) 8\. Test the Webhook Integration Let's test the complete flow with webhooks: 1. Ensure your Next.js server is running 2. Verify ngrok is properly exposing your local server 3. Check that the Blockfrost webhook is configured and enabled 4. Connect your wallet in the application 5. Lock some funds using the Lock Funds form 6. Observe how the transaction initially shows as "Pending" 7. Wait for Blockfrost to detect the transaction confirmation and send a webhook 8. Verify that the transaction status updates to "Confirmed" immediately after the webhook is received [](https://dev.ada-anvil.io/guides/smart-contract/escrow/realtime-updates#understanding-the-webhook-flow) Understanding the Webhook Flow --------------------------------------------------------------------------------------------------------------------------------------------- The complete flow with webhooks works as follows: 1. **Transaction Creation**: User locks funds, creating a transaction 2. **Initial Status**: Transaction is stored with "PENDING" status 3. **Initial Polling**: UI polls frequently at first to provide responsive feedback 4. **Blockchain Confirmation**: Transaction is confirmed on the Cardano blockchain 5. **Webhook Notification**: Blockfrost detects the confirmation and sends a webhook 6. **Status Update**: Our webhook handler updates the transaction status to "CONFIRMED" 7. **UI Update**: The next poll or React Query invalidation refreshes the UI This hybrid approach combines the best of both worlds: * Immediate feedback through initial polling * Reliable long-term updates through webhooks * Fallback to extended polling for resilience [](https://dev.ada-anvil.io/guides/smart-contract/escrow/realtime-updates#monitoring-webhook-activity) Monitoring Webhook Activity --------------------------------------------------------------------------------------------------------------------------------------- To help troubleshoot and verify webhook activity, let's add some simple logging: Copy // src/app/api/webhooks/blockfrost/route.ts // Add this to the existing file // Add at the top of the POST function console.log(`Received webhook at ${new Date().toISOString()}`); // Add inside the try block after processing console.log(`Processed ${body.payload.length} webhook events`); // Add inside the catch block console.error("Webhook error details:", JSON.stringify(err)); [](https://dev.ada-anvil.io/guides/smart-contract/escrow/realtime-updates#webhook-security-considerations) Webhook Security Considerations ----------------------------------------------------------------------------------------------------------------------------------------------- In a production environment, you would want to add security to your webhook endpoint: 1. **Webhook Secrets**: Use a shared secret with Blockfrost to verify webhook authenticity 2. **IP Filtering**: Restrict access to known Blockfrost IP addresses 3. **Rate Limiting**: Protect against DoS attacks by implementing rate limiting 4. **Request Validation**: Validate the webhook payload structure before processing For our tutorial, we've kept it simple, but consider these enhancements for production use. [](https://dev.ada-anvil.io/guides/smart-contract/escrow/realtime-updates#benefits-of-real-time-updates) Benefits of Real-time Updates ------------------------------------------------------------------------------------------------------------------------------------------- With the webhook implementation, users will experience: 1. **Immediate Feedback**: Transaction status changes are reflected immediately 2. **Reduced Network Traffic**: No need for constant polling requests 3. **Better Battery Life**: Mobile devices benefit from fewer background requests 4. **More Responsive UI**: Status changes happen in near real-time [](https://dev.ada-anvil.io/guides/smart-contract/escrow/realtime-updates#troubleshooting) Troubleshooting --------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/realtime-updates#webhook-not-receiving-events) Webhook Not Receiving Events If your webhook isn't receiving events: 1. Check ngrok status - ensure the tunnel is active 2. Verify your webhook URL in the Blockfrost dashboard 3. Check Blockfrost webhook logs for delivery attempts 4. Ensure your application is properly handling POST requests ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/realtime-updates#inconsistent-status-updates) Inconsistent Status Updates If transaction statuses are not updating correctly: 1. Enable detailed logging in your webhook handler 2. Verify the transaction hash matching in your database operations 3. Check that the database is successfully updated when webhooks are received During development, Blockfrost may take a few minutes to start sending webhooks for newly configured endpoints. Be patient during initial testing. [](https://dev.ada-anvil.io/guides/smart-contract/escrow/realtime-updates#best-practices-for-webhook-implementation) Best Practices for Webhook Implementation ------------------------------------------------------------------------------------------------------------------------------------------------------------------- 1. **Idempotency**: Ensure your webhook handler can safely process the same event multiple times 2. **Async Processing**: For high-volume applications, consider processing webhooks asynchronously 3. **Monitoring**: Implement logging and monitoring to track webhook health 4. **Fallback Strategy**: Always maintain a fallback mechanism (like polling) for resilience Congratulations! You've completed Part 5 of the guide. Your application now receives real-time updates when transactions are confirmed on the blockchain, providing a much better user experience. [PreviousTransaction Dashboard](https://dev.ada-anvil.io/guides/smart-contract/escrow/transaction-dashboard) [NextFund Unlocking](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-unlocking) Last updated 16 days ago Was this helpful? --- # Fund Locking | Anvil Development Agency [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-locking#introduction) Introduction ----------------------------------------------------------------------------------------------------- In this part, we'll implement the fund locking functionality, which allows users to securely lock their ADA in an escrow contract. This involves creating a transaction that transfers funds to a script address with specific conditions for unlocking. [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-locking#understanding-the-fund-locking-process) Understanding the Fund Locking Process --------------------------------------------------------------------------------------------------------------------------------------------------------- The fund locking process involves these steps: 1. **User Input**: The user selects an amount of ADA to lock. 2. **Transaction Building**: The application calls the Anvil API to build a transaction that sends funds to the escrow script address with the user's key hash as a datum. 3. **Transaction Signing**: The connected wallet signs the transaction, authorizing the fund transfer. 4. **Transaction Submission**: The signed transaction is submitted to the Cardano blockchain. 5. **Status Tracking**: The transaction hash is displayed to the user. In Part 4, we'll implement proper status tracking. [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-locking#smart-contract-datum-structure) Smart Contract Datum Structure ----------------------------------------------------------------------------------------------------------------------------------------- When locking funds in the escrow address, we attach a datum containing the owner's verification key hash. This datum is essential for two reasons: 1. The Hello World smart contract uses it to validate unlock requests 2. It associates locked funds with their rightful owner When building transaction outputs, we include this datum with the escrow script address, ensuring only the original owner can later unlock these funds with the correct signature. Copy datum: { type: "inline", value: { owner: paymentKeyHash // The owner's key hash who can later unlock }, shape: { validatorHash: validatorHash, purpose: "spend" } } This datum associates the locked funds with the original owner, ensuring only they can unlock these funds later when providing the correct redeemer message ("Hello, World!") and their signature. [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-locking#setup-react-query) Setup React Query --------------------------------------------------------------------------------------------------------------- Before we integrate with the Anvil API, let's first set up React Query which we'll use for managing transaction state `src/components/ReactQueryProvider.tsx`: ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-locking#id-1.-create-react-query-provider) 1\. Create React Query Provider Copy // src/components/ReactQueryProvider.tsx "use client"; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { ReactNode } from 'react'; const queryClient = new QueryClient(); export default function ReactQueryProvider({ children }: { children: ReactNode }) { return ( {children} ); } ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-locking#id-2.-update-root-layout) 2\. Update Root Layout Update your `src/app/layout.tsx` component to include the React Query provider: You need to add the import and wrap the application with the `ReactQueryProvider` provider. RootLayout Component \`src/app/layout.tsx\`[](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-locking#rootlayout-component-src-app-layout.tsx) Copy import type { Metadata } from "next"; import { Geist, Geist_Mono } from "next/font/google"; import "./globals.css"; import { ClientWeldProvider } from "@/components/WeldProvider"; import ReactQueryProvider from '@/components/ReactQueryProvider'; import { cookies } from "next/headers"; import { STORAGE_KEYS } from "@ada-anvil/weld/server"; const geistSans = Geist({ variable: "--font-geist-sans", subsets: ["latin"], }); const geistMono = Geist_Mono({ variable: "--font-geist-mono", subsets: ["latin"], }); export const metadata: Metadata = { title: "Cardano Smart Escrow", description: "Smart escrow solution for Cardano blockchain", }; export default async function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { // Retrieve wallet connection state from cookies for SSR const cookieStore = await cookies(); const wallet = cookieStore.get(STORAGE_KEYS.connectedWallet)?.value; const changeAddress = cookieStore.get(STORAGE_KEYS.connectedChange)?.value; const stakeAddress = cookieStore.get(STORAGE_KEYS.connectedStake)?.value; const lastConnectedWallet = wallet ? { wallet, changeAddress, stakeAddress } : undefined; return ( {/* Wrap the application with the ClientWeldProvider for wallet connectivity */} {children} ); } [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-locking#adding-anvil-api-integration) Adding Anvil API Integration ------------------------------------------------------------------------------------------------------------------------------------- We'll first set up the Anvil API integration, which we'll use for transaction building and submission. ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-locking#id-2.-create-the-anvil-api-module) 2\. Create the Anvil API Module Let's create a `src/lib/anvil-api.ts` module to handle interactions with the Anvil API for building and submitting transactions: Notice how the `lockFunds` function that calls the Anvil API. This is building a transaction that sends ADA to the escrow script address with the specified datum. Once submitted, the transaction will be included in a block and the ADA will be locked in the escrow address. Copy // src/lib/anvil-api.ts const API = process.env.ANVIL_API_ENDPOINT; const X_API_KEY = process.env.ANVIL_API_KEY; if (!API || !X_API_KEY) { throw new Error('ANVIL_API_ENDPOINT or ANVIL_API_KEY environment variables are not set'); } const getHeaders = () => ({ 'Content-Type': 'application/json', 'x-api-key': X_API_KEY, }); // Error handling utilities const handleApiError = (context: string, error: unknown): string => { console.error(`Error ${context}:`, error); const message = error instanceof Error ? error.message : String(error); return `Failed to ${context}: ${message}`; }; // Generic API fetch with error handling async function fetchApi(endpoint: string, options: RequestInit, context: string): Promise { try { const response = await fetch(`${API}${endpoint}`, options); if (!response.ok) { const errText = await response.text(); console.error(`${context} error:`, response.status, response.statusText, errText); throw new Error(`${response.status} ${response.statusText} - ${errText}`); } return await response.json() as T; } catch (error) { throw new Error(handleApiError(context, error)); } } // Interface for lock funds parameters interface LockFundsParams { changeAddress: string; // User's wallet address for change lovelaceAmount: number; // Amount in lovelace to lock in escrow ownerKeyHash: string; // Public key hash of the owner who can unlock funds message?: string; // Optional transaction message } // Interface for the lock funds response interface LockFundsResponse { txHash?: string; // Transaction hash if successful complete?: string; // Complete tx for client-side signing error?: string; // Error message if the request fails } /** * Get the script address for a validator hash */ export async function getScriptAddress(validatorHash: string): Promise { const data = await fetchApi<{ hex: string }>( `/validators/${validatorHash}/address`, { method: 'GET', headers: getHeaders(), }, 'get script address' ); return data.hex; } /** * Get the payment verification key hash from an address */ export async function getAddressKeyHash(address: string): Promise { const data = await fetchApi<{ payment: string }>( `/utils/addresses/parse`, { method: 'POST', headers: getHeaders(), body: JSON.stringify({ address }), }, 'get address key hash' ); return data.stake; } /** * Lock funds in the escrow smart contract * This creates a transaction that sends ADA to the script address with the specified datum */ export async function lockFunds(params: LockFundsParams): Promise { try { // Derive owner stake key hash for datum. // This is in case wallets use multi-accounts (i.e. Eternl) and the you use a different payment address to unlock the funds // Using the stake key hash will allow you to use any payment address to sign and pay for Anvil API fees and still unlock the funds. const stakeKeyHash = await getAddressKeyHash(params.changeAddress); // Get the validator hash from environment const validatorHash = process.env.ESCROW_VALIDATOR_HASH; if (!validatorHash) { throw new Error('Escrow validator hash not found'); } // Get script address const scriptAddress = await getScriptAddress(validatorHash); // Prepare the transaction input const input = { changeAddress: params.changeAddress, message: params.message || "Locking funds in escrow using Anvil API", outputs: [\ {\ address: scriptAddress,\ lovelace: params.lovelaceAmount,\ datum: {\ type: "inline",\ value: {\ owner: stakeKeyHash\ },\ shape: {\ validatorHash: validatorHash,\ purpose: "spend"\ }\ }\ }\ ], }; // Build the transaction using our generic fetch utility const result = await fetchApi<{ hash: string, complete: string }>( `/transactions/build`, { method: 'POST', headers: getHeaders(), body: JSON.stringify(input), }, 'build lock transaction' ); // Return hash and complete transaction for client-side signing and DB recording return { txHash: result.hash, complete: result.complete, }; } catch (error: unknown) { console.error('Error locking funds:', error); const message = error instanceof Error ? error.message : String(error); return { error: message }; } } /** * Submit a signed transaction to the blockchain */ export async function submitTransaction(signedTx: string, complete: string): Promise<{ txHash: string }> { const result = await fetchApi<{ txHash: string }>( `/transactions/submit`, { method: 'POST', headers: getHeaders(), body: JSON.stringify({ signatures: [signedTx], transaction: complete, }), }, 'submit transaction' ); return { txHash: result.txHash }; } ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-locking#id-4.-update-transaction-status-types) 4\. Update Transaction Status Types Let's add a new `types.ts` file to include the states for the locking process: Copy // src/lib/types.ts // Transaction status constants export const TX_STATUS = { PENDING: 'pending' as const, SIGN_LOCK: 'signLock' as const, SIGN_UNLOCK: 'signUnlock' as const, CONFIRMED: 'confirmed' as const, UNLOCKED: 'unlocked' as const, } as const; // Create a type from the values export type TransactionStatus = typeof TX_STATUS[keyof typeof TX_STATUS]; export type Transaction = { txHash: string; wallet: string; amount: number; status: TransactionStatus; timestamp: number; }; ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-locking#id-5.-create-api-endpoint-for-locking-funds) 5\. Create API Endpoint for Locking Funds Now, let's create an API endpoint `src/app/api/escrow/lock/route.ts` in our Next.js application for locking funds: Copy // src/app/api/escrow/lock/route.ts import { NextRequest, NextResponse } from 'next/server'; import { lockFunds } from '@/lib/anvil-api'; export async function POST(request: NextRequest) { try { const body = await request.json(); const { changeAddress, amount, ownerKeyHash, message } = body; if (!changeAddress || amount == null || !ownerKeyHash) { return NextResponse.json( { error: 'Missing required parameters' }, { status: 400 } ); } const { txHash, complete, error } = await lockFunds({ changeAddress, lovelaceAmount: amount, ownerKeyHash, message: message || 'Locking funds in escrow using Anvil API', }); if (error || !txHash || !complete) { return NextResponse.json( { error: error || 'Failed to build lock transaction' }, { status: 500 } ); } return NextResponse.json({ txHash, complete }); } catch (err: unknown) { console.error('Error locking funds:', err); const message = err instanceof Error ? err.message : String(err); return NextResponse.json( { error: message || 'Failed to lock funds' }, { status: 500 } ); } } ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-locking#id-6.-create-api-endpoint-for-transaction-submission) 6\. Create API Endpoint for Transaction Submission After we have called the API to build the transaction, we will prompt the user to sign the transaction via Weld. Upon signing, we will submit the signed transaction to the blockchain via the following API route: This `src/app/api/escrow/submit/route.ts` endpoint will be used for submitting both lock and unlock transactions. We will update it later to handle unlock transactions. Copy // src/app/api/escrow/submit/route.ts import { NextRequest, NextResponse } from 'next/server'; import { submitTransaction } from '@/lib/anvil-api'; import { TX_STATUS } from '@/lib/types'; export async function POST(request: NextRequest) { try { const body = await request.json(); const { complete, signature, type } = body; // Validate inputs if (!complete || !signature || type !== TX_STATUS.SIGN_LOCK) { return NextResponse.json( { error: 'Missing complete transaction or signature' }, { status: 400 } ); } // Submit the signed transaction to the blockchain const result = await submitTransaction(signature, complete); return NextResponse.json({ txHash: result.txHash }); } catch (error: unknown) { console.error('Error submitting transaction:', error); const message = error instanceof Error ? error.message : String(error); return NextResponse.json( { error: message || 'Failed to submit transaction' }, { status: 500 } ); } } ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-locking#id-7.-create-the-transaction-operations-hook) 7\. Create the Transaction Operations Hook Let's create a custom hook `src/hooks/useTransactionOperations.ts` to handle transaction operations: See the `lockFunds` function to see how we call our API endpoints and use Weld to sign the transaction between calls. We will also update this later to handle unlock transactions. Copy // src/hooks/useTransactionOperations.ts "use client"; import { useQueryClient } from '@tanstack/react-query'; import { Transaction, TransactionStatus, TX_STATUS } from '@/lib/types'; import { useCallback, useState } from 'react'; export function useTransactionUpdater(wallet?: string) { const queryClient = useQueryClient(); return useCallback( (txHash: string, newStatus: TransactionStatus, newTransaction?: Partial) => { if (!wallet) return; // Update cache optimistically queryClient.setQueryData( ['transactions', wallet], old => { if (!old) return []; // Update existing transaction or add new one const existingTx = old.find(tx => tx.txHash === txHash); if (existingTx) { return old.map(tx => tx.txHash === txHash ? { ...tx, status: newStatus } : tx ); } // Create and add new transaction if provided if (newTransaction) { const newTx: Transaction = { txHash, wallet: wallet, amount: newTransaction.amount || 0, status: newStatus, timestamp: newTransaction.timestamp || Date.now(), }; return [newTx, ...old]; } return old; } ); // Refresh data from server queryClient.invalidateQueries({ queryKey: ['transactions', wallet] }); }, [queryClient, wallet] ); } //-------------------------------------------------------------- // Transaction operation hooks (API calls, wallet operations) //-------------------------------------------------------------- type CardanoWallet = { changeAddressHex?: string; handler?: { signTx: (txComplete: string, witnessOnly: boolean) => Promise; }; }; // API response types interface TransactionResponse { txHash: string; error?: string; } interface BuildTransactionResponse { complete?: string; error?: string; } const apiPost = async (endpoint: string, payload: Record): Promise => { const response = await fetch(`/api/escrow/${endpoint}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }); const data = await response.json(); if (data.error) { throw new Error(data.error); } return data as T; }; export function useTransactionOperations(wallet: CardanoWallet, address?: string) { const [processing, setProcessing] = useState(null); const [error, setError] = useState(null); const updateTransaction = useTransactionUpdater(address); const LOVELACE_PER_ADA = 1_000_000; const buildLockTransaction = async (lovelaceAmount: number) => { if (!address || !wallet?.changeAddressHex) return null; const data = await apiPost('lock', { changeAddress: address, amount: lovelaceAmount, ownerKeyHash: wallet.changeAddressHex, message: `Locking ${lovelaceAmount / LOVELACE_PER_ADA} ADA in escrow` }); if (!data.complete) { throw new Error('Build failed'); } return data.complete; }; // Transaction signing and submission const signTransaction = async (txComplete: string): Promise => { const signed = await wallet?.handler?.signTx(txComplete, true); if (!signed) { throw new Error('Signing failed'); } return signed; }; const submitLockTransaction = async (signedTx: string, txComplete: string, lovelaceAmount: number): Promise => { return apiPost('submit', { signature: signedTx, complete: txComplete, changeAddress: address, amount: lovelaceAmount, type: TX_STATUS.SIGN_LOCK }); }; const lockFunds = async (adaAmount: number) => { if (!address) return null; setError(null); setProcessing('lock'); try { // Convert ADA to Lovelace (smallest unit) const lovelaceAmount = adaAmount * LOVELACE_PER_ADA; // Build → Sign → Submit pattern const txComplete = await buildLockTransaction(lovelaceAmount); if (!txComplete) throw new Error('Failed to build transaction'); const signedTx = await signTransaction(txComplete); const result = await submitLockTransaction(signedTx, txComplete, lovelaceAmount); // Update transaction status in cache if (result.txHash) { updateTransaction(result.txHash, TX_STATUS.PENDING, { amount: lovelaceAmount, timestamp: Date.now() }); } return result; } catch (err: unknown) { const msg = err instanceof Error ? err.message : String(err); setError(msg); throw err; } finally { setProcessing(null); } }; return { lockFunds, processing, error }; } This hook encapsulates the logic for building, signing, and submitting transactions. It handles all the API calls and error states, making our component code cleaner. ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-locking#id-8a.-create-the-lock-funds-form-component) 8a. Create the Lock Funds Form Component Now, let's create the form component `src/components/LockFundsForm.tsx` for locking funds: Copy // src/components/LockFundsForm.tsx "use client"; import { useState } from 'react'; import { useWallet } from '@ada-anvil/weld/react'; import { useAmountSlider } from '@/hooks/useAmountSlider'; import { useTransactionOperations } from '@/hooks/useTransactions'; export default function LockFundsForm() { const wallet = useWallet(); const address = wallet.changeAddressBech32; const [successMessage, setSuccessMessage] = useState(null); const [txHash, setTxHash] = useState(null); const { lockFunds, processing, error: errorMessage } = useTransactionOperations(wallet, address); const isLocking = processing === 'lock'; const { amount, maxAmount, handleAmountChange, handleInputChange, sliderBackground, isWalletConnected } = useAmountSlider({ wallet }); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!isWalletConnected || amount <= 0 || !wallet.changeAddressBech32) return; setSuccessMessage(null); setTxHash(null); try { const result = await lockFunds(amount); if (result?.txHash) { setSuccessMessage(`Successfully prepared transaction to lock ${amount} ADA`); setTxHash(result.txHash); } } catch (error) { console.error('Failed to lock funds:', error); } }; return (

Lock Funds

{errorMessage && (
{errorMessage}
)} {successMessage && (
{successMessage}
{txHash && (
Transaction: {txHash}
)}
)}

{isWalletConnected ? `Available: ${wallet.balanceAda?.toFixed(2) || '0.00'} ADA` : 'Connect wallet to lock funds'}

); } ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-locking#id-8b.-create-the-amount-slider-hook) 8b. Create the Amount Slider Hook Let's create a custom hook `src/hooks/useAmountSlider.ts` for the amount slider to make our form component cleaner: Copy // src/hooks/useAmountSlider.ts import { useState, ChangeEvent, useMemo, useEffect } from 'react'; import { useWallet } from '@ada-anvil/weld/react'; type UseAmountSliderProps = { wallet: ReturnType; }; export const useAmountSlider = ({ wallet }: UseAmountSliderProps) => { const [amount, setAmount] = useState(1); const maxAmount = Number(wallet.balanceAda?.toFixed(2) || 1); useEffect(() => { if (!wallet.isConnected && amount !== 1) { setAmount(1); } }, [wallet.isConnected, amount]); const handleAmountChange = (e: ChangeEvent) => { const newAmount = parseFloat(e.target.value); const clampedAmount = Math.min(Math.max(newAmount, 1), maxAmount); setAmount(clampedAmount); }; const handleInputChange = (e: ChangeEvent) => { const value = e.target.value; if (value === '') { setAmount(1); return; } const newAmount = parseFloat(value); if (!isNaN(newAmount)) { const clampedAmount = Math.min(Math.max(newAmount, 1), maxAmount); setAmount(clampedAmount); } }; const isConnected = wallet.isConnected; const sliderBackground = useMemo(() => { if (!isConnected) return '#e5e7eb'; const percentage = (amount / maxAmount) * 100; return `linear-gradient(to right, black 0%, black ${percentage}%, #e5e7eb ${percentage}%, #e5e7eb 100%)`; }, [isConnected, amount, maxAmount]); return { amount, setAmount, maxAmount, handleAmountChange, handleInputChange, sliderBackground, isWalletConnected: wallet.isConnected }; }; ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-locking#id-9.-update-the-home-page) 9\. Update the Home Page Finally, update the home page `src/app/page.tsx` to include the LockFundsForm component: Copy // src/app/page.tsx /** * Main dashboard page for the Cardano Escrow application * Displays wallet connector and escrow functionality */ import WalletConnector from "@/components/WalletConnector"; import LockFundsForm from "@/components/LockFundsForm"; export default function Page() { return (

Cardano Escrow

); } [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-locking#testing-your-implementation) Testing Your Implementation ----------------------------------------------------------------------------------------------------------------------------------- To test the fund locking functionality: 1. Start your development server: Copy npm run dev 1. Navigate to http://localhost:3000 in your browser 2. Connect your wallet using the wallet connector 3. Use the slider to select an amount of ADA to lock 4. Click the "Lock Funds" button 5. Approve the transaction in your wallet when prompted 6. Verify that you see a success message with the transaction hash [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-locking#troubleshooting) Troubleshooting ----------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-locking#transaction-building-errors) Transaction Building Errors If you encounter errors when building transactions: * Check that your Anvil API key is correct in your `.env.local` file * Verify that the validator hash is set correctly in your environment variables * Ensure your wallet has sufficient funds (including for transaction fees) * Check the browser console and server logs for more detailed error messages ### [](https://dev.ada-anvil.io/guides/smart-contract/escrow/fund-locking#wallet-signing-errors) Wallet Signing Errors If transaction signing fails: * Make sure your wallet is unlocked * Check that you've approved the dApp connection * Try refreshing the page and reconnecting your wallet In a real application, you'd want to store transaction details for later reference. This lessens your need to query the blockchain for users transactions. We'll implement this in Part 4 using a database. Congratulations! You've completed Part 3 of the guide. Your application can now lock funds in a Cardano smart contract. [PreviousWallet Integration](https://dev.ada-anvil.io/guides/smart-contract/escrow/wallet-integration) [NextTransaction Dashboard](https://dev.ada-anvil.io/guides/smart-contract/escrow/transaction-dashboard) Last updated 2 months ago Was this helpful? --- # Email Protection | Cloudflare Please enable cookies. Email Protection ================ You are unable to access this email address dev.ada-anvil.io ------------------------------------------------------------ The website from which you got to this page is protected by Cloudflare. Email addresses on that page have been hidden in order to keep them from being accessed by malicious bots. **You must enable Javascript in your browser in order to decode the e-mail address**. If you have a website and are interested in protecting it in a similar way, you can [sign up for Cloudflare](https://www.cloudflare.com/sign-up?utm_source=email_protection) . * [How does Cloudflare protect email addresses on website from spammers?](https://developers.cloudflare.com/waf/tools/scrape-shield/email-address-obfuscation/) * [Can I sign up for Cloudflare?](https://developers.cloudflare.com/fundamentals/setup/account/create-account/) Cloudflare Ray ID: **96771712eec3d46f** • Your IP: Click to reveal 54.237.218.47 • Performance & security by [Cloudflare](https://www.cloudflare.com/5xx-error-landing) --- # Mint with Smart Contract | Anvil Development Agency This guide provides a practical implementation of a mint validator. For a general explanation of script purposes and validator types, see [Mint Validator](https://dev.ada-anvil.io/guides/smart-contract/validators/mint-validator) . [](https://dev.ada-anvil.io/guides/smart-contract/mint-smart-contract#objective) Objective ----------------------------------------------------------------------------------------------- * Implement a simple Aiken smart contract for minting tokens * Demonstrate the `mint` purpose validator in action * Implement time constraints with `validity_range` * Require specific signers with `extra_signatories` [](https://dev.ada-anvil.io/guides/smart-contract/mint-smart-contract#where-to-find-the-code) Where to Find the Code ------------------------------------------------------------------------------------------------------------------------- The complete example is available in the [Simple Contract repository](https://github.com/Cardano-Forge/simple-contract/tree/main) . You can follow the [README.md](https://github.com/Cardano-Forge/simple-contract/blob/main/deno/README.md) for setup instructions. [](https://dev.ada-anvil.io/guides/smart-contract/mint-smart-contract#smart-contract-overview) Smart Contract Overview --------------------------------------------------------------------------------------------------------------------------- This example implements a minting policy with two validation conditions: 1. **Time-lock**: Minting is restricted to transactions before a predefined expiration timestamp 2. **Authorized Signer**: Only transactions signed by a specific key can mint tokens The contract is written in Aiken and compiled to a Plutus script that produces a CIP-57 blueprint. [](https://dev.ada-anvil.io/guides/smart-contract/mint-smart-contract#implementation-example) Implementation Example ------------------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/smart-contract/mint-smart-contract#key-components-for-minting-policy-transactions) Key Components for Minting Policy Transactions When building a transaction to mint tokens with a time-locked policy, you need to include: 1. **Mint Array**: Defines what tokens to create with their metadata 2. **Script Interactions**: Provides the redeemer needed by the validator 3. **Required Signers**: Ensures the transaction is signed by the authorized key 4. **Validity Interval**: Sets the transaction time to comply with the time-lock ### [](https://dev.ada-anvil.io/guides/smart-contract/mint-smart-contract#complete-implementation) Complete Implementation The following example demonstrates the full process of building, signing, and submitting a transaction that mints tokens with our time-locked policy: Copy import { Buffer } from "node:buffer"; import { FixedTransaction, PrivateKey } from "npm:@emurgo/cardano-serialization-lib-nodejs"; // Policy hash from your deployed contract blueprint const policyHash = "eb7bddc5b588e238d2974d544a479b6bc0dc06852b38d12308ac62e5"; // Admin key hash that is authorized to mint (from the contract) const adminKeyHash = "a2108b5b8f1fb54abdb20c23d8ef4b8303f53bd538e1cefe91335a5d"; // Step 1: Build the transaction input const input = { // Address to receive change from the transaction changeAddress: customer.base_address_preprod, // Optional transaction message (appears in block explorers) message: "Minting with time-locked policy", // Define the token to mint mint: [\ {\ version: "cip25",\ assetName: "MyToken001",\ policyId: policyHash,\ type: "plutus", // Indicates a Plutus script-based policy\ quantity: 1,\ metadata: {\ name: "My First Token",\ description: "A token with time-lock and signer verification",\ image: "ipfs://QmYourImageHash"\ },\ },\ ], // Provide script interaction with redeemer scriptInteractions: [\ {\ purpose: "mint",\ hash: policyHash,\ redeemer: {\ type: "hex",\ value: "00", // Simple redeemer for this contract (empty array in CBOR)\ },\ },\ ], // Required signer enforced by the policy requiredSigners: [adminKeyHash], // Time validity for the time-lock validityInterval: { // Current time in seconds (must be before the contract's deadline) start: Math.floor(Date.now() / 1000) } }; // Step 2: Build transaction with Anvil API const response = await fetch("https://preprod.api.ada-anvil.app/v2/services/transactions/build", { method: "POST", headers: { "x-api-key": apiKey, "Content-Type": "application/json" }, body: JSON.stringify(input), }); const result = await response.json(); const txToSign = result.complete; const tx = FixedTransaction.from_bytes(Buffer.from(txToSign, "hex")); // Step 3: Sign with both required keys (multi-signature) // Customer signature (paying for the transaction) tx.sign_and_add_vkey_signature(PrivateKey.from_bech32(customer.skey)); // Admin signature (required by the minting policy) tx.sign_and_add_vkey_signature(PrivateKey.from_bech32(admin.skey)); // Step 4: Submit transaction const submitted = await fetch("https://preprod.api.ada-anvil.app/v2/services/transactions/submit", { method: "POST", headers: { "x-api-key": apiKey, "Content-Type": "application/json" }, body: JSON.stringify({ transaction: tx.to_hex() }), }); const submittedResponse = await submitted.json(); console.log("Transaction ID:", submittedResponse.hash); [](https://dev.ada-anvil.io/guides/smart-contract/mint-smart-contract#important-technical-details) Important Technical Details ----------------------------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/smart-contract/mint-smart-contract#transaction-validation) Transaction Validation The transaction will only succeed if the time-lock and signature conditions are met: The transaction must be submitted before the contract's deadline timestamp. If the current time exceeds this deadline, validation will fail and the transaction will be rejected. ### [](https://dev.ada-anvil.io/guides/smart-contract/mint-smart-contract#signing-the-transaction) Signing the Transaction This example requires a **multi-signature transaction** where both the customer wallet and the admin wallet must sign: **Real-World Implementation** In production applications: * **Customer/User signatures** would typically be handled in the frontend using wallet connectors like [Weld](https://github.com/Cardano-Forge/weld) . These connectors will pass the signatures to the `transactions/submit` endpoint. * **Admin signatures** should be applied to the transaction on a secure server. Both signatures are required for different reasons: 1. **Customer signature**: Required because they own the inputs (UTXOs) being used to pay transaction fees 2. **Admin signature**: Required by the minting policy's `extra_signatories` condition If either signature is missing, the transaction will fail. Last updated 2 months ago Was this helpful? --- # Mint Validator | Anvil Development Agency **Note**: This guide is currently in development and will be completed soon [](https://dev.ada-anvil.io/guides/smart-contract/validators/mint-validator#mint-validator) Mint Validator --------------------------------------------------------------------------------------------------------------- ### [](https://dev.ada-anvil.io/guides/smart-contract/validators/mint-validator#overview) Overview Mint validators (also called minting policies) control the creation and destruction of native tokens on Cardano. They define the rules under which tokens can be minted (created) or burned (destroyed), acting as a programmatic authority over asset supply management. ### [](https://dev.ada-anvil.io/guides/smart-contract/validators/mint-validator#real-world-use-cases) Real-world Use Cases * **NFT Collections**: Regulate minting of unique, non-fungible assets with provable scarcity * **Fungible Tokens**: Create tokens with controlled supply mechanisms * **Token Burning**: Implement redemption or destruction policies * **Evolving NFTs**: Enable assets that can change state or properties over time * **Multi-stage Releases**: Control phased distribution of limited assets ### [](https://dev.ada-anvil.io/guides/smart-contract/validators/mint-validator#required-fields-for-token-minting) Required Fields for Token Minting **For mint validators, you need two key components in your transaction:** 1. **mint**: An array defining what tokens to mint or burn 2. **scriptInteractions**: Specifies the validator hash, purpose, and redeemer The policy ID in the mint array must match the validator hash in the scriptInteractions: Copy { "mint": [{ \ "type": "plutus",\ "policyId": "policy_id_here",\ "assetName": "TOKEN_NAME",\ "quantity": 1,\ "metadata": { ... } // Optional token metadata\ }], "scriptInteractions": [{\ "hash": "policy_id_here", // Must match policyId above\ "purpose": "mint",\ "redeemer": { ... }\ }] } **Note**: For native scripts (non-Plutus policies), use `type: "simple"` in the mint array and omit the scriptInteractions entry. **PREREQUISITE: DEPLOYED SMART CONTRACT** Before you can interact with any validator, you must have a compiled and deployed smart contract blueprint. The validator hash used in these examples comes from your deployed blueprint. Refer to the [Blueprint Management](https://dev.ada-anvil.io/guides/smart-contract/blueprint-management) guide for details on how to deploy your smart contract blueprint. ### [](https://dev.ada-anvil.io/guides/smart-contract/validators/mint-validator#mint-validator-api-interaction-example) Mint Validator API Interaction Example Use the below examples to build transactions that interact with mint validators. If you are unfamiliar with the Anvil API transaction builder, review the [Transaction Overview](https://dev.ada-anvil.io/guides/transaction) guide first. #### [](https://dev.ada-anvil.io/guides/smart-contract/validators/mint-validator#id-1.-minting-nfts-cip-25-standard) 1\. Minting NFTs (CIP-25 Standard) #### [](https://dev.ada-anvil.io/guides/smart-contract/validators/mint-validator#id-2.-minting-fungible-tokens-cip-68-standard) 2\. Minting Fungible Tokens (CIP-68 Standard) ### [](https://dev.ada-anvil.io/guides/smart-contract/validators/mint-validator#see-examples) See Examples [Mint with Smart Contract](https://dev.ada-anvil.io/guides/smart-contract/mint-smart-contract) Last updated 2 months ago Was this helpful? ---