Cardano Transaction Sponsorship
Sponsor Cardano transactions to pay network fees on behalf of your users. This guide walks you through setup, implementation, and common patterns.
Prerequisites
- A UTXOS account with a project created
- Node.js 18+ and npm/bun installed
- A Cardano provider (e.g., Blockfrost)
Quick Start
Create a sponsorship
- Log in to the UTXOS Dashboard
- Navigate to Sponsorships and create a new sponsorship
- Configure your sponsorship settings
- Copy the Sponsorship ID for use in your code
Fund the sponsorship wallet
- Find your sponsorship wallet address in the dashboard
- Send ADA to this address from your funding wallet
- Wait for confirmation before proceeding
Start with a small amount for testing. You can add more funds at any time.
Install the SDK
npm install @utxos/sdk @meshsdk/providerInitialize the SDK
import { Web3Sdk } from "@utxos/sdk";
import { BlockfrostProvider } from "@meshsdk/provider";
const provider = new BlockfrostProvider(
process.env.BLOCKFROST_API_KEY_PREPROD
);
const sdk = new Web3Sdk({
projectId: process.env.NEXT_PUBLIC_UTXOS_PROJECT_ID,
apiKey: process.env.UTXOS_API_KEY,
network: "testnet", // or "mainnet"
privateKey: process.env.UTXOS_PRIVATE_KEY,
fetcher: provider,
submitter: provider,
});Never expose your private key in client-side code. Run all sponsorship logic on your server.
Build the transaction
Use the static sponsorship info as placeholders:
import { MeshTxBuilder } from "@meshsdk/core";
// Get placeholder inputs
const staticInfo = sdk.sponsorship.getStaticInfo();
// Build your transaction
const txBuilder = new MeshTxBuilder({ fetcher: provider });
txBuilder
// Your business logic here
.txOut(recipientAddress, [{ unit: "lovelace", quantity: "2000000" }])
// Required: Set change address from static info
.changeAddress(staticInfo.changeAddress)
// Required: Add static UTXO as input
.txIn(
staticInfo.utxo.input.txHash,
staticInfo.utxo.input.outputIndex,
staticInfo.utxo.output.amount,
staticInfo.utxo.output.address,
0
)
// Optional: Add collateral for script transactions
.txInCollateral(
staticInfo.collateral.input.txHash,
staticInfo.collateral.input.outputIndex,
staticInfo.collateral.output.amount,
staticInfo.collateral.output.address
);
const unsignedTx = await txBuilder.complete();Sponsor and submit
// Request sponsorship
const sponsorResult = await sdk.sponsorship.sponsorTx({
sponsorshipId: "your_sponsorship_id",
tx: unsignedTx,
});
if (!sponsorResult.success) {
throw new Error(sponsorResult.error);
}
// User signs the sponsored transaction
const signedTx = await userWallet.cardano.signTx(sponsorResult.data, true);
// Submit to the network
const txHash = await provider.submitTx(signedTx);
console.log(`Transaction submitted: ${txHash}`);Complete Example: Sponsored NFT Mint
This example demonstrates minting an NFT where the developer pays all fees:
import { Web3Sdk } from "@utxos/sdk";
import { BlockfrostProvider, MeshTxBuilder, ForgeScript, resolveScriptHash, stringToHex } from "@meshsdk/core";
// Initialize provider and SDK
const provider = new BlockfrostProvider(process.env.BLOCKFROST_API_KEY_PREPROD);
const sdk = new Web3Sdk({
projectId: process.env.NEXT_PUBLIC_UTXOS_PROJECT_ID,
apiKey: process.env.UTXOS_API_KEY,
network: "testnet",
privateKey: process.env.UTXOS_PRIVATE_KEY,
fetcher: provider,
submitter: provider,
});
async function mintSponsoredNft(userWallet: any, metadata: any) {
// Get user's address for the NFT
const userAddress = await userWallet.cardano.getChangeAddress();
// Create minting policy
const forgingScript = ForgeScript.withOneSignature(userAddress);
const policyId = resolveScriptHash(forgingScript);
const tokenName = "MyNFT";
const tokenNameHex = stringToHex(tokenName);
// Get static sponsorship info
const staticInfo = sdk.sponsorship.getStaticInfo();
// Build the minting transaction
const txBuilder = new MeshTxBuilder({ fetcher: provider });
txBuilder
.mint("1", policyId, tokenNameHex)
.mintingScript(forgingScript)
.metadataValue(721, { [policyId]: { [tokenName]: metadata } })
.txOut(userAddress, [{ unit: policyId + tokenNameHex, quantity: "1" }])
.changeAddress(staticInfo.changeAddress)
.txIn(
staticInfo.utxo.input.txHash,
staticInfo.utxo.input.outputIndex,
staticInfo.utxo.output.amount,
staticInfo.utxo.output.address,
0
)
.txInCollateral(
staticInfo.collateral.input.txHash,
staticInfo.collateral.input.outputIndex,
staticInfo.collateral.output.amount,
staticInfo.collateral.output.address
);
const unsignedTx = await txBuilder.complete();
// Sponsor the transaction
const sponsorResult = await sdk.sponsorship.sponsorTx({
sponsorshipId: process.env.SPONSORSHIP_ID,
tx: unsignedTx,
});
if (!sponsorResult.success) {
throw new Error(`Sponsorship failed: ${sponsorResult.error}`);
}
// User signs and submits
const signedTx = await userWallet.cardano.signTx(sponsorResult.data, true);
const txHash = await provider.submitTx(signedTx);
return { txHash, policyId, tokenName };
}What configuration options are available?
Configure these settings in the UTXOS dashboard for each sponsorship:
| Option | Description | Recommended Value |
|---|---|---|
numUtxosTriggerPrepare | UTXO count that triggers automatic pool replenishment | 5 |
numUtxosPrepare | Number of UTXOs to create during preparation | 10 |
utxoAmount | ADA amount for each sponsorship UTXO | 5 ADA (standard) or 99 ADA (batch) |
Choosing UTXO Amount
// Standard transactions (most use cases)
const staticInfo = sdk.sponsorship.getStaticInfo("5");
// High-fee transactions or batch operations
const staticInfo = sdk.sponsorship.getStaticInfo("99");Environment Variables
Add these to your .env file:
NEXT_PUBLIC_UTXOS_PROJECT_ID=your_project_id
UTXOS_API_KEY=your_api_key
UTXOS_PRIVATE_KEY=your_entity_secret_private_key
BLOCKFROST_API_KEY_PREPROD=your_blockfrost_key
SPONSORSHIP_ID=your_sponsorship_idTroubleshooting
”Insufficient UTXOs” error
Cause: The sponsorship wallet UTXO pool is depleted.
Solutions:
- Wait a few seconds and retry. The SDK auto-chains a preparation transaction.
- Add more funds to the sponsorship wallet.
- Increase
numUtxosPreparein your configuration.
”Invalid CBOR” error
Cause: The transaction format is incorrect or missing required fields.
Solutions:
- Verify you included
staticInfo.changeAddressas the change address - Verify you added
staticInfo.utxoas a transaction input - Check that your transaction builder produces valid CBOR
”Sponsorship not found” error
Cause: The sponsorship ID is incorrect or the sponsorship was deleted.
Solutions:
- Copy the sponsorship ID directly from the dashboard
- Verify the sponsorship exists and is active
Transaction stuck in mempool
Cause: Network congestion or fee too low.
Solutions:
- Wait for the transaction to confirm or expire
- If using custom fee calculation, increase the fee estimate
- Check the Cardano network status for congestion
User wallet signature fails
Cause: The user wallet cannot sign the sponsored transaction.
Solutions:
- Ensure you pass
trueas the second argument tosignTx()for partial signing - Verify the user has the required keys for their inputs
- Check that the wallet is connected and unlocked
Best Practices
Always implement server-side rate limiting to prevent sponsorship abuse.
- Validate user requests before sponsoring to prevent spam
- Set per-user limits to control costs (e.g., 10 sponsored transactions per day)
- Monitor your sponsorship wallet balance and set up alerts for low funds
- Log all sponsored transactions for auditing and debugging
- Handle errors gracefully and provide clear feedback to users
Next Steps
- Review the How It Works guide for deeper technical understanding
- Explore Developer-Controlled Wallets for backend automation
- Check the Security Best Practices for production deployments