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

Reputation for liquidity providers.
Records experimental LP participation data supplied through hookData without moving funds or claiming identity assurance.
beforeInitialize○ INACTIVEafterInitialize○ INACTIVEbeforeAddLiquidity○ INACTIVEafterAddLiquidity● ACTIVEbeforeRemoveLiquidity○ INACTIVEafterRemoveLiquidity● ACTIVEbeforeSwap○ INACTIVEafterSwap○ INACTIVEbeforeDonate○ INACTIVEafterDonate○ INACTIVEbeforeSwapReturnDelta○ INACTIVEafterSwapReturnDelta○ INACTIVEafterAddLiquidityReturnDelta○ INACTIVEafterRemoveLiquidityReturnDelta○ INACTIVE001// 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 {014using PoolIdLibrary for PoolKey;015error InvalidHookData();016error ZeroUser();017error UnverifiedRouter(address sender);018uint256 public constant MAX_SCORE = 1_000;019uint256 public constant MAX_AGE_SCORE = 400;020uint256 public constant MAX_ACTIVITY_SCORE = 250;021uint256 public constant AGE_POINTS_PER_DAY = 4;022uint256 public constant ADD_POINTS = 25;023uint256 public constant ACTIVE_POINTS = 250;024uint256 public constant REMOVE_PENALTY = 15;025address public immutable verifiedRouter;026027struct Passport {028uint64 firstSeen;029uint64 lastAction;030uint32 additions;031uint32 removals;032uint256 activeLiquidity;033bool initialized;034}035mapping(PoolId => mapping(address => Passport)) private _passports;036event PassportCreated(PoolId indexed poolId, address indexed user, uint64 firstSeen);037event LiquidityAddedToPassport(038PoolId indexed poolId, address indexed user, uint256 amount, uint256 activeLiquidity039);040event LiquidityRemovedFromPassport(041PoolId indexed poolId, address indexed user, uint256 amount, uint256 activeLiquidity042);043event PassportScoreChanged(PoolId indexed poolId, address indexed user, uint256 score);044045constructor(IPoolManager manager, address router) BaseHook(manager) {046verifiedRouter = router;047}048049function getHookPermissions() public pure override returns (Hooks.Permissions memory) {050return Hooks.Permissions(051false, false, false, true, false, true, false, false, false, false, false, false, false, false052);053}054055function _afterAddLiquidity(056address sender,057PoolKey calldata key,058ModifyLiquidityParams calldata params,059BalanceDelta,060BalanceDelta,061bytes calldata hookData062) internal override returns (bytes4, BalanceDelta) {063_authorize(sender);064address user = _parseUser(hookData);065if (params.liquidityDelta <= 0) return (BaseHook.afterAddLiquidity.selector, BalanceDeltaLibrary.ZERO_DELTA);066PoolId id = key.toId();067Passport storage p = _initialize(id, user);068uint256 amount = uint256(params.liquidityDelta);069p.lastAction = uint64(block.timestamp);070p.additions += 1;071p.activeLiquidity += amount;072emit LiquidityAddedToPassport(id, user, amount, p.activeLiquidity);073emit PassportScoreChanged(id, user, _score(p));074return (BaseHook.afterAddLiquidity.selector, BalanceDeltaLibrary.ZERO_DELTA);075}076077function _afterRemoveLiquidity(078address sender,079PoolKey calldata key,080ModifyLiquidityParams calldata params,081BalanceDelta,082BalanceDelta,083bytes calldata hookData084) internal override returns (bytes4, BalanceDelta) {085_authorize(sender);086address user = _parseUser(hookData);087PoolId id = key.toId();088Passport storage p = _initialize(id, user);089uint256 amount = _abs(params.liquidityDelta);090p.lastAction = uint64(block.timestamp);091p.removals += 1;092p.activeLiquidity = amount >= p.activeLiquidity ? 0 : p.activeLiquidity - amount;093emit LiquidityRemovedFromPassport(id, user, amount, p.activeLiquidity);094emit PassportScoreChanged(id, user, _score(p));095return (BaseHook.afterRemoveLiquidity.selector, BalanceDeltaLibrary.ZERO_DELTA);096}097098function _authorize(address sender) internal view {099if (verifiedRouter != address(0) && sender != verifiedRouter) revert UnverifiedRouter(sender);100}101102function _parseUser(bytes calldata data) internal pure returns (address user) {103if (data.length != 32) revert InvalidHookData();104user = abi.decode(data, (address));105if (user == address(0)) revert ZeroUser();106}107108function _initialize(PoolId id, address user) internal returns (Passport storage p) {109p = _passports[id][user];110if (!p.initialized) {111p.initialized = true;112p.firstSeen = uint64(block.timestamp);113emit PassportCreated(id, user, p.firstSeen);114}115}116117function _score(Passport memory p) internal view returns (uint256) {118if (!p.initialized) return 0;119uint256 ageDays = (block.timestamp - p.firstSeen) / 1 days;120uint256 age = ageDays * AGE_POINTS_PER_DAY;121if (age > MAX_AGE_SCORE) age = MAX_AGE_SCORE;122uint256 activity = uint256(p.additions) * ADD_POINTS;123if (activity > MAX_ACTIVITY_SCORE) activity = MAX_ACTIVITY_SCORE;124uint256 score = age + activity + (p.activeLiquidity > 0 ? ACTIVE_POINTS : 0);125uint256 penalty = uint256(p.removals) * REMOVE_PENALTY;126score = penalty >= score ? 0 : score - penalty;127return score > MAX_SCORE ? MAX_SCORE : score;128}129130function _abs(int256 value) internal pure returns (uint256) {131if (value >= 0) return uint256(value);132unchecked {133return uint256(-(value + 1)) + 1;134}135}136137function getPassport(PoolId id, address user) external view returns (Passport memory) {138return _passports[id][user];139}140141function passportScore(PoolId id, address user) public view returns (uint256) {142return _score(_passports[id][user]);143}144145function liquidityAge(PoolId id, address user) external view returns (uint256) {146Passport memory p = _passports[id][user];147return p.initialized ? block.timestamp - p.firstSeen : 0;148}149150function tier(PoolId id, address user) external view returns (string memory) {151uint256 s = passportScore(id, user);152if (s < 200) return "ROOKIE";153if (s < 400) return "BRONZE";154if (s < 600) return "SILVER";155if (s < 800) return "GOLD";156return "DIAMOND";157}158}159
A compiled contract is not necessarily safe. Tests are not an audit. A verified source can still contain exploitable logic.