This is the Foundry project for Basememe’s Uniswap v4 hook module and the on-chain components it invokes during hook execution (fee collection, reward distribution, and LP position management).
Dependencies are managed as git submodules under lib/ (Foundry default). The first install requires network access.
- Foundry (
forge)
# Option A: clone with submodules
# git clone --recurse-submodules <repo>
# Option B: after cloning normally
./script/install-deps.sh
forge buildDeployment scripts read env vars. For convenience:
cp .env.example .env
set -a && source .env && set +a- Hook:
src/v4/BasememeHook.sol - Fee & liquidity manager:
src/v4/BasememeLpManager.sol - ETH rewards vault (used for WETH unwrap payouts):
src/rewards/ProtocolRewards.sol - Deployment scripts:
script/
BasememeHook enables the following Uniswap v4 hook permissions (see src/v4/BasememeHook.sol:getHookPermissions()):
beforeInitialize: prevents third parties from callingPoolManager.initialize(...)to create pools with this hook; initialization is only allowed when initiated by the hook itself viainitializePool(...)(which isonlyFactory).beforeAddLiquidity: gates liquidity provisioning until the token/pool is marked “graduated”.afterSwap: triggers fee collection and reward distribution via the configuredBasememeLpManager.
- Hook responsibilities:
- Pool creation helper:
initializePool(...)constructs aPoolKeyand callspoolManager.initialize(...). - Lifecycle gating:
beforeAddLiquidityreverts unless the pool is marked “graduated”. - Fee collection trigger:
afterSwapcalls the pool’s configuredBasememeLpManagerto collect/distribute fees.
- Pool creation helper:
- LP manager:
src/v4/BasememeLpManager.sol- Holds/mints v4-periphery
PositionManagerLP positions (owned by the contract). - Collects fees and (if needed) swaps them into the configured paired token.
- Distributes rewards to recipients and optionally via
ProtocolRewardsfor ETH payouts.
- Holds/mints v4-periphery
- Rewards vault:
src/rewards/ProtocolRewards.sol- Simple ETH accounting contract used when rewards are distributed as ETH (via WETH unwrap).
- Pool creation:
factory -> BasememeHook.initializePool(...) -> PoolManager.initialize(...) -> BasememeHook.beforeInitialize(...)
- Swap (fee collection):
PoolManager.swap(...) -> BasememeHook.afterSwap(...) -> BasememeLpManager.collectRewardsWithoutUnlock(...)
BasememeHook.initializePool(...)andBasememeHook.setTokenGraduated(...)are restricted byonlyFactory.- The
factoryaddress is an immutable set at hook deployment time. setTokenGraduatedis one-way: oncetrueit cannot be set back tofalse.
- The
BasememeHook.afterSwap(...):- Looks up
lpManagers[poolId]. If unset, it returns without side effects. - Decodes an optional
tradeReferralfromhookDataiffhookData.length == 32. - Calls
BasememeLpManager.collectRewardsWithoutUnlock(...).
- Looks up
BasememeLpManager.collectRewardsWithoutUnlock(...)is restricted byonlyHook(immutable hook address).
PoolManager.swap(...)triggersBasememeHook.afterSwap(...).BasememeHook.afterSwap(...)callsBasememeLpManager.collectRewardsWithoutUnlock(...).BasememeLpManagercollects fees from its positions viaPositionManager.modifyLiquiditiesWithoutUnlock(...).- If fee tokens are not already the paired token,
BasememeLpManagerswaps fees viaPoolManager.swap(...)and settles by paying the input token and taking the output token. - Rewards are distributed:
- If paired token is WETH: unwrap and deposit ETH into
ProtocolRewards.deposit(...). - Otherwise: ERC20 transfers directly to recipients.
- If paired token is WETH: unwrap and deposit ETH into
BasememeLpManageruses:ReentrancyGuardon its factory entrypoints.- An internal
_inCollectguard inside_collectRewards(...)to avoid recursive collection loops (important because the fee conversion swap can itself trigger hook execution).
Set env vars:
PRIVATE_KEY: deployer EOA private key (hex, no0xis ok)RPC_URL: JSON-RPC URL for the target chainPOOL_MANAGER: Uniswap v4PoolManageraddress on the target chainFACTORY: your factory address that is allowed to callinitializePool/setTokenGraduated
Run:
FOUNDRY_HOME=./.foundry forge script script/DeployBasememeHook.s.sol:DeployBasememeHook --rpc-url "$RPC_URL" --broadcastNotes:
BasememeHookinheritsBaseHook, which validates Uniswap v4 permission bits encoded in the deployed hook address.script/DeployBasememeHook.s.solmines a CREATE2 salt so the deployed address encodes the expected permissions.- The deploy script uses an intermediate
BasememeHookDeployercontract to deploy the hook with CREATE2 and then transfer ownership to your EOA. - After deploying, record addresses/tx hashes in
DEPLOYMENTS.md.
If you use WETH unwrap payouts, deploy ProtocolRewards (or point to an existing instance).
Set env vars:
PRIVATE_KEYRPC_URL
Run:
FOUNDRY_HOME=./.foundry forge script script/DeployProtocolRewards.s.sol:DeployProtocolRewards --rpc-url "$RPC_URL" --broadcastBasememeLpManager is called by the hook on the swap path to collect fees and distribute rewards.
Set env vars:
PRIVATE_KEYRPC_URLFACTORYHOOKPOOL_MANAGERPOSITION_MANAGERPERMIT2WETHPROTOCOL_REWARDS
Run:
FOUNDRY_HOME=./.foundry forge script script/DeployBasememeLpManager.s.sol:DeployBasememeLpManager --rpc-url "$RPC_URL" --broadcast- Format:
forge fmt - Tip: set
FOUNDRY_HOME=./.foundryto keep all Foundry cache/config local to this repo (useful for CI or restricted environments).