Documentation Index
Fetch the complete documentation index at: https://docs.skale.space/llms.txt
Use this file to discover all available pages before exploring further.
Accept payments using the MPP (Machine Payments Protocol) SDK on SKALE. MPP enables gasless transactions, encrypted amounts, and confidential token transfers—making it ideal for agentic and privacy-focused applications.
Prerequisites
- Node.js, Bun, or pnpm installed
- A SKALE Chain endpoint (SKALE Base or SKALE Base Sepolia)
- Understanding of MPP protocol
- USDC.e or eUSDC tokens for testing
Overview
The MPP SDK provides multiple payment strategies:
- Standard transfers – Simple token transfers on SKALE Base
- Gasless payments – Users pay without holding gas tokens (EIP-3009/EIP-2612)
- Encrypted amounts – Hide transaction amounts using threshold encryption
- Confidential tokens – Native privacy with eUSDC on chains with Programmable Privacy
Environment Setup
Create a .env file:
# SKALE Chain RPC
SKALE_RPC=https://skale-base.skalenodes.com/v1/base
# Your receiving address
RECEIVING_ADDRESS=0xYourReceivingAddress
# Chain configuration
CHAIN=skale-base # or 'skale-base-sepolia' for confidential tokens
CURRENCY=USDC.e # or 'eUSDC' for confidential tokens
Basic Payment Example
Step 1: Install Dependencies
npm install @skalenetwork/mpp viem
Step 2: Create Payment Method
import { mpp } from '@skalenetwork/mpp/client'
import { createWalletClient, http } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
// Setup wallet
const account = privateKeyToAccount('0xYourPrivateKey')
const walletClient = createWalletClient({
account,
transport: http(process.env.SKALE_RPC)
})
// Create standard payment method
const standardPayment = mpp.charge({
chain: 'skale-base',
currency: 'USDC.e'
})
// Create gasless payment method
const gaslessPayment = mpp.charge({
chain: 'skale-base',
currency: 'USDC.e',
extensions: { gasless: 'eip3009' }
})
Step 3: Process Payment
async function processPayment(amount: string, useGasless = false) {
const method = useGasless ? gaslessPayment : standardPayment
// Create payment transaction
const tx = await method.createTransfer({
to: process.env.RECEIVING_ADDRESS,
amount: amount, // Amount in base units (e.g., "1000000" for 1 USDC)
from: account.address
})
// Sign and send
const hash = await walletClient.sendTransaction(tx)
console.log('Payment sent:', hash)
return hash
}
// Process 5 USDC payment
await processPayment('5000000', true) // Gasless
Encrypted Payments
Hide transaction amounts onchain using threshold encryption:
import { mpp } from '@skalenetwork/mpp/client'
// Create encrypted payment method
const encryptedPayment = mpp.charge({
chain: 'skale-base',
currency: 'USDC.e',
extensions: { skale: { encrypted: true } }
})
async function processEncryptedPayment(amount: string) {
const tx = await encryptedPayment.createTransfer({
to: process.env.RECEIVING_ADDRESS,
amount: amount,
from: account.address
})
const hash = await walletClient.sendTransaction(tx)
console.log('Encrypted payment sent:', hash)
return hash
}
Gasless + Encrypted Payments
Combine both features for maximum UX and privacy:
const privatePayment = mpp.charge({
chain: 'skale-base',
currency: 'USDC.e',
extensions: {
gasless: 'eip3009',
skale: { encrypted: true }
}
})
async function processPrivatePayment(amount: string) {
const tx = await privatePayment.createTransfer({
to: process.env.RECEIVING_ADDRESS,
amount: amount,
from: account.address
})
const hash = await walletClient.sendTransaction(tx)
console.log('Private payment sent:', hash)
return hash
}
Confidential Token Payments
Use native confidential tokens (eUSDC) on SKALE Base Sepolia for full privacy:
import { mpp } from '@skalenetwork/mpp/client'
// Confidential token with gasless + encryption
const confidentialPayment = mpp.charge({
chain: 'skale-base-sepolia',
currency: 'eUSDC',
extensions: {
skale: {
encrypted: true,
confidentialToken: true
},
gasless: 'eip3009'
}
})
async function processConfidentialPayment(amount: string) {
const tx = await confidentialPayment.createTransfer({
to: process.env.RECEIVING_ADDRESS,
amount: amount,
from: account.address
})
const hash = await walletClient.sendTransaction(tx)
console.log('Confidential payment sent:', hash)
return hash
}
Server-Side Integration
For backend payment processing:
import { mpp } from '@skalenetwork/mpp/server'
import { Mppx } from '@skalenetwork/mppx' // MPPx server SDK
// Setup MPPx server
const method = mpp.charge({
chain: 'skale-base',
currency: 'USDC.e',
extensions: { gasless: 'eip3009' }
})
const mppx = Mppx.create({
methods: [method],
realm: 'api.yourservice.com',
secretKey: process.env.MPP_SECRET
})
// Express middleware example
app.post('/api/payment', async (req, res) => {
const { amount, from } = req.body
try {
const result = await mppx.process({
amount,
from,
to: process.env.RECEIVING_ADDRESS
})
res.json({ success: true, txHash: result.hash })
} catch (error) {
res.status(400).json({ error: error.message })
}
})
Webhook Integration
Handle payment confirmations:
app.post('/webhook/mpp', async (req, res) => {
const { txHash, status, amount, sender } = req.body
if (status === 'confirmed') {
// Update database
await db.payments.create({
txHash,
amount,
sender,
status: 'completed',
timestamp: new Date()
})
// Trigger service delivery
await deliverService(sender, amount)
}
res.sendStatus(200)
})
Payment Verification
Verify payments onchain:
import { createPublicClient, http } from 'viem'
const publicClient = createPublicClient({
transport: http(process.env.SKALE_RPC)
})
async function verifyPayment(txHash: string, expectedAmount: string) {
const receipt = await publicClient.getTransactionReceipt({ hash: txHash })
if (receipt.status !== 'success') {
throw new Error('Transaction failed')
}
// For encrypted transactions, use the Privacy SDK to decrypt
// For standard transactions, verify logs
console.log('Payment verified:', receipt)
return true
}
Testing
Test payments on testnet before mainnet:
# SKALE Base Sepolia
SKALE_RPC=https://base-sepolia-testnet.skalenodes.com/v1/jubilant-horrible-ancha
CHAIN=skale-base-sepolia
CURRENCY=USDC.e
# SKALE Base Sepolia
SKALE_RPC=https://base-sepolia-testnet.skalenodes.com/v1/jubilant-horrible-ancha
CHAIN=skale-base-sepolia
CURRENCY=eUSDC
// Test payment
async function testPayment() {
const testPayment = mpp.charge({
chain: 'skale-base-sepolia', // Use testnet
currency: 'USDC.e',
extensions: { gasless: 'eip3009' }
})
const tx = await testPayment.createTransfer({
to: '0xTestReceiver',
amount: '100000', // 0.1 USDC
from: account.address
})
const hash = await walletClient.sendTransaction(tx)
console.log('Test payment:', hash)
}
Error Handling
Common errors and solutions:
try {
const hash = await processPayment('1000000')
} catch (error) {
if (error.message.includes('insufficient funds')) {
// For gasless: User lacks token balance
// For standard: User lacks gas (sFUEL)
console.error('Insufficient balance')
} else if (error.message.includes('EIP-3009')) {
// Token doesn't support gasless or user hasn't approved
console.error('Gasless not available for this token')
} else if (error.message.includes('BITE')) {
// Chain doesn't support encryption
console.error('Encrypted transactions not available on this chain')
}
}
Best Practices
Choose the right strategy:
- Standard: Fastest, lowest overhead
- Gasless: Best UX for new users
- Encrypted: Privacy for sensitive amounts
- Confidential: Maximum privacy (chains with Programmable Privacy only)
Security considerations:
- Never expose private keys in client-side code
- Use server-side MPPx for production payment processing
- Validate all payment amounts before processing
- Implement idempotency for payment webhooks
Resources