🔧 ProgrammierungGitHub Release: dependabot/dependabot-core v0.395.0 (07.09.2026)(07.09.2026 um 15:21 Uhr)
🔧 ProgrammierungGitHub Release: dependabot/dependabot-core v0.396.0 (14.09.2026)(14.09.2026 um 19:05 Uhr)
🔧 ProgrammierungGitHub Release: langwatch/scenario vpython/v1.4.0 (02.09.2026)(02.09.2026 um 04:47 Uhr)
🔧 ProgrammierungGitHub Release: langwatch/scenario vjavascript/v1.5.0 (02.09.2026)(02.09.2026 um 04:47 Uhr)
🔧 ProgrammierungGitHub Release: langwatch/scenario vjavascript/v1.6.0 (06.09.2026)(06.09.2026 um 17:45 Uhr)
🔧 Programmierungclawpatrol v0.5.10(13.09.2026 um 02:54 Uhr)
⚠️ Malware / Trojaner / VirenCAPE-parsers v0.1.69(13.09.2026 um 04:14 Uhr)
⚠️ Malware / Trojaner / Virendarknet-mcp-server(13.09.2026 um 04:55 Uhr)
🐧 Linux Tippsazurelinux v3.0.20260909-3.0(13.09.2026 um 09:51 Uhr)
🕵️ Sicherheitslückenatomicvulns(13.09.2026 um 10:36 Uhr)
🔧 ProgrammierungGitHub Release: dependabot/dependabot-core v0.395.0 (07.09.2026)(07.09.2026 um 15:21 Uhr)
🔧 ProgrammierungGitHub Release: dependabot/dependabot-core v0.396.0 (14.09.2026)(14.09.2026 um 19:05 Uhr)
🔧 ProgrammierungGitHub Release: langwatch/scenario vpython/v1.4.0 (02.09.2026)(02.09.2026 um 04:47 Uhr)
🔧 ProgrammierungGitHub Release: langwatch/scenario vjavascript/v1.5.0 (02.09.2026)(02.09.2026 um 04:47 Uhr)
🔧 ProgrammierungGitHub Release: langwatch/scenario vjavascript/v1.6.0 (06.09.2026)(06.09.2026 um 17:45 Uhr)
🔧 Programmierungclawpatrol v0.5.10(13.09.2026 um 02:54 Uhr)
⚠️ Malware / Trojaner / VirenCAPE-parsers v0.1.69(13.09.2026 um 04:14 Uhr)
⚠️ Malware / Trojaner / Virendarknet-mcp-server(13.09.2026 um 04:55 Uhr)
🐧 Linux Tippsazurelinux v3.0.20260909-3.0(13.09.2026 um 09:51 Uhr)
🕵️ Sicherheitslückenatomicvulns(13.09.2026 um 10:36 Uhr)

🔧 Programmierung 🕛 vor 3 Monaten 11 Min Lesezeit
0

DUST Sponsorship on Midnight: How One Wallet Pays Fees for Another User's Transaction

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht

DUST sponsorship on Midnight lets a backend wallet pay transaction fees for users who do not have DUST yet. Every transaction on Midnight consumes DUST, a shielded, non-transferable capacity resource generated by holding NIGHT tokens. That design keeps fees predictable and privacy intact, but it creates onboarding friction: a brand-new wallet holds zero DUST. Without DUST, it cannot submit a single transaction, including the first one a user might want to make in your DApp.



DUST cannot be sent from one wallet to another. You cannot airdrop it, and there is no transferDUST() call.

The only way to cover fees for a wallet that has no DUST is through sponsorship. In this flow, a backend wallet with healthy DUST reserves pays the fees on behalf of the user without changing who signed the transaction or who owns its outputs.



Target audience: Developers building DApps on the Midnight network.



Prerequisites:




  • Familiarity with TypeScript

  • Basic understanding of the Midnight wallet SDK

  • A Midnight wallet setup with access to a proof server



Related reading: ·





Step 1: The user balances their own tokens without DUST



The user calls balanceUnboundTransaction and explicitly excludes 'dust' from the tokenKindsToBalance array. This tells the SDK to settle shielded and unshielded inputs/outputs but leave the DUST portion open for someone else to fill.




CODE
import { WalletFacade } from '@midnight-ntwrk/wallet-sdk';

// userWallet is a WalletFacade instance, already synced
const transaction = await userWallet.transact(contractCall);

const userRecipe = await userWallet.balanceUnboundTransaction(
transaction,
{
shieldedSecretKeys: userShieldedKeys,
dustSecretKey: userDustKey,
},
{
ttl: new Date(Date.now() + 30 * 60 * 1000), // 30-minute TTL
tokenKindsToBalance: ['shielded', 'unshielded'], // 'dust' is deliberately omitted
}
);

// Sign and finalize the user's portion
const userSigned = await userWallet.signRecipe(
userRecipe,
(payload) => userKeystore.signData(payload)
);
const userFinalized = await userWallet.finalizeRecipe(userSigned);

// Send userFinalized to the sponsor service
const response = await fetch('https://your-sponsor-service/sponsor', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userFinalized }),
});

const { txHash } = await response.json();
console.log(`Transaction confirmed: ${txHash}`);






The tokenKindsToBalance parameter is the key piece here. By leaving 'dust' out, the user is saying: "I'm settling my own tokens, but I'm not covering the fee. The sponsor will." The finalized transaction is complete from the user's perspective but is not yet submittable.






Step 2: The sponsor adds DUST fees



The sponsor service receives userFinalized and calls balanceFinalizedTransaction with tokenKindsToBalance: ['dust']. This time only DUST is balanced. The sponsor is not touching the user's shielded or unshielded tokens.




CODE
// sponsorWallet is a WalletFacade instance with healthy DUST reserves
const sponsorRecipe = await sponsorWallet.balanceFinalizedTransaction(
userFinalized,
{
shieldedSecretKeys: sponsorShieldedKeys,
dustSecretKey: sponsorDustKey,
},
{
ttl: new Date(Date.now() + 30 * 60 * 1000),
tokenKindsToBalance: ['dust'], // only DUST
}
);

const sponsorSigned = await sponsorWallet.signRecipe(
sponsorRecipe,
(payload) => sponsorKeystore.signData(payload)
);
const sponsorFinalized = await sponsorWallet.finalizeRecipe(sponsorSigned);









Step 3: The sponsor submits






CODE
const txHash = await sponsorWallet.submitTransaction(sponsorFinalized);
console.log(`Submitted: ${txHash}`);






The transaction is now fully balanced. The user's tokens are accounted for, the sponsor's DUST covers the fee, and the combined transaction hits the network.









Full sponsor service implementation



Here is a complete Express-based sponsor service you can adapt for production.




CODE
// sponsor-service.ts
import express from 'express';
import { WalletFacade } from '@midnight-ntwrk/wallet-sdk';
import type { FinalizedTransaction } from '@midnight-ntwrk/wallet-api';

const app = express();
app.use(express.json({ limit: '1mb' }));

// Sponsor wallet state

let sponsorWallet: WalletFacade;
let sponsorShieldedKeys: { secretKey: Uint8Array };
let sponsorDustKey: { secretKey: Uint8Array };
let sponsorKeystore: { signData: (payload: Uint8Array) => Promise<Uint8Array> };

async function initSponsorWallet(): Promise<void> {
const seedPhrase = process.env.SPONSOR_SEED_PHRASE;
if (!seedPhrase) throw new Error('SPONSOR_SEED_PHRASE is required');

// Initialize the wallet facade with Preview Testnet config
sponsorWallet = await WalletFacade.create({
seedPhrase,
networkId: process.env.NETWORK_ID ?? '0',
rpcUrl: process.env.RPC_URL ?? 'https://rpc.midnight.network',
indexerUrl: process.env.INDEXER_URL ?? 'https://indexer.midnight.network',
proofServerUrl: process.env.PROOF_SERVER_URL ?? 'http://localhost:6300',
});

// Wait until the wallet has synced to the chain tip
await sponsorWallet.waitForSync();

// Derive keys. These come from your wallet initialization flow.
sponsorShieldedKeys = { secretKey: sponsorWallet.shieldedSecretKey };
sponsorDustKey = { secretKey: sponsorWallet.dustSecretKey };
sponsorKeystore = {
signData: (payload) => sponsorWallet.signWithUnshieldedKey(payload),
};

const state = await sponsorWallet.getState();
console.log(`Sponsor wallet ready. DUST balance: ${state.dust.available}`);
}

// DUST monitoring

const DUST_LOW_WATERMARK = 100n; // alert threshold in DUST units

async function checkDustLevel(): Promise<void> {
const state = await sponsorWallet.getState();
const available = state.dust.available;

if (available < DUST_LOW_WATERMARK) {
console.warn(
`Sponsor DUST low: ${available}. ` +
`Add NIGHT to the sponsor wallet to regenerate.`
);
// In production: trigger a PagerDuty alert, Slack message, etc.
}
}

// Sponsorship endpoint

app.post('/sponsor', async (req, res) => {
const { userFinalized } = req.body as { userFinalized: FinalizedTransaction };

if (!userFinalized) {
return res.status(400).json({ error: 'userFinalized is required' });
}

try {
await checkDustLevel();

// Balance only the DUST portion. The user already balanced everything else.
const sponsorRecipe = await sponsorWallet.balanceFinalizedTransaction(
userFinalized,
{
shieldedSecretKeys: sponsorShieldedKeys,
dustSecretKey: sponsorDustKey,
},
{
ttl: new Date(Date.now() + 30 * 60 * 1000),
tokenKindsToBalance: ['dust'],
}
);

const sponsorSigned = await sponsorWallet.signRecipe(
sponsorRecipe,
(payload) => sponsorKeystore.signData(payload)
);
const sponsorFinalized = await sponsorWallet.finalizeRecipe(sponsorSigned);

// Submit the fully balanced transaction
const txHash = await sponsorWallet.submitTransaction(sponsorFinalized);

res.json({ success: true, txHash });
} catch (error) {
console.error('Sponsorship failed:', error);
res.status(500).json({ success: false, error: String(error) });
}
});

// Health check

app.get('/health', async (_req, res) => {
const state = await sponsorWallet.getState();
res.json({ dust: String(state.dust.available), synced: state.isSynced });
});

// Startup

initSponsorWallet()
.then(() => {
app.listen(3001, () =>
console.log('Sponsor service running on port 3001')
);
})
.catch((err) => {
console.error('Failed to initialize sponsor wallet:', err);
process.exit(1);
});






Environment variables:




CODE
export SPONSOR_SEED_PHRASE="your twelve word seed phrase here"
export NETWORK_ID="0" # Preview Testnet
export RPC_URL="https://rpc.midnight.network"
export INDEXER_URL="https://indexer.midnight.network"
export PROOF_SERVER_URL="http://localhost:6300"






Install dependencies:




CODE
npm install @midnight-ntwrk/wallet-sdk @midnight-ntwrk/wallet-api express
npm install --save-dev @types/express tsx typescript






Run the service:




CODE
npx tsx src/sponsor-service.ts












ownPublicKey() in sponsored transactions



One question comes up consistently when developers first work with sponsored transactions: whose key does ownPublicKey() return when the sponsor wallet is doing the balancing?



The answer is the prover's key, always. This is by design.



ownPublicKey() reflects whoever generated the ZK proof for the transaction, not whoever paid the DUST fee. In a standard sponsored flow, the user proves their own transaction before sending it to the sponsor, so ownPublicKey() returns the user's coinPublicKey. The sponsor's identity never leaks into the transaction's cryptographic identity.



This matters for three reasons:




  • Address derivation: outputs from the transaction are owned by the prover's address, not the sponsor's.

  • UTXO ownership: the wallet that can later spend those outputs is the prover's wallet.

  • Balance queries: if you are tracking state after the transaction, query the prover's address.



Some advanced architectures use an explicit key override pattern, where a separate prover wallet generates ZK proofs and the sponsor handles only fee payment and submission. In that configuration, the override is active at balance time:




CODE
// With key override active on the sponsor wallet:
sponsorWallet.ownPublicKey();
// → returns overrideKeys.coinPublicKey (the prover's key)
// → NOT the sponsor wallet's own coinPublicKey






The key override is useful when you have a dedicated proof-generation service that is separate from your fee-paying backend. The prover wallet handles cryptographic identity and proof generation; the sponsor wallet handles DUST and submission. ownPublicKey() always reflects the prover, regardless of which wallet calls it.









DUST regeneration vs depletion



Understanding the regeneration curve is essential for sizing your sponsor wallet correctly. Once you know the math, operating a sponsor service becomes straightforward.






The regeneration curve



to understand how DUST generation, decay, and the grace period work.

  • Use the or Discord if you hit implementation issues.

  • Vollständiger Original-Bericht
    Ausführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
    ↗ Original-Artikel auf dev.to lesen
    Wie bewertest du diesen Beitrag?
    1 Klick Feedback
    Teilen mit Netzwerk & Team:

    Community-Analysen & Experten-Meinungen 0

    Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
    Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
    Community Pulse: Relevanz-Einschätzung
    1 Klick Experten-Votum
    🔴 Akute Relevanz 0%
    🟡 In Evaluierung 0%
    🟢 Keine Auswirkung 0%
    Spannende Innovation 0%
    Verwandte Story-Cluster & Quellen (Vektor-KI)
    Port 8095 Engine
    10 Quellen
    GitHub Release: dependabot/dependabot-core v0.393.0 (24.08.2026)
    1 Quelle
    clawpatrol v0.5.10
    1 Quelle
    CAPE-parsers v0.1.69
    Ähnliche Beiträge
    🔍 Verwandte News

    Auch interessante Nachrichten DUST Sponsorship on Midnight: How One Wallet Pays Fees for Another User's Transaction

    Thematisch verwandte Begriffe: DUST, Sponsorship, Midnight, Wallet · 6 Treffer

    Laden...

    Videos werden geladen ...

    Laden...

    Beiträge werden geladen ...

    Laden...

    Videos werden geladen ...

    Laden...

    Beiträge werden geladen ...

    Laden...

    Videos werden geladen ...

    Laden...

    Beiträge werden geladen ...

    Laden...

    Videos werden geladen ...

    Laden...

    Beiträge werden geladen ...

    Laden...

    Videos werden geladen ...