A Uniswap V4 hook system for tokenized Real World Assets (RWA) that creates an infinite primary market with protected secondary market liquidity.
DobNodeLiquidity enables tokenization of revenue-generating assets (e.g., solar farms) with:
- Primary Market: Mint tokens at oracle NAV (99% to operator, 1% fee)
- Secondary Market: Redeem tokens at NAV minus dynamic penalty based on default risk
- Liquid Nodes: Permissionless liquidity providers offering instant redemption at competitive rates
- Price Stabilization: Automatic intervention when pool price deviates >5% from oracle NAV
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ DobOracle │◄────│ V4 Hook │────►│ DobToken │
│ (NAV/Risk) │ │ (Primary) │ │ (ERC20) │
└─────────────┘ └──────┬──────┘ └─────────────┘
│
▼
┌─────────────┐
│ LiquidNode │
│ (Secondary) │
└─────────────┘
- User sends USDC to the hook
- Hook reads current NAV from oracle
- 99% goes to operator, 1% fee
- DOB tokens minted to user at NAV rate
DOB received = (USDC × 0.99) / NAV
- User sends DOB tokens to redeem
- Hook calculates dynamic penalty based on default risk:
- Base penalty: 3%
- Risk adjustment: +risk/1000
- Maximum: 50%
- USDC returned minus penalty
Penalty BPS = min(300 + defaultRisk/10, 5000)
USDC received = DOB × NAV × (1 - penalty)
For large redemptions, Liquid Nodes compete to provide instant liquidity:
- LiquidNode queries oracle for current conditions
- Calculates fee based on risk tier:
- Low risk (<15%): 5% fee
- Medium risk (<30%): 10% fee
- High risk (≥30%): 20% fee
- User can choose best offer from competing nodes
The system includes automatic price stabilization to maintain DOB pool prices close to oracle NAV:
How It Works:
- After every swap, the hook compares pool price to oracle NAV
- If deviation exceeds 5%, the Liquid Node Stabilizer intervenes
- Price too low → Liquid Node buys DOB to support price
- Price too high → Liquid Node sells DOB to cap price
- Intervention amount is proportional to deviation size
- 0.5% fee collected on each intervention
Benefits:
- Prevents excessive price manipulation
- Protects users from large deviations
- Generates revenue for the Liquid Node
- Maintains market confidence
| Contract | Description |
|---|---|
DobOracle.sol |
Push oracle storing NAV and default risk |
DobToken.sol |
ERC20 with hook-only mint/burn |
DobNodeLiquidityHook.sol |
Uniswap V4 hook for primary market |
DobNodeLiquidityHookLocal.sol |
Local testing version (bypasses V4 address validation) |
LiquidNodeStabilizer.sol |
Automatic price stabilization mechanism |
LiquidNodeExample.sol |
Permissionless liquidity provider |
MockPoolManagerLocal.sol |
Simplified pool manager for local testing |
DobOracle
function nav() external view returns (uint256); // Current NAV (18 decimals)
function defaultRisk() external view returns (uint256); // Risk in basis points
function update(uint256 _nav, uint256 _risk) external; // Update valuesDobToken
function mint(address to, uint256 amount) external; // Hook only
function burnFrom(address from, uint256 amount) external; // Hook onlyLiquidNodeExample
function quoteFromOracle(uint256 rwaAmount) external view
returns (uint256 usdcProvided, uint256 feeBps);# Clone and enter project
cd dob-node-liquidity
# Install Foundry dependencies
forge install
# Install frontend dependencies
cd frontend
npm install
cd ..# Unit tests
forge test
# With verbosity
forge test -vvv
# Specific test
forge test --match-test testBuyCalculation -vvv- 18 unit tests: Oracle, Token, LiquidNode, Redemption calculations
- 3 E2E tests: Complete investment lifecycle with multiple investors
- 7 stabilization tests: Price stabilization mechanism verification
The simulation demonstrates the automatic price stabilization system through 8 scenarios:
# Terminal 1
anvil# Terminal 2
forge script script/DeploySimpleLocal.s.sol \
--tc DeploySimpleLocal \
--rpc-url http://localhost:8545 \
--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
--broadcastforge script script/SimulateStabilization.s.sol \
--tc SimulateStabilization \
--rpc-url http://localhost:8545 \
--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
--broadcast \
--gas-limit 30000000What the simulation shows:
- Small Swap - No stabilization (deviation < 5%)
- Large DOB Sale - Price drops, Liquid Node buys DOB
- Large DOB Buy - Price rises, Liquid Node sells DOB
- Oracle NAV Increase - NAV changes to $1.10
- Swap After NAV Change - Triggers stabilization
- Oracle NAV Decrease - NAV drops to $0.90
- Swap After NAV Drop - Stabilization intervention
- Multiple Rapid Swaps - Multiple interventions with fee accumulation
Expected output:
- Pool reserves and prices after each scenario
- Liquid Node balance changes
- Fees collected (starts at 0, accumulates to ~21,000 USDC)
- Stabilization triggers when deviation > 5%
To clean all build artifacts and start fresh:
# Clean Foundry artifacts
forge clean
# Clean frontend build
cd frontend
rm -rf node_modules dist
npm install
cd ..
# Clean all (nuclear option)
rm -rf out cache broadcast frontend/node_modules frontend/dist
forge install
cd frontend && npm install && cd ..# Terminal 1
anvil# Terminal 2
forge script script/DeployDob.s.sol \
--tc DeployDobLocal \
--rpc-url http://127.0.0.1:8545 \
--broadcast \
--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80Save the deployed addresses from the output.
Edit frontend/src/contracts.ts with deployed addresses:
export const CONTRACTS = {
oracle: '0x...deployed_oracle_address...',
dobToken: '0x...deployed_token_address...',
liquidNode: '0x...deployed_liquidnode_address...',
} as const;cd frontend
npm run devOpen http://localhost:5173 in your browser.
Import an Anvil test account into MetaMask:
- Private Key:
0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 - Network: Localhost 8545 (Chain ID: 31337)
- NAV: Current Net Asset Value with % change
- Risk: Default risk percentage with severity indicator
- Balance: Your DOB holdings and USD value
- Input USDC amount
- Shows DOB tokens to receive
- Displays operator allocation
- Input DOB amount (with Max button)
- Shows USDC to receive
- Displays current penalty rate
Oracle controls with preset scenarios:
- Launch: NAV $1.00, Risk 10%
- Revenue: NAV $1.15, Risk 7%
- Stress: NAV $0.85, Risk 35%
- Recovery: NAV $1.30, Risk 4%
dob-node-liquidity/
├── src/
│ ├── interfaces/
│ │ └── IDobOracle.sol
│ ├── mocks/
│ │ └── MockPoolManagerLocal.sol
│ ├── DobOracle.sol
│ ├── DobToken.sol
│ ├── MockUSDC.sol
│ ├── DobNodeLiquidityHook.sol
│ ├── DobNodeLiquidityHookLocal.sol # Local testing hook
│ ├── LocalBaseHook.sol # Base for local hooks
│ ├── LiquidNodeStabilizer.sol # Price stabilization
│ └── LiquidNodeExample.sol
├── test/
│ ├── DobNodeLiquidity.t.sol # Unit tests
│ ├── DobNodeLiquidity.e2e.t.sol # E2E tests
│ ├── LiquidNodeStabilizer.t.sol # Stabilizer tests
│ └── StabilizationE2E.t.sol # Stabilization E2E
├── script/
│ ├── DeployDob.s.sol # Production deploy
│ ├── DeploySimpleLocal.s.sol # Local deploy with stabilization
│ └── SimulateStabilization.s.sol # Simulation script
├── frontend/
│ ├── src/
│ │ ├── App.tsx
│ │ ├── main.tsx
│ │ ├── wagmi.ts
│ │ ├── contracts.ts
│ │ └── index.css
│ ├── package.json
│ └── vite.config.ts
├── foundry.toml
└── README.md
[profile.default]
src = "src"
out = "out"
libs = ["lib"]
solc = "0.8.26"
evm_version = "cancun"
via_ir = true
optimizer = true
optimizer_runs = 200The frontend supports:
- Localhost (Anvil)
- Base Sepolia (testnet)
- Base (mainnet)
Update frontend/src/wagmi.ts with your WalletConnect project ID for production.
The E2E test demonstrates a complete scenario:
- Launch - NAV $1.00, Risk 10%
- Initial Investment - Alice invests $10,000 → receives 9,900 DOB
- Revenue Period - NAV rises to $1.15, risk drops to 7%
- New Investor - Bob invests $5,000
- Profit Taking - Alice sells 2,000 DOB at profit
- Market Crash - NAV drops to $0.85, risk spikes to 35%
- Emergency Exit - Bob panic sells at high penalty
- Recovery - NAV recovers to $1.30, risk drops to 4%
- Final Exits - Remaining holders exit with gains
For mainnet deployment:
- Set
PRIVATE_KEYenvironment variable - Use
DeployDobcontract (notDeployDobLocal) - Mine correct hook address with CREATE2 for V4 permissions
- Update oracle updater address
- Configure real USDC and PoolManager addresses
export PRIVATE_KEY=your_private_key
forge script script/DeployDob.s.sol \
--tc DeployDob \
--rpc-url https://mainnet.base.org \
--broadcast \
--verifyThe price stabilization system uses these parameters:
| Parameter | Value | Description |
|---|---|---|
| Deviation Threshold | 5% (500 bps) | Triggers stabilization when exceeded |
| Intervention Fee | 0.5% (50 bps) | Fee collected by Liquid Node on interventions |
| Intervention Size | Proportional | (balance × deviation) / (BPS × 10) |
| Pool Fee | 0.3% (30 bps) | Standard Uniswap V4 pool fee |
| Direction | Bidirectional | Both buying and selling DOB |
Example Stabilization:
- Pool price: $0.92, NAV: $1.00 (8% deviation)
- Liquid Node has 50,000 USDC
- Intervention:
(50,000 × 800) / (10,000 × 10)= 400 USDC - Fee:
400 × 0.005= 2 USDC - Net intervention: 398 USDC used to buy DOB
- Oracle updater is trusted (single point of control)
- Hook has exclusive mint/burn permissions
- Penalty caps at 50% to prevent total loss
- LiquidNodes are permissionless but competitive
- Stabilization mechanism can be drained if NAV diverges significantly from market
- LocalBaseHook bypasses V4 address validation (for local testing only)
MIT