Skip to main content

Context

For developers looking to support custom liquidity mining, Subscriber contracts can be used to receive notifications about position modifications or transfers.

Guide

1. Implement the ISubscriber interface

Can also refer to MockSubscriber for an actual implementation example.

import {ISubscriber} from "v4-periphery/src/interfaces/ISubscriber.sol";

contract MySubscriber is ISubscriber {
uint256 public notifySubscribeCount;
uint256 public notifyUnsubscribeCount;
uint256 public notifyModifyLiquidityCount;
uint256 public notifyBurnCount;
// other implementations...

function notifySubscribe(uint256, bytes memory) external onlyByPosm {
notifySubscribeCount++;
}

function notifyUnsubscribe(uint256) external onlyByPosm {
notifyUnsubscribeCount++;
}

function notifyModifyLiquidity(uint256, int256, BalanceDelta) external onlyByPosm {
notifyModifyLiquidityCount++;
}

function notifyBurn(uint256, address, PositionInfo, uint256, BalanceDelta)
external
onlyByPosm
{
notifyBurnCount++;
}
}

2. A caveat on unsubscribe()

To prevent gas griefing during unsubscription, Uniswap v4 sets a fixed variable unsubscribeGasLimit when calling a subscriber’s notifyUnsubscribe() function.

Without this limit, malicious subscribers could prevent liquidity providers from unsubscribing. If notifyUnsubscribe() were to consume too much gas, it would cause the unsubscription transaction to revert, thus leading to a denial-of-service

With the gas limit in place, if the subscriber’s notification fails, the unsubscription will still succeed and only the notification to the subscriber is skipped.

From _unsubscribe() on Notifier:

if (address(_subscriber).code.length > 0) {
// require that the remaining gas is sufficient to notify the subscriber
// otherwise, users can select a gas limit where .notifyUnsubscribe hits OutOfGas yet the
// transaction/unsubscription can still succee
if (gasleft() < unsubscribeGasLimit) GasLimitTooLow.selector.revertWith();
try _subscriber.notifyUnsubscribe{gas: unsubscribeGasLimit}(tokenId) {} catch {}
}

3. Opt-in to a subscriber contract

To opt-in to a subscriber contract, call subscribe() on PositionManager.

import {IPositionManager} from "v4-periphery/src/interfaces/IPositionManager.sol";

IPositionManager posm = IPositionManager(<address>);
ISubscriber mySubscriber = ISubscriber(<address>);

bytes memory optionalData = ...;
posm.subscribe(tokenId, mySubscriber, optionalData);