Skip to content

💰 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:

  1. RLC Tokens: Direct payment using RLC tokens
  2. Vouchers: Pre-funded vouchers for simplified payment
  3. 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:

ts
// 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

ts
// Execute with RLC payment (default)
const 
result
= await
dataProtectorCore
.
processProtectedData
({
protectedData
: '0x123abc...',
app
: '0x456def...',
useVoucher
: false, // Explicitly use RLC payment
});

Using CLI with RLC

bash
# 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 Chain Not Supported

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

ts
const 
result
= await
dataProtectorCore
.
processProtectedData
({
protectedData
: '0x123abc...',
app
: '0x456def...',
useVoucher
: true, // Use voucher for payment
});

TIP

If your voucher doesn't have enough RLC 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

ts
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

bash
# 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:

  1. Application Fee: Paid to the app developer
  2. Data Fee: Paid to the data owner (if using protected data)
  3. Workerpool Fee: Paid to the computation provider
  4. Gas Fees: Blockchain transaction costs

Setting Maximum Prices

You can control costs by setting maximum prices for each component:

ts
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:

ts
// 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:

ts
// 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:

ts
// 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:

ts
// 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:

ts
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:

ts
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:

ts
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

ts
// 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

ts
// Use vouchers for production applications
const 
result
= await
dataProtectorCore
.
processProtectedData
({
protectedData
: '0x123abc...',
app
: '0x456def...',
useVoucher
: true, // More reliable for production
});

3. Monitor Costs Regularly

ts
// 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

ts
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: