Transaction Sponsorship Usage
Transaction sponsorship allows you to pay for users’ transaction fees, improving user experience by removing the need for users to hold native tokens for gas fees.
Prerequisites
Before you begin, ensure you have:
- A UTXOS project with API access
- Your project ID and API key from the dashboard
- A Developer Controlled Wallet with sufficient funds for sponsoring transactions
Create A Sponsorship
- Navigate to the UTXOS dashboard and log into your account
- Create a new sponsorship campaign
- Configure your sponsorship settings and policies
- Copy the
Sponsorship ID
from the sponsorship settings for use in your code
Initialize The SDK
Initialize the Web3Sdk
with your project credentials and network configuration:
import { Web3Sdk } from "@utxos/web3-sdk";
const sdk = new Web3Sdk({
projectId: process.env.NEXT_PUBLIC_UTXOS_PROJECT_ID, // https://utxos.dev/dashboard
apiKey: process.env.UTXOS_API_KEY,
network: "preprod",
privateKey: process.env.UTXOS_PRIVATE_KEY,
fetcher: provider,
submitter: provider,
});
Never expose your private key in client-side code. Use environment variables or secure key management in production.
Build The Transaction
The SDK provides static UTXO information that must be included in your transaction:
// Get the required static information
const staticInfo = sdk.sponsorship.getStaticInfo();
// Build your transaction with the required fields
const txBuilder = your_preferred_transaction_builder
.
.
// Required: Set the change address from static info
.changeAddress(staticInfo.changeAddress)
// Required: Add the static UTXO as an input
.txIn(
staticInfo.utxo.input.txHash,
staticInfo.utxo.input.outputIndex,
staticInfo.utxo.output.amount,
staticInfo.utxo.output.address,
0,
)
// If required: Add the static UTXO as collateral
.txInCollateral(
staticInfo.collateral.input.txHash,
staticInfo.collateral.input.outputIndex,
staticInfo.collateral.output.amount,
staticInfo.collateral.output.address,
);
const transaction = txBuilder.complete();
If you prefer to configure manually, use these static values for the preprod network:
const universalStaticUtxo = {
input: {
outputIndex: 0,
txHash: "5a1edf7da58eff2059030abd456947a96cb2d16b9d8c3822ffff58d167ed8bfc",
},
output: {
address:
"addr_test1qrsj3xj6q99m4g9tu9mm2lzzdafy04035eya7hjhpus55r204nlu6dmhgpruq7df228h9gpujt0mtnfcnkcaj3wj457q5zv6kz",
amount: [{ unit: "lovelace", quantity: "5000000" }],
},
};
const universalStaticChangeAddress =
"addr_test1qrsj3xj6q99m4g9tu9mm2lzzdafy04035eya7hjhpus55r204nlu6dmhgpruq7df228h9gpujt0mtnfcnkcaj3wj457q5zv6kz";
These are the static UTXOs available for the preprod network, it 5 ADA and 99 ADA respectively:
{
"5": {
input: {
outputIndex: 0,
txHash:
"5a1edf7da58eff2059030abd456947a96cb2d16b9d8c3822ffff58d167ed8bfc",
},
output: {
address:
"addr_test1qrsj3xj6q99m4g9tu9mm2lzzdafy04035eya7hjhpus55r204nlu6dmhgpruq7df228h9gpujt0mtnfcnkcaj3wj457q5zv6kz",
amount: [
{
unit: "lovelace",
quantity: "5000000",
},
],
},
},
"99": {
input: {
outputIndex: 0,
txHash:
"8222b0327a95e8c357016a5df64d93d7cf8a585a07c55327ae618a7e00d58d9e",
},
output: {
address:
"addr_test1qrsj3xj6q99m4g9tu9mm2lzzdafy04035eya7hjhpus55r204nlu6dmhgpruq7df228h9gpujt0mtnfcnkcaj3wj457q5zv6kz",
amount: [
{
unit: "lovelace",
quantity: "99000000",
},
],
},
},
}
Sponsor The Transaction
Once your transaction is built, sponsor it and submit to the network:
try {
// Step 1: Request sponsorship for your transaction
const sponsoredTx = await sdk.sponsorship.sponsorTx({
sponsorshipId: "your_sponsorship_id", // Replace with your actual sponsorship ID
tx: transaction, // Your built transaction
});
// Step 2: User signs the sponsored transaction
const signedTx = await userWallet.signTx(sponsoredTx, true);
// Step 3: Submit the signed transaction to the network
const txHash = await provider.submitTx(signedTx);
console.log(`Transaction submitted successfully! Hash: ${txHash}`);
} catch (error) {
console.error("Sponsorship failed:", error);
// Handle sponsorship errors (insufficient funds, policy violations, etc.)
}
Complete Example
Here’s a complete working example:
const provider = BlockfrostProvider("...");
const sponsorshipId = "your_sponsorship_id"; // Replace with your actual sponsorship ID
const sdk = new Web3Sdk({
projectId: "11111111-2222-3333-YOUR-PROJECTID", // Your project ID from dashboard
apiKey: "YOUR_API_KEY", // Your API key from dashboard
network: "testnet", // Use "testnet" for testing, "mainnet" for production
privateKey: "YOUR_PRIVATE_KEY", // Developer's wallet private key (keep secure!)
fetcher: provider, // Your Cardano provider for fetching data
submitter: provider, // Your Cardano provider for submitting transactions
});
async function developerCreateTx() {
/**
* get wallet info
*/
const userWallet = await getUserWallet();
const userAddress = await userWallet.getChangeAddress();
/**
* build transaction
*/
const txBuilder = new MeshTxBuilder({
fetcher: provider,
});
const forgingScript = ForgeScript.withOneSignature(userAddress);
const demoAssetMetadata = {
name: "Mesh Token",
image: "ipfs://QmRzicpReutwCkM6aotuKjErFCUD213DpwPq6ByuzMJaua",
mediaType: "image/jpg",
description: "This NFT was minted by Mesh (https://meshjs.dev/).",
};
const policyId = resolveScriptHash(forgingScript);
const tokenName = "MeshToken";
const tokenNameHex = stringToHex(tokenName);
const metadata = { [policyId]: { [tokenName]: { ...demoAssetMetadata } } };
const staticInfo = sdk.sponsorship.getStaticInfo();
txBuilder
.mint("1", policyId, tokenNameHex)
.mintingScript(forgingScript)
.metadataValue(721, 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();
return unsignedTx;
}
async function runFullDemo() {
const staticInfo = sdk.sponsorship.getStaticInfo();
const tx = await developerCreateTx();
const tx2 = await sdk.sponsorship.sponsorTx({
sponsorshipId: sponsorshipId,
tx: tx,
});
// 3. client sign
const userWallet = await main.getUserWallet();
const signedTx = await userWallet.signTx(tx2, true);
const txHash = await provider.submitTx(signedTx);
console.log("txHash", txHash);
}