Developer Integration Guide

How to integrate IFR Lock into your product — permissionless, on-chain license gating for any application.

1. Overview: How to Integrate IFR Lock Into Your Product

Any product can use IFR Lock to offer premium tiers to its users. The contract is fully permissionless — no approval, API key, or partnership is required. Your product simply queries the on-chain lock status of a wallet and gates features accordingly.

Your product decides the minimum lock amount for premium access. The user locks IFR tokens on-chain, and your application checks whether the lock meets your threshold.

Integration in 3 steps: (1) Set up a resolver that reads from the IFRLock contract, (2) query isLocked() for each user wallet, (3) gate features based on the boolean result.

2. Architecture Overview

The integration follows a simple three-layer architecture. Your application talks to a lightweight resolver (your backend), which queries the IFRLock contract on Ethereum.

Your App
Frontend / Backend
License Resolver
Your API server
IFRLock
On Ethereum

The resolver maps wallet addresses to license status. It is stateless — it queries on demand and does not persist user data. You own and operate the resolver, giving you full control over caching, rate limiting, and minimum lock thresholds.

3. Step 1: ABI Reference

You only need the read functions from the IFRLock contract. Here is the minimal ABI for integration:

Minimal ABI (read-only)
const IFR_LOCK_ABI = [
  "function isLocked(address user, uint256 minAmount) view returns (bool)",
  "function lockedBalance(address user) view returns (uint256)",
  "function lockInfo(address user) view returns (uint256 amount, uint256 lockedAt)",
  "function totalLocked() view returns (uint256)"
];
Network Contract Address
Sepolia (testnet) 0x0Cab0A9440643128540222acC6eF5028736675d3
Mainnet TBD

4. Step 2: Set Up Your Resolver

The resolver is a simple Express server that queries the IFRLock contract and returns a JSON response. Deploy this on your own infrastructure.

Node.js / Express — resolver.js
const { ethers } = require("ethers");
const express = require("express");

const LOCK_ADDRESS = "0x0Cab0A9440643128540222acC6eF5028736675d3";
const LOCK_ABI = [
  "function isLocked(address user, uint256 minAmount) view returns (bool)",
  "function lockedBalance(address user) view returns (uint256)",
];
const MIN_LOCK = ethers.utils.parseUnits("5000", 9); // 5000 IFR minimum

const provider = new ethers.providers.JsonRpcProvider(process.env.RPC_URL);
const lock = new ethers.Contract(LOCK_ADDRESS, LOCK_ABI, provider);

const app = express();

app.get("/api/license/:wallet", async (req, res) => {
  try {
    const wallet = req.params.wallet;
    const isPremium = await lock.isLocked(wallet, MIN_LOCK);
    const balance = await lock.lockedBalance(wallet);
    res.json({
      wallet,
      premium: isPremium,
      lockedAmount: ethers.utils.formatUnits(balance, 9),
      minRequired: "5000",
    });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

app.listen(3000);

Note: The resolver uses ethers v5 syntax. IFR uses 9 decimals (not 18), so always use parseUnits(value, 9) and formatUnits(value, 9).

5. Step 3: Gate Features in Your App

On the frontend, call your resolver endpoint and use the response to enable or disable premium features.

Frontend — feature gating
// Frontend example
async function checkPremium(walletAddress) {
  const res = await fetch(`/api/license/${walletAddress}`);
  const data = await res.json();
  if (data.premium) {
    // Enable premium features
    showPremiumUI();
  } else {
    // Show standard plan + prompt to lock IFR
    showUpgradePrompt(data.minRequired);
  }
}

The resolver returns a simple JSON object. The premium field is a boolean — true if the user has at least the minimum amount locked, false otherwise. Your frontend logic is entirely up to you.

6. Step 4: Lock/Unlock UI (Optional)

If you want to let users lock IFR directly from your application (instead of directing them to the Inferno dashboard), you can integrate the lock flow with a connected wallet such as MetaMask.

Lock flow — approve + lock
// Requires user's connected wallet (e.g., MetaMask)
const TOKEN_ADDRESS = "0x3Bd71947F288d1dd8B21129B1bE4FF16EDd5d1F4";
const TOKEN_ABI = ["function approve(address spender, uint256 amount) returns (bool)"];

async function lockIFR(signer, amount) {
  const token = new ethers.Contract(TOKEN_ADDRESS, TOKEN_ABI, signer);
  const lock = new ethers.Contract(LOCK_ADDRESS, LOCK_ABI_FULL, signer);

  const parsedAmount = ethers.utils.parseUnits(amount, 9);

  // Step 1: Approve
  const approveTx = await token.approve(LOCK_ADDRESS, parsedAmount);
  await approveTx.wait();

  // Step 2: Lock
  const lockTx = await lock.lock(parsedAmount);
  await lockTx.wait();

  return true;
}

Important: The lock flow requires two transactions: (1) approve the IFRLock contract to spend tokens, then (2) call lock(). Both must be signed by the user's wallet.

7. Using lockType for Multi-App

If you want to tag locks with your application's identifier, use lockWithType(). This is useful for analytics and distinguishing which app prompted the lock.

lockType — application tagging
// Lock with your app's specific tag
const lockType = ethers.utils.id("myapp_premium"); // bytes32 hash
await lock.lockWithType(amount, lockType);

Note: lockType is metadata only — it does not affect isLocked() queries. One user has one lock balance, shared across all apps. The tag is for your own tracking purposes.

8. Privacy Checklist

Integrating with on-chain data requires care around user privacy. Follow these guidelines:

The resolver should be stateless — query on demand, don't persist. If you need caching, use an in-memory store with a short TTL and no personal identifiers.

9. Choosing Your Minimum Lock Amount

The minimum lock amount is entirely your decision. It lives in your resolver, not on-chain, so you can change it at any time without a contract interaction.

Tier Lock Amount Use Case
Basic 1,000 IFR Low barrier, broad access, community features
Standard 5,000 IFR Premium features, moderate commitment
Enterprise 25,000 IFR High-value access, strong holder commitment

Consider the value of your premium features when setting the threshold. A higher minimum means fewer premium users but stronger commitment. A lower minimum increases accessibility. You can also implement multiple tiers by checking against different thresholds.

10. Testing on Sepolia

Before going live, test your integration on the Sepolia testnet. You will need Sepolia ETH (from a faucet) and test IFR tokens (contact the team or swap on Uniswap Sepolia).

Foundry (cast) — query lock status
# Get Sepolia ETH from a faucet
# Get test IFR tokens (contact the team or use Uniswap Sepolia)

# Query lock status directly
cast call 0x0Cab0A9440643128540222acC6eF5028736675d3 \
  "isLocked(address,uint256)(bool)" \
  YOUR_WALLET_ADDRESS \
  5000000000000  # 5000 * 1e9

Tip: The cast command is part of the Foundry toolkit. It lets you call read functions directly from the command line without writing any code. Replace YOUR_WALLET_ADDRESS with an actual Sepolia address.

Quick Reference

Resource Link / Value
IFRLock (Sepolia) 0x0Cab0A9440643128540222acC6eF5028736675d3
InfernoToken (Sepolia) 0x3Bd71947F288d1dd8B21129B1bE4FF16EDd5d1F4
Token Decimals 9
Ticker $IFR
Ethers.js version v5 (CommonJS)
Uniswap Router (Sepolia) 0xC532a74256D3Db42D0Bf7a0400fEFDbad7694008

11. Partner Benefits

Partners who integrate IFR Lock into their products gain access to a range of benefits designed to support growth and alignment with the Inferno ecosystem.

Lock-triggered Creator Rewards

Partner rewards are driven by real user engagement through the PartnerVault smart contract. When a user locks IFR for a creator's product, the creator earns a percentage of the locked amount from the Partner Ecosystem Pool.

Parameter Value Description
RewardBps 10–25% (governance-controlled) Percentage of each lock amount credited to the creator
Annual Emission Cap 4M IFR / year Hard cap on total yearly rewards to prevent pool exhaustion
Per-Partner Cap Configurable per partner Maximum allocation per partner (governance-controlled)
Vesting 6–12 months linear Rewards vest linearly with optional cliff — no instant dumps

How It Works

  1. Partner Registration: Governance creates a partner entry in PartnerVault with a beneficiary address, max allocation, and vesting schedule.
  2. User Locks IFR: When a user locks IFR for the partner's product via IFRLock, the lock event is recorded.
  3. Reward Accrual: recordLockReward(partnerId, lockAmount) credits the partner with lockAmount × rewardBps / 10000 IFR from the pool.
  4. Vested Claiming: Partners call claim(partnerId) to withdraw vested rewards. Linear vesting ensures long-term alignment.

Total Partner Pool: 40M IFR (4% of total supply), managed by the PartnerVault contract on-chain. The remaining 60M IFR (6%) is reserved for community grants, bug bounties, and ecosystem development.

PartnerVault Integration (ethers.js v5)

Query partner rewards and claim
const VAULT_ADDR = "0x5F12C0bC616e9Ca347D48C33266aA8fe98490A39";
const VAULT_ABI = [
  "function claimable(uint256 partnerId) view returns (uint256)",
  "function partners(uint256 partnerId) view returns (address,uint256,uint256,uint256,uint256,uint256,uint256,uint256,bool,bool,string)",
  "function claim(uint256 partnerId)",
];

const vault = new ethers.Contract(VAULT_ADDR, VAULT_ABI, provider);

// Check claimable amount for partner #0
const amount = await vault.claimable(0);
console.log("Claimable:", ethers.utils.formatUnits(amount, 9), "IFR");

// Claim vested rewards (requires signer)
const vaultSigner = vault.connect(signer);
await vaultSigner.claim(0);

12. Full Integration Example

Below is a complete ethers.js v5 example showing the full resolver flow — from contract setup to querying lock status, balance, and lock details for a given wallet.

Full resolver flow — ethers.js v5
const { ethers } = require("ethers");

const LOCK_ADDR = "0x0Cab0A9440643128540222acC6eF5028736675d3";
const LOCK_ABI = [
  "function isLocked(address user, uint256 minAmount) view returns (bool)",
  "function lockedBalance(address user) view returns (uint256)",
  "function lockInfo(address user) view returns (uint256 amount, uint256 lockedAt)",
];

const provider = new ethers.providers.JsonRpcProvider(process.env.RPC_URL);
const lock = new ethers.Contract(LOCK_ADDR, LOCK_ABI, provider);

async function checkLicense(wallet) {
  const minLock = ethers.utils.parseUnits("5000", 9); // 5000 IFR (9 decimals)
  const premium = await lock.isLocked(wallet, minLock);
  const balance = await lock.lockedBalance(wallet);
  const [amount, lockedAt] = await lock.lockInfo(wallet);
  return {
    premium,
    lockedAmount: ethers.utils.formatUnits(balance, 9),
    lockedSince: new Date(lockedAt.toNumber() * 1000).toISOString(),
  };
}

Tip: This example combines all three read functions into a single helper. In production, consider batching calls with Promise.all() or a multicall contract for better RPC efficiency.

13. Privacy & Compliance

When integrating on-chain data into your product, ensure your implementation meets privacy and compliance requirements. Use the following checklist as a guide.

Category Requirement Implementation
Data Minimization Only query boolean lock status Use isLocked() not lockedBalance()
No PII Storage Don't link wallets to identities Stateless resolver, no database
RPC Security Use private RPC endpoint Alchemy/Infura with API key
Caching Short TTL, no persistent store In-memory cache, 5min TTL max
Audit Trail Log access decisions, not amounts Log premium=true/false only
GDPR No personal data processed Wallet addresses are pseudonymous

14. Partner Onboarding Flow

The partner onboarding process is designed to get you integrated quickly and smoothly. Follow these steps to go from application to live integration.

  1. Application — Contact the Inferno team with your product description and user base size
  2. Technical Review — We assess integration feasibility and determine the minimum lock amount
  3. Token Allocation — Partners receive an IFR allocation for initial user onboarding
  4. Integration — Implement the resolver using the code examples above (typically 1-2 days)
  5. Testing — Verify on Sepolia testnet before going live
  6. Launch — Go live on mainnet, joint announcement, featured on the Inferno ecosystem page

Ready to integrate? The entire process from application to live integration typically takes less than a week. Most of the time is spent on your side implementing and testing the resolver.

SDK Quickstart — Full code examples for Node.js, React + wagmi v2, Python, tier system, and wallet signature verification: SDK_QUICKSTART.md

15. Creator Gateway Integration

The Creator Gateway enables YouTube creators and content platforms to gate premium content behind an IFR Lock — as an alternative (or addition) to traditional memberships.

// Entitlement Config Example (config/entitlements.json)
{
  "premium": {
    "logic": "OR",
    "conditions": [
      { "type": "youtube_member", "tier": "any" },
      { "type": "ifr_lock", "minIFR": 5000 }
    ]
  }
}
Full Spec: CREATOR_GATEWAY.mdYOUTUBE_INTEGRATION.md

16. Points Backend Integration

The IFR Points Backend provides off-chain points tracking and EIP-712 discount voucher issuance. Points reduce the protocol fee on swaps via FeeRouterV1.

API Endpoints

EndpointMethodDescription
/auth/siwe/nonceGETGet SIWE nonce
/auth/siwe/verifyPOSTVerify SIWE signature → JWT
/points/balanceGETGet wallet points balance
/points/eventPOSTRecord points event
/voucher/issuePOSTIssue EIP-712 discount voucher

SIWE Authentication Flow

  1. Frontend calls GET /auth/siwe/nonce
  2. User signs SIWE message with wallet
  3. Frontend sends signature to POST /auth/siwe/verify
  4. Backend returns JWT (24h expiry)
  5. All subsequent calls use Authorization: Bearer {jwt}

Voucher → FeeRouter Flow

  1. User accumulates points (guide completion, lock events, referrals)
  2. Frontend calls POST /voucher/issue with JWT
  3. Backend signs EIP-712 DiscountVoucher struct
  4. Frontend passes voucher to FeeRouterV1.swapWithFee()
  5. On-chain: discount applied, nonce consumed (replay-protected)
Full E2E Flow: E2E_FLOW.md

17. FeeRouter Integration

FeeRouterV1 routes swaps through whitelisted adapters with an optional protocol fee, discountable via EIP-712 signed vouchers.

Key Functions

// Execute swap with protocol fee (+ optional discount voucher)
function swapWithFee(
    address adapter,      // whitelisted swap adapter
    bytes calldata data,  // adapter-specific swap calldata
    address tokenIn,      // input token
    uint256 amountIn,     // input amount
    DiscountVoucher calldata voucher  // EIP-712 voucher (or empty)
) external payable;

// Voucher Struct (EIP-712)
struct DiscountVoucher {
    address user;         // beneficiary
    uint256 discountBps;  // discount in basis points
    uint256 nonce;        // replay protection
    uint256 deadline;     // expiry timestamp
    bytes signature;      // EIP-712 signature from voucherSigner
}

// Off-chain voucher validation
function isVoucherValid(DiscountVoucher calldata voucher)
    external view returns (bool);

Sepolia Deployment

ParameterValue
Address0x499289C8Ef49769F4FcFF3ca86D4BD7b55B49aa4
protocolFeeBps5 (0.05%)
FEE_CAP_BPS25 (0.25%) — hard cap
EIP-712 Domainname="InfernoFeeRouter", version="1", chainId=11155111