💰 How to Pay for iApp Executions
Understanding how to pay for iApp executions is crucial for using the iExec network effectively. This guide covers all payment methods, pricing structures, and cost management strategies.
Payment Methods Overview
iExec supports multiple payment methods for executing iApps:
- RLC Tokens: Direct payment using RLC (Request Compute Language) tokens
- Vouchers: Pre-funded vouchers for simplified payment
- Mixed Payment: Combination of RLC and vouchers
Method 1: Paying with RLC Tokens
RLC tokens are the native currency of the iExec network. You need to have RLC in your wallet to pay for executions.
Setting Up RLC Payment
Step 1: Get RLC Tokens
You can obtain RLC tokens from various exchanges:
- Centralized Exchanges: Binance, Coinbase, Kraken
- Decentralized Exchanges: Uniswap, SushiSwap
- Direct Purchase: Through iExec's official channels
Step 2: Transfer to iExec Sidechain
RLC tokens need to be on the iExec sidechain (Bellecour) for payments:
// Check your balance
const balance = await iexec.account.checkBalance('0xa0c15e...');
console.log('Nano RLC staked:', balance.stake.toString());
console.log('Nano RLC locked:', balance.locked.toString());
// Deposit RLC to the sidechain
await iexec.account.deposit(1_000_000_000); // Deposit 1 RLC
Step 3: Execute with RLC Payment
// Execute with RLC payment (default)
const result = await dataProtectorCore.processProtectedData({
protectedData: '0x123abc...',
app: '0x456def...',
useVoucher: false, // Explicitly use RLC payment
});
Using CLI with RLC
# Execute with RLC payment
iexec app run 0x456def... --protectedData 0xa0c15e...
# Check your balance
iexec account show
# Deposit RLC
iexec account deposit 100
Method 2: Paying with Vouchers
Vouchers are pre-funded payment instruments that simplify the payment process and can be shared with others.
Understanding Vouchers
Vouchers are ERC-20 tokens that represent pre-funded computation credits on the iExec network. They offer several advantages:
- Simplified Payment: No need to manage RLC transfers
- Sharing: Can be shared with team members or users
- Budget Control: Set spending limits
- Automated Top-up: Can be configured to automatically refill
Using Vouchers for Payment
Basic Voucher Usage
const result = await dataProtectorCore.processProtectedData({
protectedData: '0x123abc...',
app: '0x456def...',
useVoucher: true, // Use voucher for payment
});
TIP
If your voucher doesn't have enough xRLC to cover the deal, the SDK will automatically get the required amount to your iExec account. Ensure that your voucher is authorized to access your iExec account and that your account has sufficient funds for this transfer to proceed.
Using Someone Else's Voucher
const result = await dataProtectorCore.processProtectedData({
protectedData: '0x123abc...',
app: '0x456def...',
useVoucher: true,
voucherOwner: '0x5714eB...', // Voucher owner's address
});
WARNING
Make sure the voucher's owner has authorized you to use it. This parameter must be used in combination with useVoucher: true
.
CLI with Vouchers
# Use your own voucher
iexec app run 0x456def... --protectedData 0xa0c15e... --useVoucher
# Use someone else's voucher
iexec app run 0x456def... --protectedData 0xa0c15e... --useVoucher --voucherOwner 0x5714eB...
Understanding Pricing
Cost Components
iApp execution costs consist of several components:
- Application Fee: Paid to the app developer
- Data Fee: Paid to the data owner (if using protected data)
- Workerpool Fee: Paid to the computation provider
- Gas Fees: Blockchain transaction costs
Setting Maximum Prices
You can control costs by setting maximum prices for each component:
const result = await dataProtectorCore.processProtectedData({
protectedData: '0x123abc...',
app: '0x456def...',
dataMaxPrice: 5, // Maximum amount (in nRLC) to pay the protected data owner
appMaxPrice: 3, // Maximum amount (in nRLC) to pay the iApp provider
workerpoolMaxPrice: 2, // Maximum amount (in nRLC) to pay the workerpool provider
});
INFO
All price parameters are in nRLC (nano RLC). The default value for all price parameters is 0
, which means no maximum price limit is set.
Cost Management Strategies
1. Monitor Your Balance
Regularly check your RLC balance and voucher status:
// Check your balance
const balance = await iexec.account.checkBalance('0xa0c15e...');
console.log('Nano RLC staked:', balance.stake.toString());
console.log('Nano RLC locked:', balance.locked.toString());
// Check voucher balance (if applicable)
const myVoucher = await iexec.voucher.showUserVoucher('0xa0c15e...');
console.log('Voucher info:', myVoucher);
2. Set Reasonable Price Limits
Always set maximum prices to avoid unexpected costs:
// Good practice: Set explicit price limits
const result = await dataProtectorCore.processProtectedData({
protectedData: '0x123abc...',
app: '0x456def...',
dataMaxPrice: 5, // Maximum for data access
appMaxPrice: 3, // Maximum for app usage
workerpoolMaxPrice: 2, // Maximum for computation
});
3. Use Vouchers for Regular Usage
For frequent executions, consider using vouchers:
// Use vouchers for regular operations
const result = await dataProtectorCore.processProtectedData({
protectedData: '0x123abc...',
app: '0x456def...',
useVoucher: true, // Simplify payment process
});
4. Batch Operations
Group multiple executions to optimize costs:
// Execute multiple tasks efficiently
const tasks = [
{ protectedData: '0x123abc...', app: '0x456def...' },
{ protectedData: '0x124abc...', app: '0x456def...' },
];
const results = await Promise.all(
tasks.map((task) =>
dataProtectorCore.processProtectedData({
...task,
dataMaxPrice: 5,
appMaxPrice: 3,
workerpoolMaxPrice: 2,
useVoucher: true,
})
)
);
Payment Troubleshooting
Insufficient Balance
If you encounter insufficient balance errors:
try {
const result = await dataProtectorCore.processProtectedData({
protectedData: '0x123abc...',
app: '0x456def...',
});
} catch (error) {
if (
error instanceof Error &&
error.message.includes('insufficient balance')
) {
console.log('Please add more RLC to your account');
// Check balance and deposit more if needed
const balance = await iexec.account.checkBalance('0xa0c15e...');
console.log('Nano RLC staked:', balance.stake.toString());
console.log('Nano RLC locked:', balance.locked.toString());
}
}
Voucher Authorization Issues
If voucher payment fails:
try {
const result = await dataProtectorCore.processProtectedData({
protectedData: '0x123abc...',
app: '0x456def...',
useVoucher: true,
voucherOwner: '0x5714eB...',
});
} catch (error) {
if (error instanceof Error && error.message.includes('voucher')) {
console.log('Voucher authorization failed. Check voucher permissions.');
}
}
Price Too High Errors
If execution fails due to price constraints:
try {
const result = await dataProtectorCore.processProtectedData({
protectedData: '0x123abc...',
app: '0x456def...',
dataMaxPrice: 2, // Low price limit
appMaxPrice: 1,
workerpoolMaxPrice: 1,
});
} catch (error) {
if (error instanceof Error && error.message.includes('price')) {
console.log('Increase price limits or wait for lower prices');
// Retry with higher prices
const retryResult = await dataProtectorCore.processProtectedData({
protectedData: '0x123abc...',
app: '0x456def...',
dataMaxPrice: 8, // Higher price limit
appMaxPrice: 5,
workerpoolMaxPrice: 3,
});
}
}
Best Practices
1. Always Set Price Limits
// Never execute without price limits
const result = await dataProtectorCore.processProtectedData({
protectedData: '0x123abc...',
app: '0x456def...',
dataMaxPrice: 5, // Always set price limits
appMaxPrice: 3,
workerpoolMaxPrice: 2,
});
2. Use Vouchers for Production
// Use vouchers for production applications
const result = await dataProtectorCore.processProtectedData({
protectedData: '0x123abc...',
app: '0x456def...',
useVoucher: true, // More reliable for production
});
3. Monitor Costs Regularly
// Check costs before and after execution
const balanceBefore = await iexec.account.checkBalance('0xa0c15e...');
const result = await dataProtectorCore.processProtectedData({
protectedData: '0x123abc...',
app: '0x456def...',
dataMaxPrice: 5,
appMaxPrice: 3,
workerpoolMaxPrice: 2,
});
const balanceAfter = await iexec.account.checkBalance('0xa0c15e...');
console.log(
'Cost:',
balanceBefore.stake.toString(),
'->',
balanceAfter.stake.toString()
);
4. Handle Payment Errors Gracefully
const executeWithPaymentRetry = async (params: any, maxRetries = 3) => {
for (let i = 0; i < maxRetries; i++) {
try {
return await dataProtectorCore.processProtectedData(params);
} catch (error) {
if (
error instanceof Error &&
error.message.includes('insufficient balance')
) {
console.log('Insufficient balance, please add more RLC');
break;
}
if (i === maxRetries - 1) throw error;
console.log(`Payment retry ${i + 1}/${maxRetries}`);
await new Promise((resolve) => setTimeout(resolve, 1000));
}
}
};
Next Steps
Now that you understand payment methods:
- Learn about Adding Inputs to Execution
- Explore Using iApps with Protected Data
- Review the pricing information above for detailed cost analysis