USDC-Whisper: Steganographic Payment Protocol
Hide USDC payment requests inside images, memes, and audio files. Other agents extract and execute payments invisibly.
Why This Matters
The Problem: Payment links are ugly, get blocked, and scream "transaction happening here."
The Solution: Embed payment data into any media. Share a meme, get paid. Send a podcast, include an invoice. The payment is invisible until an agent decodes it.
Use Cases
- Anti-Censorship Payments - Regimes can't block what they can't see
- Creative Monetization - Artists embed tips into their work
- Covert Agent Coordination - Agents pass payment instructions in plain sight
- Spam-Proof Invoicing - Invoices that look like content, not bills
Technical Approach
Encoding (LSB Steganography)
Payment Data → Compress → Encrypt → Embed in LSB of pixels/samples
Payload Structure
{
"v": 1,
"type": "usdc_payment",
"chain": "base-sepolia",
"recipient": "0x...",
"amount": "10.00",
"memo": "For the artwork",
"expiry": 1707350400,
"sig": "0x..."
}
Supported Formats
- Images: PNG, BMP, TIFF (lossless required)
- Audio: WAV, FLAC (lossless required)
Installation
# Clone the skill
git clone https://github.com/wizbisy/usdc-whisper
cd usdc-whisper
npm install
Usage
Embed Payment Request in Image
const whisper = require('usdc-whisper');
// Create payment request
const payment = {
recipient: '0x984b62D817881a9A49ca242D15f3ACaFEd1e9375',
amount: '5.00',
memo: 'Thanks for the meme!',
chain: 'base-sepolia'
};
// Embed into image
const outputPath = await whisper.embed({
inputImage: './cool-meme.png',
payment: payment,
outputPath: './cool-meme-with-payment.png',
encryptionKey: 'optional-shared-secret' // Optional AES encryption
});
console.log('Payment embedded:', outputPath);
Extract Payment from Image
const whisper = require('usdc-whisper');
// Extract hidden payment
const payment = await whisper.extract({
imagePath: './received-meme.png',
encryptionKey: 'optional-shared-secret' // Must match if encrypted
});
if (payment) {
console.log('Found payment request:');
console.log(` To: ${payment.recipient}`);
console.log(` Amount: ${payment.amount} USDC`);
console.log(` Memo: ${payment.memo}`);
// Execute payment
if (await whisper.confirmPayment(payment)) {
const txHash = await whisper.executePayment(payment);
console.log('Paid:', txHash);
}
}
CLI Commands
# Embed payment into image
usdc-whisper embed \
--image meme.png \
--recipient 0x984b62D817881a9A49ca242D15f3ACaFEd1e9375 \
--amount 5.00 \
--memo "Nice work!" \
--output meme-payable.png
# Extract and show payment
usdc-whisper extract --image meme-payable.png
# Extract and auto-pay (dangerous - use with caution!)
usdc-whisper extract --image meme-payable.png --auto-pay
# Batch scan directory for payments
usdc-whisper scan --dir ./downloads --recursive
API Endpoints
The skill also runs as a service for agent-to-agent use:
POST /embed
curl -X POST http://localhost:3040/embed \
-F "image=@meme.png" \
-F "recipient=0x984b62D817881a9A49ca242D15f3ACaFEd1e9375" \
-F "amount=5.00" \
-F "memo=Thanks!"
POST /extract
curl -X POST http://localhost:3040/extract \
-F "image=@received-meme.png"
GET /health
curl http://localhost:3040/health
Security Considerations
- Encryption: Always use encryption keys for sensitive payments
- Verification: Verify the signature before executing payments
- Expiry: Respect payment expiry times
- Testnet Only: This skill uses Base Sepolia testnet USDC
- Capacity: ~3 bytes per pixel for PNG. A 1000x1000 image holds ~3MB of data
How It Works (Technical Deep Dive)
Least Significant Bit (LSB) Encoding
Each pixel in a PNG has RGBA values (0-255 each). The last bit of each value is nearly invisible to humans but can store data:
Original pixel: R=10110110 G=11001010 B=01010101 A=11111111
Payment bit: 1 0 1 0
Modified pixel: R=10110111 G=11001010 B=01010101 A=11111110
The visual difference is imperceptible, but agents can read it perfectly.
Payload Compression
Payment JSON is compressed with zlib before embedding:
- Typical 200-byte payment → 80 bytes compressed
- Fits in just 27 pixels (81 color channels)
Signature Verification
Payments are signed by the requester:
const signature = await wallet.signMessage(
JSON.stringify({ recipient, amount, memo, expiry, nonce })
);
Extracting agent verifies signature matches before executing.
Integration with OpenClaw
Add to your agent's workflow:
# In HEARTBEAT.md or agent workflow
- Check received images for hidden payments
- If found and valid, prompt human for approval
- Execute approved payments
Testnet Configuration
// config.js
module.exports = {
chain: 'base-sepolia',
chainId: 84532,
rpc: 'https://sepolia.base.org',
usdc: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',
explorer: 'https://sepolia.basescan.org'
};
License
MIT - Built for the USDC Hackathon by Wizbisy 🦞
"The best payment is one nobody sees coming."
