Mesh LogoMesh

Swap

Create peer-to-peer asset swaps with specified exchange terms

The Swap contract enables peer-to-peer asset exchanges. You specify what you want to give and what you want to receive, then lock your assets in the contract. Anyone who provides the requested assets can complete the swap. You can cancel anytime before someone accepts.

Use Cases

  • Peer-to-peer token trading
  • OTC (over-the-counter) deals
  • NFT-for-token swaps
  • Multi-asset exchanges
  • Trustless trading without intermediaries

Quick Start

Install the Package

npm install @meshsdk/contract @meshsdk/core

Initialize the Contract

import { MeshSwapContract } from "@meshsdk/contract";
import { BlockfrostProvider, MeshTxBuilder } from "@meshsdk/core";

const provider = new BlockfrostProvider("<Your-API-Key>");

const meshTxBuilder = new MeshTxBuilder({
  fetcher: provider,
  submitter: provider,
});

const contract = new MeshSwapContract({
  mesh: meshTxBuilder,
  fetcher: provider,
  wallet: wallet,
  networkId: 0,
});

Contract Logic

The Swap contract works as a simple order book entry:

StateDescription
OpenAssets locked, waiting for counterparty
CompletedCounterparty accepted, assets swapped
CancelledInitiator retrieved assets

Swap Flow

Initiator                    Contract                    Acceptor
    |                           |                           |
    |--- initiateSwap --------->|                           |
    |    (lock assets)          |                           |
    |                           |                           |
    |                           |<------- acceptSwap -------|
    |                           |    (provide requested)    |
    |                           |                           |
    |<-- receive requested -----|---- send locked --------->|

Validation Rules

  1. Initiate: Lock specified assets in contract with requested assets in datum
  2. Accept: Counterparty must provide exactly the requested assets
  3. Cancel: Only the initiator can cancel and retrieve assets

Available Actions

Initiate Swap

Create a new swap by specifying what you offer and what you want in return.

Method Signature

contract.initiateSwap(toProvide: Asset[], toReceive: Asset[]): Promise<string>

Parameters

ParameterTypeDescription
toProvideAsset[]Assets you are offering
toReceiveAsset[]Assets you want in exchange

Code Example

import { MeshSwapContract } from "@meshsdk/contract";
import { BlockfrostProvider, MeshTxBuilder, Asset } from "@meshsdk/core";

// Initialize provider and contract
const provider = new BlockfrostProvider("<Your-API-Key>");

const meshTxBuilder = new MeshTxBuilder({
  fetcher: provider,
  submitter: provider,
});

const contract = new MeshSwapContract({
  mesh: meshTxBuilder,
  fetcher: provider,
  wallet: wallet,
  networkId: 0,
});

// Define what you're offering (10 ADA)
const toProvide: Asset[] = [
  {
    unit: "lovelace",
    quantity: "10000000",
  },
];

// Define what you want (1 specific NFT)
const toReceive: Asset[] = [
  {
    unit: "d9312da562da182b02322fd8acb536f37eb9d29fba7c49dc172555274d657368546f6b656e",
    quantity: "1",
  },
];

// Initiate the swap
const tx = await contract.initiateSwap(toProvide, toReceive);
const signedTx = await wallet.signTx(tx);
const txHash = await wallet.submitTx(signedTx);

console.log("Swap initiated. Transaction hash:", txHash);
// Share this txHash with potential counterparties

What Happens on Success

  • Your offered assets transfer to the contract
  • Swap terms are stored in the datum
  • Anyone with the requested assets can accept
  • You can cancel anytime before acceptance

Accept Swap

Accept an open swap by providing the requested assets.

Method Signature

contract.acceptSwap(swapUtxo: UTxO): Promise<string>

Parameters

ParameterTypeDescription
swapUtxoUTxOThe UTxO containing the swap offer

Code Example

import { MeshSwapContract } from "@meshsdk/contract";
import { BlockfrostProvider, MeshTxBuilder } from "@meshsdk/core";

// Initialize provider and contract (acceptor's wallet)
const provider = new BlockfrostProvider("<Your-API-Key>");

const meshTxBuilder = new MeshTxBuilder({
  fetcher: provider,
  submitter: provider,
});

const contract = new MeshSwapContract({
  mesh: meshTxBuilder,
  fetcher: provider,
  wallet: acceptorWallet,
  networkId: 0,
});

// Get the swap UTxO from the initiation transaction
const swapUtxo = await contract.getUtxoByTxHash("<initiate-transaction-hash>");

// Accept the swap (must have the requested assets in wallet)
const tx = await contract.acceptSwap(swapUtxo);
const signedTx = await acceptorWallet.signTx(tx);
const txHash = await acceptorWallet.submitTx(signedTx);

console.log("Swap accepted. Transaction hash:", txHash);

What Happens on Success

  • Acceptor receives the initiator's offered assets
  • Initiator receives the requested assets
  • Swap UTxO is consumed
  • View example: Successful swap on Cardanoscan

Cancel Swap

Cancel an open swap and retrieve your assets.

Method Signature

contract.cancelSwap(swapUtxo: UTxO): Promise<string>

Parameters

ParameterTypeDescription
swapUtxoUTxOThe UTxO containing your swap offer

Code Example

import { MeshSwapContract } from "@meshsdk/contract";
import { BlockfrostProvider, MeshTxBuilder } from "@meshsdk/core";

// Initialize provider and contract (initiator's wallet)
const provider = new BlockfrostProvider("<Your-API-Key>");

const meshTxBuilder = new MeshTxBuilder({
  fetcher: provider,
  submitter: provider,
});

const contract = new MeshSwapContract({
  mesh: meshTxBuilder,
  fetcher: provider,
  wallet: initiatorWallet,
  networkId: 0,
});

// Get your swap UTxO
const swapUtxo = await contract.getUtxoByTxHash("<initiate-transaction-hash>");

// Cancel the swap
const tx = await contract.cancelSwap(swapUtxo);
const signedTx = await initiatorWallet.signTx(tx);
const txHash = await initiatorWallet.submitTx(signedTx);

console.log("Swap cancelled. Transaction hash:", txHash);

What Happens on Success

  • Your offered assets return to your wallet
  • Swap UTxO is consumed
  • No further action possible on this swap

Full Working Example

import { MeshSwapContract } from "@meshsdk/contract";
import { BlockfrostProvider, MeshTxBuilder, Asset } from "@meshsdk/core";
import { MeshCardanoBrowserWallet } from "@meshsdk/wallet";

async function swapDemo() {
  const provider = new BlockfrostProvider("<Your-API-Key>");

  // Party A wants to trade 10 ADA for an NFT
  const partyAWallet = await MeshCardanoBrowserWallet.enable("eternl");

  const contractA = new MeshSwapContract({
    mesh: new MeshTxBuilder({ fetcher: provider, submitter: provider }),
    fetcher: provider,
    wallet: partyAWallet,
    networkId: 0,
  });

  // Step 1: Party A initiates swap
  const toProvide: Asset[] = [
    { unit: "lovelace", quantity: "10000000" }, // 10 ADA
  ];

  const toReceive: Asset[] = [
    {
      unit: "d9312da562da182b02322fd8acb536f37eb9d29fba7c49dc172555274d657368546f6b656e",
      quantity: "1",
    },
  ];

  const initTx = await contractA.initiateSwap(toProvide, toReceive);
  const signedInitTx = await partyAWallet.signTx(initTx);
  const initTxHash = await partyAWallet.submitTx(signedInitTx);

  console.log("Swap initiated:", initTxHash);

  // Wait for confirmation
  await new Promise((r) => setTimeout(r, 60000));

  // Step 2: Party B accepts the swap
  const partyBWallet = await MeshCardanoBrowserWallet.enable("nami");

  const contractB = new MeshSwapContract({
    mesh: new MeshTxBuilder({ fetcher: provider, submitter: provider }),
    fetcher: provider,
    wallet: partyBWallet,
    networkId: 0,
  });

  const swapUtxo = await contractB.getUtxoByTxHash(initTxHash);

  const acceptTx = await contractB.acceptSwap(swapUtxo);
  const signedAcceptTx = await partyBWallet.signTx(acceptTx);
  const acceptTxHash = await partyBWallet.submitTx(signedAcceptTx);

  console.log("Swap completed:", acceptTxHash);
}

swapDemo().catch(console.error);

Multi-Asset Swaps

You can swap multiple assets in a single transaction:

// Offering: 5 ADA + 100 tokens
const toProvide: Asset[] = [
  {
    unit: "lovelace",
    quantity: "5000000",
  },
  {
    unit: "abc123...fungibleToken",
    quantity: "100",
  },
];

// Wanting: 2 specific NFTs
const toReceive: Asset[] = [
  {
    unit: "xyz789...nft1",
    quantity: "1",
  },
  {
    unit: "xyz789...nft2",
    quantity: "1",
  },
];

const tx = await contract.initiateSwap(toProvide, toReceive);

Swap Discovery

To find available swaps, query the contract address:

async function findOpenSwaps(contract: MeshSwapContract) {
  const scriptAddress = contract.getScriptAddress();
  const utxos = await provider.fetchAddressUTxOs(scriptAddress);

  return utxos.map((utxo) => ({
    txHash: utxo.input.txHash,
    offered: utxo.output.amount,
    // Parse datum for requested assets
  }));
}

Troubleshooting

Common Errors

ErrorCauseSolution
Script validation failedMissing requested assetsEnsure you have all requested assets
Missing required signerWrong wallet for cancelOnly initiator can cancel
UTxO not foundSwap already accepted or cancelledCheck if swap is still open
Insufficient fundsNot enough assets to acceptVerify you have the exact requested amounts

Debugging Tips

  1. Check swap status: Use getUtxoByTxHash() to verify the swap is still open
  2. Verify asset units: Asset units must match exactly (policy ID + asset name)
  3. Check quantities: You must provide the exact quantities requested
  4. Inspect datum: View the transaction on a block explorer to see requested assets

Asset Unit Format

// Correct format: policyId + assetName (in hex)
const unit = "d9312da562da182b02322fd8acb536f37eb9d29fba7c49dc172555274d657368546f6b656e";
//           |----------- policy ID -----------||-------- asset name (hex) --------|

// For lovelace, use the string "lovelace"
const lovelace = "lovelace";

Comparison: Swap vs Escrow

FeatureSwapEscrow
Parties1 initiator, any acceptor2 specific parties
TermsFixed at initiationAgreed by both parties
CompletionSingle signature (acceptor)Multi-signature
Use CasePublic offersPrivate agreements

On this page