Vault UX Improvements

This document provides comprehensive technical documentation for Noderr Protocol's institutional-grade ERC-7540 UX improvement contracts.


Overview

While ERC-7540 provides powerful async vault capabilities, the 2-step flow creates UX friction for users. Noderr Protocol has developed three complementary contracts that transform these "downsides" into competitive advantages:

ContractPurposeKey Benefit
VaultRouterBundled UX with auto-claimSingle-transaction experience
AutoClaimKeeperValidator auto-claim serviceHands-free claim execution
InstantLiquidityPoolInstant liquidity AMMSkip the wait entirely

VaultRouter

The VaultRouter is the primary interface for users interacting with ERC-7540 vaults. It provides a single-transaction UX by handling request submission, fulfillment monitoring, and claim execution.

Key Features

  • Single-Transaction UX: Users interact once; router handles the rest
  • Auto-Claim Service: Automatic claim execution when requests are fulfilled
  • Batch Operations: Deposit/redeem across multiple vaults in one transaction
  • Gasless Transactions: EIP-712 signatures for gas-free operations
  • Slippage Protection: Configurable minimum output guarantees
  • Emergency Controls: Pause functionality for security incidents

Architecture

User → VaultRouter → ERC-7540 Vault
↓
AutoClaimKeeper
↓
Automatic Claim

Core Functions

Request Deposit with Auto-Claim

function requestDeposit(
address vault,
uint256 assets,
uint256 minShares,
address receiver,
bool autoClaim
) external returns (uint256 requestId);

Parameters:

  • vault: The ERC-7540 vault address
  • assets: Amount of underlying tokens to deposit
  • minShares: Minimum shares to receive (slippage protection)
  • receiver: Address to receive the vault shares
  • autoClaim: Enable automatic claim when fulfilled (0.1% fee)

Example:

// Deposit 1000 NODR with auto-claim enabledawait vaultRouter.requestDeposit(
conservativeVaultAddress,
parseUnits('1000', 18),
parseUnits('990', 18),  // Accept up to 1% slippage
userAddress,
true// Enable auto-claim
);

Batch Deposit

function batchDeposit(
BatchDepositParams[] calldata params,
address receiver,
bool autoClaim
) external;
struct BatchDepositParams {
address vault;
uint256 assets;
uint256 minShares;
}

Deposit to multiple vaults in a single transaction, saving 50-70% on gas costs.

Example:

// Deposit to 3 vaults at onceawait vaultRouter.batchDeposit([
{ vault: conservativeVault, assets: parseUnits('500', 18), minShares: parseUnits('495', 18) },
{ vault: moderateVault, assets: parseUnits('300', 18), minShares: parseUnits('297', 18) },
{ vault: aggressiveVault, assets: parseUnits('200', 18), minShares: parseUnits('196', 18) }
], userAddress, true);

Gasless Deposit (EIP-712)

function depositWithPermit(
address vault,
uint256 assets,
uint256 minShares,
address receiver,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external returns (uint256 requestId);

Users sign a message offchain; relayers submit the transaction and pay gas.

Fee Structure

ServiceFeeDescription
Standard Request0%No fee for manual claim
Auto-Claim0.1%Deducted from claimed amount
Gasless2%+Covers gas + relayer profit

Configuration

ParameterDefaultDescription
defaultTimeout1 hourRequest expiration time
autoClaimFeeBps10 (0.1%)Fee for auto-claim service

AutoClaimKeeper

The AutoClaimKeeper enables validator nodes to earn rewards by executing auto-claims for users. It integrates with TrustFingerprint™ for reputation-based reward scaling.

Key Features

  • Validator Registration: Stake NODR to become a keeper
  • TrustFingerprint™ Integration: Higher trust = higher rewards
  • Batch Execution: Process multiple claims efficiently
  • Performance Tracking: Success rate, gas usage, rewards
  • Slashing Mechanism: Penalties for failed executions

Becoming a Keeper

function registerKeeper() external;

Requirements:

  • Minimum stake: 1,000 NODR
  • Minimum TrustFingerprint™: 0.50

Executing Claims

// Execute single deposit claim
function executeDepositClaim(
address user,
address vault,
address receiver
) external;
// Execute single redeem claim
function executeRedeemClaim(
address user,
address vault,
address receiver
) external;
// Batch execute multiple claims
function batchExecuteClaims(
ClaimParams[] calldata deposits,
ClaimParams[] calldata redeems
) external;

Reward Calculation

Base Reward = claimValue × baseRewardBps / 10000
Trust Bonus = Base Reward × (trustScore / 100) × trustBonusBps / 10000
Total Reward = Base Reward + Trust Bonus

Example:

  • Claim value: 10,000 NODR
  • Base reward rate: 0.1%
  • TrustFingerprint™: 0.85
  • Trust bonus rate: 0.05%
Base Reward = 10,000 × 0.001 = 10 NODR
Trust Bonus = 10 × 0.85 × 0.0005 = 0.00425 NODR
Total Reward = 10.00425 NODR

Keeper Metrics

struct KeeperMetrics {
uint256 totalClaims;      // Total claims executed
uint256 successfulClaims; // Successful executions
uint256 failedClaims;     // Failed executions
uint256 totalGasUsed;     // Cumulative gas usage
uint256 totalRewards;     // Cumulative rewards earned
uint256 lastClaimTime;    // Timestamp of last claim
}

Slashing Conditions

ConditionPenalty
Failed execution0.1% of stake
>10% failure rateTemporary suspension
Malicious behaviorFull stake slash

InstantLiquidityPool

The InstantLiquidityPool provides immediate deposit/withdrawal execution for users who prefer speed over cost. LPs earn fees from impatient users.

Key Features

  • Instant Deposits: Skip the 1-2 hour wait
  • Instant Redemptions: Immediate asset return
  • Dynamic Pricing: 2-5% fee based on utilization
  • LP Rewards: Earn fees by providing liquidity
  • Automated Rebalancing: Pool maintains optimal levels

How It Works

Standard Flow (1-2 hours):
User → requestDeposit → [wait] → claimDeposit → Shares
Instant Flow (immediate):
User → instantDeposit → Shares (pay 2-5% premium)

For Users: Instant Operations

Instant Deposit

function instantDeposit(
address vault,
uint256 assets,
uint256 minShares,
address receiver
) external returns (uint256 shares);

Example:

// Instant deposit with 3% feeconst shares = await instantLiquidityPool.instantDeposit(
conservativeVaultAddress,
parseUnits('1000', 18),
parseUnits('950', 18),  // Accept up to 5% slippage (includes fee)
userAddress
);

Instant Redeem

function instantRedeem(
address vault,
uint256 shares,
uint256 minAssets,
address receiver
) external returns (uint256 assets);

For LPs: Providing Liquidity

Add Liquidity

function addLiquidity(
address vault,
uint256 assets,
uint256 shares
) external;

LPs can provide:

  • Asset liquidity: NODR tokens for instant deposits
  • Share liquidity: Vault shares for instant redemptions

Remove Liquidity

function removeLiquidity(
address vault,
uint256 assets,
uint256 shares
) external;

Claim Fees

function claimFees(address vault) external;

Fee Structure

UtilizationFee
0-20%2.0%
20-40%2.5%
40-60%3.0%
60-80%3.5%
80-100%5.0%

Fee Distribution:

  • 90% to LPs (proportional to stake)
  • 10% to protocol treasury

LP Economics

Expected Returns:

  • Low utilization: 20-50% APY
  • Medium utilization: 50-100% APY
  • High utilization: 100-150% APY

Example:

  • LP provides 10,000 NODR liquidity
  • Pool processes 100,000 NODR/day at 3% average fee
  • Daily fees: 3,000 NODR
  • LP share (10% of pool): 300 NODR/day
  • Annual return: 300 × 365 = 109,500 NODR (1,095% APY)

Note: Returns vary based on utilization and competition.


Integration Guide

1. Check instant liquidity availability
2. If available and user prefers speed → Use InstantLiquidityPool
3. If user prefers cost savings → Use VaultRouter with auto-claim
4. If user wants full control → Use VaultRouter without auto-claim

TypeScript Integration

import { parseUnits } from'viem';
// Option 1: Instant deposit (immediate, 2-5% fee)const instantShares = await instantLiquidityPool.instantDeposit(
vaultAddress,
parseUnits('1000', 18),
parseUnits('950', 18),
userAddress
);
// Option 2: Auto-claim deposit (1-2 hours, 0.1% fee)await vaultRouter.requestDeposit(
vaultAddress,
parseUnits('1000', 18),
parseUnits('990', 18),
userAddress,
true// auto-claim
);
// Option 3: Manual deposit (1-2 hours, 0% fee)await vaultRouter.requestDeposit(
vaultAddress,
parseUnits('1000', 18),
parseUnits('990', 18),
userAddress,
false// manual claim
);
// Later: manually claimconst shares = await vaultRouter.claimDeposit(vaultAddress, userAddress);

Solidity Integration

import "./interfaces/IVaultRouter.sol";
import "./interfaces/IInstantLiquidityPool.sol";
contract MyProtocol {
IVaultRouter public vaultRouter;
IInstantLiquidityPool public instantPool;
function smartDeposit(
address vault,
uint256 amount,
bool preferSpeed
) external {
if (preferSpeed && instantPool.hasLiquidity(vault, amount)) {
// Instant deposit
instantPool.instantDeposit(vault, amount, 0, msg.sender);
} else {
// Auto-claim deposit
vaultRouter.requestDeposit(vault, amount, 0, msg.sender, true);
}
}
}

Deployed Contracts

ContractAddressNetwork
VaultRouterPending DeploymentBase Sepolia
AutoClaimKeeperPending DeploymentBase Sepolia
InstantLiquidityPoolPending DeploymentBase Sepolia

Security Considerations

Access Control

ContractRolePermissions
VaultRouterOPERATOR_ROLEExecute auto-claims
VaultRouterPAUSER_ROLEEmergency pause
AutoClaimKeeperKEEPER_ROLEExecute claims
InstantLiquidityPoolREBALANCER_ROLETrigger rebalancing

Emergency Functions

All contracts implement:

  • pause() / unpause() - Halt operations
  • emergencyWithdraw() - Admin asset recovery
  • UUPS upgradeable pattern for security patches

Audit Status

These contracts are pending third-party security audit. Use on testnet only until audits are complete.



Last Updated: December 28, 2025

Version: 1.0

results matching ""

    No results matching ""