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/coreInitialize 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:
| State | Description |
|---|---|
| Open | Assets locked, waiting for counterparty |
| Completed | Counterparty accepted, assets swapped |
| Cancelled | Initiator retrieved assets |
Swap Flow
Initiator Contract Acceptor
| | |
|--- initiateSwap --------->| |
| (lock assets) | |
| | |
| |<------- acceptSwap -------|
| | (provide requested) |
| | |
|<-- receive requested -----|---- send locked --------->|Validation Rules
- Initiate: Lock specified assets in contract with requested assets in datum
- Accept: Counterparty must provide exactly the requested assets
- 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
| Parameter | Type | Description |
|---|---|---|
toProvide | Asset[] | Assets you are offering |
toReceive | Asset[] | 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 counterpartiesWhat 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
| Parameter | Type | Description |
|---|---|---|
swapUtxo | UTxO | The 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
| Parameter | Type | Description |
|---|---|---|
swapUtxo | UTxO | The 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
| Error | Cause | Solution |
|---|---|---|
Script validation failed | Missing requested assets | Ensure you have all requested assets |
Missing required signer | Wrong wallet for cancel | Only initiator can cancel |
UTxO not found | Swap already accepted or cancelled | Check if swap is still open |
Insufficient funds | Not enough assets to accept | Verify you have the exact requested amounts |
Debugging Tips
- Check swap status: Use
getUtxoByTxHash()to verify the swap is still open - Verify asset units: Asset units must match exactly (policy ID + asset name)
- Check quantities: You must provide the exact quantities requested
- 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
| Feature | Swap | Escrow |
|---|---|---|
| Parties | 1 initiator, any acceptor | 2 specific parties |
| Terms | Fixed at initiation | Agreed by both parties |
| Completion | Single signature (acceptor) | Multi-signature |
| Use Case | Public offers | Private agreements |
Related Links
- Source Code on GitHub
- Successful Swap Example
- Escrow Contract - For private two-party agreements
- MeshTxBuilder Documentation
- Wallet Integration Guide