How Transaction Sponsorship Works
Transaction sponsorship lets your users sign transactions while your developer-controlled wallet covers the network fees. This guide explains the architecture, the end-to-end flow, and how the SDK manages UTXOs automatically.
TL;DR
- Create and fund a developer-controlled wallet that pays fees
- Build your transaction using a “static” placeholder UTXO and change address from the SDK
- Call
sponsorTx()to swap in a real UTXO and apply the developer wallet signature - Have the user sign and submit the final sponsored transaction
- The SDK automatically manages UTXO creation and chains prep transactions when needed
What are the key concepts?
| Concept | Description |
|---|---|
| Developer-controlled wallet | The wallet you fund and control. It pays fees and manages a pool of UTXOs for sponsorship. |
| Static sponsorship info | A fixed template input and change address used while building the transaction. The SDK replaces this with a real UTXO. |
| Sponsorship UTXO | An actual UTXO selected from your wallet pool to replace the static input. |
| Collateral UTXO | A designated UTXO for script collateral requirements (optional). |
| CBOR transaction | The serialized transaction format that the SDK parses and rewrites. |
The SDK keeps your sponsorship UTXO pool healthy automatically. When the pool runs low, it chains a preparatory transaction to create new sponsorship-sized UTXOs without manual intervention.
Step-by-Step Flow
Create and fund the developer-controlled wallet
Create a sponsorship in the UTXOS dashboard. Fund the associated wallet by sending cryptocurrency to its address.
Configure your sponsorship settings to include enough UTXOs for your expected concurrency. For example, if you expect 10 simultaneous sponsorship requests, maintain at least 15-20 UTXOs in the pool.
Build the transaction with static sponsorship info
Use the static info from the SDK as placeholders when building your transaction:
import { Web3Sdk } from "@utxos/sdk";
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,
});
// Get placeholder inputs
const staticInfo = sdk.sponsorship.getStaticInfo();
// Build with your preferred transaction builder
const txBuilder = yourTransactionBuilder
// Add your business logic (mints, outputs, scripts, etc.)
.txOut(recipientAddress, [{ unit: "lovelace", quantity: "1000000" }])
// 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 if your transaction requires it
.txInCollateral(
staticInfo.collateral.input.txHash,
staticInfo.collateral.input.outputIndex,
staticInfo.collateral.output.amount,
staticInfo.collateral.output.address
);
const unsignedTx = await txBuilder.complete();The static inputs and change address are placeholders only. Do not use them for any other purpose. The SDK replaces them with real values during sponsorship.
Request sponsorship
Send the built CBOR transaction to the sponsorship SDK:
const sponsorResult = await sdk.sponsorship.sponsorTx({
sponsorshipId: "your_sponsorship_id",
tx: unsignedTx,
});
if (!sponsorResult.success) {
throw new Error(sponsorResult.error);
}
// sponsorResult.data contains the sponsored CBOR transactionThe SDK performs these operations:
- Verifies UTXO pool availability
- Swaps the static placeholder with a selected real UTXO
- Signs the transaction with the developer-controlled wallet
- Returns the new CBOR transaction ready for user signature
User signs and submits
Have the user sign the sponsored transaction and submit it to the network:
// User adds their signature(s)
const signedTx = await userWallet.cardano.signTx(sponsorResult.data, true);
// Submit to the network
const txHash = await provider.submitTx(signedTx);
console.log(`Transaction submitted: ${txHash}`);What happens under the hood?
UTXO Availability Check
- Fetch all UTXOs from the developer-controlled wallet
- Exclude UTXOs pending in the mempool to prevent double-spends
- The remaining set forms the available pool
Automated UTXO Pool Maintenance
The SDK monitors your UTXO pool and takes action when needed:
- Identify UTXOs that are not the standard sponsorship amount
- Flag sponsorship-sized UTXOs pending longer than the configured threshold
- Build a “prepare UTXO” transaction to consolidate and create fresh UTXOs
- Chain this preparation with the sponsorship request automatically
UTXO Selection
The SDK randomly selects an eligible UTXO from the available pool for each sponsorship request. Random selection distributes load and reduces contention.
Transaction Rewrite
- Parse the provided CBOR transaction
- Remove static placeholder inputs and the static change output
- Add the selected real sponsorship UTXO as input
- Set change to the developer-controlled wallet address
- Return the rewritten CBOR transaction
API Reference
sdk.sponsorship.getStaticInfo(amount?)
Returns placeholder information for building sponsored transactions.
| Parameter | Type | Default | Description |
|---|---|---|---|
amount | "5" | "99" | "5" | Sponsorship amount in ADA. Use "99" for high-fee transactions or batch operations. |
Returns:
{
changeAddress: string;
utxo: {
input: { txHash: string; outputIndex: number };
output: { amount: string | bigint; address: string };
};
collateral: {
input: { txHash: string; outputIndex: number };
output: { amount: string | bigint; address: string };
};
}sdk.sponsorship.sponsorTx(options)
Sponsors a transaction by replacing static placeholders with real UTXOs.
| Parameter | Type | Required | Description |
|---|---|---|---|
sponsorshipId | string | Yes | The sponsorship ID from your dashboard |
tx | string | Yes | CBOR-encoded unsigned transaction |
Returns:
{
success: boolean;
data?: string; // CBOR-encoded sponsored transaction
error?: string; // Error message if success is false
}Error Handling
| Error | Cause | Solution |
|---|---|---|
| Insufficient UTXOs | Pool depleted | The SDK auto-chains a prep transaction. Retry if your policy requires confirmation first. |
| Pending UTXOs | Too many UTXOs in mempool | Increase your UTXO buffer or wait for confirmations. |
| CBOR/Parsing errors | Invalid transaction format | Verify your transaction includes the static input and change address exactly as provided. |
| Timeout errors | Network issues | Implement retries with exponential backoff. |
Security Best Practices
Never expose the developer-controlled wallet private key to client-side code. All sponsorship operations must occur server-side.
- Rate limit sponsorship requests to prevent abuse and UTXO exhaustion
- Monitor your UTXO pool for minimum count, standard size, and pending ratio
- Use dedicated change addresses for simpler accounting and auditing
- Set spending limits per user or per time period in your application logic
Configuration Recommendations
| Setting | Recommendation |
|---|---|
| UTXO count | 2-3x your expected peak concurrent sponsorships |
| UTXO size | Typical fee + 50% buffer (5 ADA covers most transactions) |
| Refresh threshold | Refragment UTXOs pending longer than 10 minutes |
FAQ
Can I use any transaction builder?
Yes. Any builder that can set a change address, add inputs, and produce CBOR output works with sponsorship.
What format does sponsorTx expect?
A CBOR-encoded transaction as a hex string.
Does the SDK always create consolidation transactions?
Only when your available UTXO pool falls below the configured target or contains stale UTXOs that need refreshing.
Who signs what?
The developer-controlled wallet signs to provide the fee input. The user signs their parts (spending inputs, mints, scripts) before submission.
How many confirmations before new sponsorship UTXOs are usable?
Pending UTXOs are excluded until confirmed. Follow your network’s standard confirmation policy.