Gas Relayer API
The Gas Relayer enables gasless transactions for your users - they can interact with blockchain features (claiming perks, redeeming rewards) without needing ETH for gas fees. Your brand wallet pays all gas costs.
🚀 Try It Out: Interactive API Explorer → - Test gasless transactions directly in your browser!
Overview
Pattern: Meta-transaction relayer
- User performs action in your app
- Frontend creates transaction details
- Gas Relayer signs and executes with dev wallet
- User receives transaction hash
- Your brand pays the gas fee
Key Benefit: Zero blockchain complexity for end users - no wallet setup, no ETH needed, no gas fees.
Base URL
https://relayer.loyalteez.app
Production URL:
https://relayer.loyalteez.app
Development: Same URL (uses Privy tokens for authentication)
Authentication
All requests require Privy authentication:
Authorization: Bearer YOUR_PRIVY_ACCESS_TOKEN
Get the access token from Privy SDK after user login.
Endpoints
POST /relay
Execute a gasless transaction on behalf of a user.
Headers:
Authorization: Bearer PRIVY_ACCESS_TOKEN
Content-Type: application/json
Origin: https://yoursite.com
Request Body:
{
"to": "0x1234567890123456789012345678901234567890",
"data": "0xabcdef...",
"value": "0",
"gasLimit": 500000,
"userAddress": "0x9876543210987654321098765432109876543210",
"metadata": {
"action": "claim_perk",
"perkId": "123",
"permit": {
"owner": "0x...",
"spender": "0x...",
"value": "1000000",
"deadline": 1699564800,
"v": 27,
"r": "0x...",
"s": "0x..."
}
}
}
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
to | address | Yes | Contract address to call |
data | hex | Yes | Encoded function call data |
value | string | No | ETH value to send (default: "0") |
gasLimit | number | No | Max gas limit (default: auto-estimate) |
userAddress | address | Yes | User's wallet address |
metadata | object | No | Additional context |
metadata.permit | object | No | EIP-2612 permit for gasless approval |
Response:
{
"success": true,
"transactionHash": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
"gasUsed": "250000",
"effectiveGasPrice": "25000000000",
"blockNumber": 12345678,
"from": "0x_DEV_WALLET_ADDRESS_",
"to": "0x_CONTRACT_ADDRESS_",
"status": "confirmed"
}
Error Response:
{
"error": "Transaction validation failed: Contract not whitelisted",
"code": "VALIDATION_FAILED",
"details": {
"contract": "0x...",
"allowed": ["0x...", "0x..."]
}
}
EIP-2612 Permit Integration
Enable gasless approvals using EIP-2612 permit signatures:
// 1. User signs permit (no gas needed)
const permit = await getPermitSignature({
token: tokenAddress,
owner: userAddress,
spender: contractAddress,
value: amount,
deadline: Math.floor(Date.now() / 1000) + 3600 // 1 hour
});
// 2. Send transaction with permit
const response = await fetch('https://relayer.loyalteez.app/relay', {
method: 'POST',
headers: {
'Authorization': `Bearer ${privyAccessToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
to: contractAddress,
data: encodedFunctionCall,
userAddress: userAddress,
metadata: {
permit: permit // Gas relayer executes permit before main transaction
}
})
});
Permit Object Schema:
interface Permit {
owner: string; // User's wallet address
spender: string; // Contract to approve
value: string; // Amount to approve
deadline: number; // Unix timestamp
v: number; // Signature v
r: string; // Signature r
s: string; // Signature s
}
Security
Whitelisted Contracts
Only transactions to approved contracts are allowed:
- PerkNFT: Claiming perks
- Loyalteez: LTZ token transfers
- PointsSale: Purchasing with LTZ
- BusinessEscrow: Business transactions
Attempts to call non-whitelisted contracts will be rejected.
Rate Limiting
- 35 transactions per hour per user
- Prevents abuse and DoS attacks
- Protects your gas budget
Gas Limits
- Maximum: 1,000,000 gas per transaction
- Cap: 100 Gwei gas price maximum
- Prevents expensive transactions from draining budget
Authentication
- Privy tokens verified on every request
- Token must be valid and not expired
- Origin header validated for CORS
Rate Limits
| Limit Type | Value | Scope |
|---|---|---|
| Transactions per hour | 35 | Per user address |
| Max gas per transaction | 1,000,000 | Per transaction |
| Max gas price | 100 Gwei | Per transaction |
| API requests | 100/min | Per IP |
Error Codes
| Status | Code | Description |
|---|---|---|
| 400 | MISSING_FIELDS | Required fields missing |
| 401 | UNAUTHORIZED | Invalid or missing Privy token |
| 403 | VALIDATION_FAILED | Transaction validation failed |
| 403 | CONTRACT_NOT_WHITELISTED | Contract not approved |
| 429 | RATE_LIMIT_EXCEEDED | Too many transactions |
| 500 | TRANSACTION_FAILED | On-chain execution failed |
Integration Example
React + Privy + Gas Relayer
import { usePrivy } from '@privy-io/react-auth';
import { ethers } from 'ethers';
function ClaimPerkButton({ perkId }) {
const { getAccessToken, user } = usePrivy();
const claimPerk = async () => {
// 1. Get Privy access token
const token = await getAccessToken();
// 2. Encode contract function call
const iface = new ethers.Interface(PERK_NFT_ABI);
const data = iface.encodeFunctionData('claimPerk', [perkId]);
// 3. Call gas relayer
const response = await fetch('https://relayer.loyalteez.app/relay', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
to: process.env.REACT_APP_PERK_NFT_ADDRESS,
data: data,
userAddress: user.wallet.address,
metadata: {
action: 'claim_perk',
perkId: perkId
}
})
});
const result = await response.json();
if (result.success) {
console.log('Perk claimed! TxHash:', result.transactionHash);
// Show success message to user
} else {
console.error('Claim failed:', result.error);
// Show error message
}
};
return (
<button onClick={claimPerk}>
Claim Perk (No Gas Fees!)
</button>
);
}
With EIP-2612 Permit
async function claimPerkWithPermit(perkId, ltzCost) {
const token = await getAccessToken();
// 1. Generate permit signature (gasless approval)
const permit = await signPermit({
token: LTZ_TOKEN_ADDRESS,
spender: PERK_NFT_ADDRESS,
value: ltzCost,
deadline: Math.floor(Date.now() / 1000) + 3600
});
// 2. Encode claim function
const data = ethers.utils.defaultAbiCoder.encode(
['uint256'],
[perkId]
);
// 3. Execute via gas relayer (handles permit + claim)
const response = await fetch('https://relayer.loyalteez.app/relay', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
to: PERK_NFT_ADDRESS,
data: data,
userAddress: user.wallet.address,
metadata: {
action: 'claim_perk',
perkId: perkId,
permit: permit // ← Gasless approval!
}
})
});
return await response.json();
}
Testing
Test Authentication
# Get Privy token from your app
TOKEN="your_privy_access_token"
curl -X POST https://relayer.loyalteez.app/relay \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"to": "0x1234...",
"data": "0xabcd...",
"userAddress": "0x5678...",
"gasLimit": 100000
}'
Test Rate Limiting
Send 40 requests rapidly - requests 36+ should fail with 429 RATE_LIMIT_EXCEEDED.
Gas Cost Monitoring
Track your gas spending in the Partner Portal:
- Dashboard → Gas Usage
- Total ETH spent on gas
- Transactions per day
- Average gas per transaction
- Estimated monthly cost
Set up alerts when gas spending exceeds budget.
Best Practices
1. Estimate Gas First
// Estimate before relaying
const estimated = await contract.estimateGas.claimPerk(perkId);
const withBuffer = estimated * 120n / 100n; // +20% buffer
// Include in relay request
gasLimit: Number(withBuffer)
2. Handle Errors Gracefully
try {
const result = await relayTransaction(txData);
// Success
} catch (error) {
if (error.code === 'RATE_LIMIT_EXCEEDED') {
// Show "Please wait before trying again"
} else if (error.code === 'VALIDATION_FAILED') {
// Show "Transaction not allowed"
} else {
// Generic error message
}
}
3. Show Transaction Status
// While waiting
showLoading('Processing transaction...');
// On success
showSuccess(`Transaction confirmed! View on explorer: ${explorerUrl}/${txHash}`);
// On failure
showError('Transaction failed. Please try again.');
4. Cache Permit Signatures
// Permit valid for 1 hour - reuse it!
const cachedPermit = localStorage.getItem(`permit_${userAddress}_${spender}`);
if (cachedPermit && cachedPermit.deadline > Date.now() / 1000) {
// Reuse existing permit
} else {
// Generate new permit
const newPermit = await signPermit(...)
localStorage.setItem(`permit_${userAddress}_${spender}`, newPermit);
}
Monitoring & Alerts
Set up monitoring for:
- ✅ Gas spending: Alert when > $X per day
- ✅ Failed transactions: Alert when failure rate > 5%
- ✅ Rate limit hits: Alert when users hitting limits
- ✅ Response time: Alert when > 2 seconds
Available in Partner Portal → Settings → Monitoring
Support
- Documentation: Gas Relayer Guide
- Examples: React Integration
- Email: [email protected]
- Status: status.loyalteez.app