ERC1967Proxy
RobinhoodERC1967Proxy is a Uniswap v4 hook on Robinhood Chain. Runs code on every swap. Can take tokens from swaps. Gates who can add liquidity. Runs at pool creation.
- Contract name
- ERC1967Proxy
How this hook works
Trading taxA trading tax hook. It takes a cut of every trade, controls who adds liquidity and only lets approved callers create pools.
Takes a cut of every trade
On top of the normal pool fee, it keeps a share of each trade. The fee is paid out in the same trade.
The code that does this (buy):function _buy( Buy memory b_, HookContext memory c_, bool issues_, uint160 priceLimit_ ) private returns (BeforeSwapDelta) { PoolState storage pool_ = _s().pools[b_.id]; uint64 feeRate_ = SafeCast.toUint64( c_.hookFee + IdatMath.launchFeeRate(block.timestamp - pool_.graduatedAt, c_.launchFee, c_.launchFeeDuration) ); if (issues_) { _issue(b_, c_, pool_.watermark, feeRate_); if (b_.issueUsdg != 0 && _past(b_.usdgIsCurrency0, b_.endSqrtPrice, priceLimit_)) { revert PriceLimitInIssuance(); } } uint256 poolPart_ = b_.amountIn - b_.issueUsdg; b_.hookFee = FullMath.mulDiv(poolPart_, c_.hookFee, WAD); b_.launchFee = FullMath.mulDiv(poolPart_, feeRate_, WAD) - b_.hookFee; b_.issueFee = FullMath.mulDiv(b_.issueUsdg, c_.issueFee, WAD); (uint256 manager_, uint256 protocol_) = _splitHookFee(b_.hookFee, c_.hookFeeManagerShare, c_.hookFeeProtocolShare); uint256 issueFeeManager_ = FullMath.mulDiv(b_.issueFee, c_.issueFeeManagerShare, WAD); manager_ += issueFeeManager_; protocol_ += b_.issueFee - issueFeeManager_; uint256 taken_ = b_.issueUsdg + b_.hookFee + b_.launchFee; _distribute(address(b_.idat), taken_ - manager_ - protocol_, manager_, protocol_); // …Rules for adding liquidity
It decides who can add liquidity, or when.
The code that does this (beforeAddLiquidity):function beforeAddLiquidity( address sender_, PoolKey calldata, ModifyLiquidityParams calldata, bytes calldata ) external view onlyPoolManager returns (bytes4) { if (sender_ != _LIQUIDITY) revert LiquidityLocked(); return IHooks.beforeAddLiquidity.selector; }Only approved callers create pools
New pools with this hook can only be created by approved callers, for example its own factory or launcher.
The code that does this (beforeInitialize):function beforeInitialize( address sender_, PoolKey calldata key_, uint160 sqrtPriceX96_ ) external onlyPoolManager returns (bytes4) { if (sender_ != _LIQUIDITY) revert OnlyLiquidityCreatesPools(); Currency token_; if (key_.currency0 == _USDG) token_ = key_.currency1; else if (key_.currency1 == _USDG) token_ = key_.currency0; else revert OnlyLiquidityCreatesPools(); address idat_ = IIdatToken(Currency.unwrap(token_)).idat(); if (!IIdatFactory(_FACTORY).isIdat(idat_)) revert OnlyLiquidityCreatesPools(); HookStorage storage $ = _s(); if (PoolId.unwrap($.poolIdOf[idat_]) != 0) revert OnlyLiquidityCreatesPools(); PoolId id_ = key_.toId(); $.pools[id_] = PoolState({ idat: idat_, graduatedAt: uint64(block.timestamp), watermark: sqrtPriceX96_, lastCompoundAt: uint64(block.timestamp) }); $.poolIdOf[idat_] = id_; return IHooks.beforeInitialize.selector; }Simple trades only
Trades must state the amount going in, not the amount coming out.
The code that does this (beforeSwap):function beforeSwap( address sender_, PoolKey calldata key_, SwapParams calldata params_, bytes calldata ) external onlyPoolManager returns (bytes4, BeforeSwapDelta, uint24) { if (params_.amountSpecified >= 0) revert ExactOutputNotSupported(); Buy memory b_; b_.id = key_.toId(); b_.idat = IIdat(_s().pools[b_.id].idat); b_.usdgIsCurrency0 = key_.currency0 == _USDG; b_.token = Currency.unwrap(b_.usdgIsCurrency0 ? key_.currency1 : key_.currency0); b_.amountIn = uint256(-params_.amountSpecified); bool isBuy_ = params_.zeroForOne == b_.usdgIsCurrency0; HookContext memory c_ = b_.idat.hookContext(); BeforeSwapDelta delta_; if (c_.status == Status.ShutDown) { if (isBuy_) revert BuysClosed(); _tstore(_swapSlot(b_.id), 0); delta_ = _payout(b_, sender_, c_.payoutRate); } else { _compoundIfDue(b_); if (isBuy_) { delta_ = _buy(b_, c_, sender_ != address(b_.idat), params_.sqrtPriceLimitX96); } else { _tstore( _swapSlot(b_.id), _LIVE_FLAG | (uint256(c_.hookFee) << 128) | (uint256(c_.hookFeeManagerShare) << 64) | c_.hookFeeProtocolShare // …
Who controls it: The code can be replaced by the hook developer (it is upgradeable).
What do these labels mean? Based on the hook’s published code.
What it can do
Its code executes inside each trade in pools that use it.
It can take or add tokens during a swap. Fees, burns and custom curves work this way, and so do scams.
It checks every deposit before it goes in.
It runs when a pool is created, often to lock in that pool’s settings.
Hook history
- Handled a trade 10 times
Latest transactions
- 0xc46c42a9…88ea2c block 112,759,327
- 0x64b242b9…7f6218 block 112,759,326
- 0xfdd2c3ff…0ffdf5 block 112,758,967
- 0x4770c1b7…350b80 block 112,758,398
- 0x3a98207b…075c54 block 112,729,612
- 0xedef4ccc…fb9491 block 112,729,076
- 0xc2be75e3…2214a3 block 112,729,075
- 0x903f98bb…2582bf block 112,728,534
- 0x0c9e21fc…77c060 block 112,486,776
- 0xc8ddf3f4…57f84b block 112,486,769
- Paid out what was owed 4 times
Latest transactions
- 0xf6d61168…77ea31 block 112,760,581
- 0x03eaa009…393cbc block 112,731,161
- 0xb8b23213…9f3723 block 112,486,811
- 0xa00d183e…12608c block 112,486,804
Counted from what the hook recorded on-chain across all its pools, for every action in its published code, plus token transfers in and out of it.