← back to projects

🥇 Best LI.FI-Powered DeFi Integration · HackMoney 2026

ENSRouter

Feb 2026
ENSCross-chainLI.FIUSDCNext.js

// the problem

Wallet fragmentation is worse than it looks. You might prefer ETH on Base, USDC on Arbitrum, and USDT on Polygon depending on what you're doing. To receive payments correctly, you'd need to communicate all of this to each sender — and update them every time your preferences change. ENS names already solve the 'find my address' problem. ENSRouter extends that to solve the 'how do you want to receive funds' problem.

// the design decision

The obvious approach is a database mapping ENS names to routing preferences. But that's a trusted intermediary. Instead, we store the routing config directly in ENS text records using three custom keys: ENSRouter.chain, ENSRouter.tokenAlloc, and ENSRouter.slippage. The payment itself never touches a backend — LI.FI SDK executes bridging and swapping entirely client-side. The backend only exists for Telegram notifications.

// key implementation detail

All three text record writes are batched into a single multicall() transaction on the ENS Public Resolver, so the recipient pays gas once. The tokenAlloc format is USDC:60,ETH:30,DAI:10 — a simple string that any application can parse. We add a USDC fallback: if a swap to a non-USDC token fails (e.g., insufficient liquidity), the system retries with USDC, so recipients always receive something.
// Three setText calls batched into one multicall
const calls = [
  encodeFunctionData({
    abi: ensResolverAbi,
    functionName: 'setText',
    args: [namehash(ensName), 'ENSRouter.chain', chain]
  }),
  encodeFunctionData({
    abi: ensResolverAbi,
    functionName: 'setText',
    args: [namehash(ensName), 'ENSRouter.tokenAlloc', 'USDC:60,ETH:30,DAI:10']
  }),
  encodeFunctionData({
    abi: ensResolverAbi,
    functionName: 'setText',
    args: [namehash(ensName), 'ENSRouter.slippage', '1']
  })
];
await writeContract({ functionName: 'multicall', args: [calls] });

// what i learned

ENS text records are an underused primitive. They're the right place to store any user preference that should be portable, public, and not controlled by a single application. The harder problem was UX: multicall batching on ENS mainnet is not obvious, and the auto-chain-switch flow needed careful state management across React effect cycles.