HOOKHOOD CONCEPT

Liquidity Passport Hook

Reputation for liquidity providers.

2ACTIVE CALLBACKS

Records experimental LP participation data supplied through hookData without moving funds or claiming identity assurance.

liquidityreputation
HOOK DNA

Callback permissions

Lifecycle

beforeInitialize○ INACTIVE
afterInitialize○ INACTIVE
beforeAddLiquidity○ INACTIVE
afterAddLiquidity● ACTIVE
beforeRemoveLiquidity○ INACTIVE
afterRemoveLiquidity● ACTIVE
beforeSwap○ INACTIVE
afterSwap○ INACTIVE
beforeDonate○ INACTIVE
afterDonate○ INACTIVE

Advanced accounting

beforeSwapReturnDelta○ INACTIVE
afterSwapReturnDelta○ INACTIVE
afterAddLiquidityReturnDelta○ INACTIVE
afterRemoveLiquidityReturnDelta○ INACTIVE
MECHANISM

Read the control surface.

POOL ACTIONafterAddLiquidity + afterRemoveLiquiditySIGNATURE LAB POLICYPOOLMANAGER ACCOUNTING
SOLIDITY

LiquidityPassportHook.sol

REFERENCE IMPLEMENTATION. Requires a correctly mined permission address. Compilation and tests are separate status signals.
LiquidityPassportHook.solSOLIDITY 0.8.26
001// SPDX-License-Identifier: MIT002pragma solidity ^0.8.26;003004import {BaseHook} from "@openzeppelin/uniswap-hooks/src/base/BaseHook.sol";005import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol";006import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";007import {ModifyLiquidityParams} from "@uniswap/v4-core/src/types/PoolOperation.sol";008import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";009import {PoolId, PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol";010import {BalanceDelta, BalanceDeltaLibrary} from "@uniswap/v4-core/src/types/BalanceDelta.sol";011012/// @notice Experimental participation score. Not KYC, identity verification, or credit scoring.013contract LiquidityPassportHook is BaseHook {014    using PoolIdLibrary for PoolKey;015    error InvalidHookData();016    error ZeroUser();017    error UnverifiedRouter(address sender);018    uint256 public constant MAX_SCORE = 1_000;019    uint256 public constant MAX_AGE_SCORE = 400;020    uint256 public constant MAX_ACTIVITY_SCORE = 250;021    uint256 public constant AGE_POINTS_PER_DAY = 4;022    uint256 public constant ADD_POINTS = 25;023    uint256 public constant ACTIVE_POINTS = 250;024    uint256 public constant REMOVE_PENALTY = 15;025    address public immutable verifiedRouter;026027    struct Passport {028        uint64 firstSeen;029        uint64 lastAction;030        uint32 additions;031        uint32 removals;032        uint256 activeLiquidity;033        bool initialized;034    }035    mapping(PoolId => mapping(address => Passport)) private _passports;036    event PassportCreated(PoolId indexed poolId, address indexed user, uint64 firstSeen);037    event LiquidityAddedToPassport(038        PoolId indexed poolId, address indexed user, uint256 amount, uint256 activeLiquidity039    );040    event LiquidityRemovedFromPassport(041        PoolId indexed poolId, address indexed user, uint256 amount, uint256 activeLiquidity042    );043    event PassportScoreChanged(PoolId indexed poolId, address indexed user, uint256 score);044045    constructor(IPoolManager manager, address router) BaseHook(manager) {046        verifiedRouter = router;047    }048049    function getHookPermissions() public pure override returns (Hooks.Permissions memory) {050        return Hooks.Permissions(051            false, false, false, true, false, true, false, false, false, false, false, false, false, false052        );053    }054055    function _afterAddLiquidity(056        address sender,057        PoolKey calldata key,058        ModifyLiquidityParams calldata params,059        BalanceDelta,060        BalanceDelta,061        bytes calldata hookData062    ) internal override returns (bytes4, BalanceDelta) {063        _authorize(sender);064        address user = _parseUser(hookData);065        if (params.liquidityDelta <= 0) return (BaseHook.afterAddLiquidity.selector, BalanceDeltaLibrary.ZERO_DELTA);066        PoolId id = key.toId();067        Passport storage p = _initialize(id, user);068        uint256 amount = uint256(params.liquidityDelta);069        p.lastAction = uint64(block.timestamp);070        p.additions += 1;071        p.activeLiquidity += amount;072        emit LiquidityAddedToPassport(id, user, amount, p.activeLiquidity);073        emit PassportScoreChanged(id, user, _score(p));074        return (BaseHook.afterAddLiquidity.selector, BalanceDeltaLibrary.ZERO_DELTA);075    }076077    function _afterRemoveLiquidity(078        address sender,079        PoolKey calldata key,080        ModifyLiquidityParams calldata params,081        BalanceDelta,082        BalanceDelta,083        bytes calldata hookData084    ) internal override returns (bytes4, BalanceDelta) {085        _authorize(sender);086        address user = _parseUser(hookData);087        PoolId id = key.toId();088        Passport storage p = _initialize(id, user);089        uint256 amount = _abs(params.liquidityDelta);090        p.lastAction = uint64(block.timestamp);091        p.removals += 1;092        p.activeLiquidity = amount >= p.activeLiquidity ? 0 : p.activeLiquidity - amount;093        emit LiquidityRemovedFromPassport(id, user, amount, p.activeLiquidity);094        emit PassportScoreChanged(id, user, _score(p));095        return (BaseHook.afterRemoveLiquidity.selector, BalanceDeltaLibrary.ZERO_DELTA);096    }097098    function _authorize(address sender) internal view {099        if (verifiedRouter != address(0) && sender != verifiedRouter) revert UnverifiedRouter(sender);100    }101102    function _parseUser(bytes calldata data) internal pure returns (address user) {103        if (data.length != 32) revert InvalidHookData();104        user = abi.decode(data, (address));105        if (user == address(0)) revert ZeroUser();106    }107108    function _initialize(PoolId id, address user) internal returns (Passport storage p) {109        p = _passports[id][user];110        if (!p.initialized) {111            p.initialized = true;112            p.firstSeen = uint64(block.timestamp);113            emit PassportCreated(id, user, p.firstSeen);114        }115    }116117    function _score(Passport memory p) internal view returns (uint256) {118        if (!p.initialized) return 0;119        uint256 ageDays = (block.timestamp - p.firstSeen) / 1 days;120        uint256 age = ageDays * AGE_POINTS_PER_DAY;121        if (age > MAX_AGE_SCORE) age = MAX_AGE_SCORE;122        uint256 activity = uint256(p.additions) * ADD_POINTS;123        if (activity > MAX_ACTIVITY_SCORE) activity = MAX_ACTIVITY_SCORE;124        uint256 score = age + activity + (p.activeLiquidity > 0 ? ACTIVE_POINTS : 0);125        uint256 penalty = uint256(p.removals) * REMOVE_PENALTY;126        score = penalty >= score ? 0 : score - penalty;127        return score > MAX_SCORE ? MAX_SCORE : score;128    }129130    function _abs(int256 value) internal pure returns (uint256) {131        if (value >= 0) return uint256(value);132        unchecked {133            return uint256(-(value + 1)) + 1;134        }135    }136137    function getPassport(PoolId id, address user) external view returns (Passport memory) {138        return _passports[id][user];139    }140141    function passportScore(PoolId id, address user) public view returns (uint256) {142        return _score(_passports[id][user]);143    }144145    function liquidityAge(PoolId id, address user) external view returns (uint256) {146        Passport memory p = _passports[id][user];147        return p.initialized ? block.timestamp - p.firstSeen : 0;148    }149150    function tier(PoolId id, address user) external view returns (string memory) {151        uint256 s = passportScore(id, user);152        if (s < 200) return "ROOKIE";153        if (s < 400) return "BRONZE";154        if (s < 600) return "SILVER";155        if (s < 800) return "GOLD";156        return "DIAMOND";157    }158}159
DEVELOPMENT STATUS

Evidence, not implication.

concept● PASS
reference Code● PASS
compile● PASS
unit Tests● PASS
fuzz Tests○ NOT VERIFIED
invariant Tests○ NOT VERIFIED
fork Tested○ NOT VERIFIED
external Audit○ NOT VERIFIED
testnet Deployment○ NOT VERIFIED
mainnet Deployment○ NOT VERIFIED
DEPLOYMENTS

Onchain records

NO MAINNET DEPLOYMENT. No testnet deployment metadata has been exported.
SECURITY

Trust no badge blindly.

A compiled contract is not necessarily safe. Tests are not an audit. A verified source can still contain exploitable logic.

  • Review permission bits and constructor configuration.
  • Model custom accounting and rounding independently.
  • Require fork, testnet, independent review, and audit evidence before real assets.
CHANGELOG

Reference history

2026-08-14 — Initial HookHood registry entry. No deployed revisions recorded.