Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unsubscribe on transfer #395

Merged
merged 3 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_subscribe.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
88168
88124
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_unsubscribe.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
63080
63036
2 changes: 1 addition & 1 deletion .forge-snapshots/positionDescriptor bytecode size.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
31687
31806
2 changes: 1 addition & 1 deletion src/PositionManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ contract PositionManager is
/// @dev will revert if pool manager is locked
function transferFrom(address from, address to, uint256 id) public virtual override onlyIfPoolManagerLocked {
super.transferFrom(from, to, id);
if (positionInfo[id].hasSubscriber()) _notifyTransfer(id, from, to);
if (positionInfo[id].hasSubscriber()) _unsubscribe(id);
}

/// @inheritdoc IPositionManager
Expand Down
13 changes: 0 additions & 13 deletions src/base/Notifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -103,19 +103,6 @@ abstract contract Notifier is INotifier {
}
}

function _notifyTransfer(uint256 tokenId, address previousOwner, address newOwner) internal {
ISubscriber _subscriber = subscriber[tokenId];

bool success =
_call(address(_subscriber), abi.encodeCall(ISubscriber.notifyTransfer, (tokenId, previousOwner, newOwner)));

if (!success) {
address(_subscriber).bubbleUpAndRevertWith(
ISubscriber.notifyTransfer.selector, TransferNotificationReverted.selector
);
}
}

function _call(address target, bytes memory encodedCall) internal returns (bool success) {
if (target.code.length == 0) NoCodeSubscriber.selector.revertWith();
assembly ("memory-safe") {
Expand Down
4 changes: 0 additions & 4 deletions src/interfaces/ISubscriber.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,4 @@ interface ISubscriber {
/// @param liquidityChange the change in liquidity on the underlying position
/// @param feesAccrued the fees to be collected from the position as a result of the modifyLiquidity call
function notifyModifyLiquidity(uint256 tokenId, int256 liquidityChange, BalanceDelta feesAccrued) external;
/// @param tokenId the token ID of the position
/// @param previousOwner address of the old owner
/// @param newOwner address of the new owner
function notifyTransfer(uint256 tokenId, address previousOwner, address newOwner) external;
}
9 changes: 0 additions & 9 deletions test/mocks/MockBadSubscribers.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ contract MockReturnDataSubscriber is ISubscriber {
uint256 public notifySubscribeCount;
uint256 public notifyUnsubscribeCount;
uint256 public notifyModifyLiquidityCount;
uint256 public notifyTransferCount;

error NotAuthorizedNotifer(address sender);

Expand Down Expand Up @@ -48,10 +47,6 @@ contract MockReturnDataSubscriber is ISubscriber {
notifyModifyLiquidityCount++;
}

function notifyTransfer(uint256, address, address) external onlyByPosm {
notifyTransferCount++;
}

function setReturnDataSize(uint256 _value) external {
memPtr = _value;
}
Expand Down Expand Up @@ -90,10 +85,6 @@ contract MockRevertSubscriber is ISubscriber {
revert TestRevert("notifyModifyLiquidity");
}

function notifyTransfer(uint256, address, address) external view onlyByPosm {
revert TestRevert("notifyTransfer");
}

function setRevert(bool _shouldRevert) external {
shouldRevert = _shouldRevert;
}
Expand Down
5 changes: 0 additions & 5 deletions test/mocks/MockSubscriber.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ contract MockSubscriber is ISubscriber {
uint256 public notifySubscribeCount;
uint256 public notifyUnsubscribeCount;
uint256 public notifyModifyLiquidityCount;
uint256 public notifyTransferCount;
int256 public liquidityChange;
BalanceDelta public feesAccrued;

Expand Down Expand Up @@ -45,8 +44,4 @@ contract MockSubscriber is ISubscriber {
liquidityChange = _liquidityChange;
feesAccrued = _feesAccrued;
}

function notifyTransfer(uint256, address, address) external onlyByPosm {
notifyTransferCount++;
}
}
101 changes: 22 additions & 79 deletions test/position-managers/PositionManager.notifier.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot {
assertEq(int256(sub.feesAccrued().amount1()), int256(feeRevenue1) - 1 wei);
}

function test_notifyTransfer_withTransferFrom_succeeds() public {
function test_transferFrom_unsubscribes() public {
uint256 tokenId = lpm.nextTokenId();
mint(config, 100e18, alice, ZERO_BYTES);

Expand All @@ -231,10 +231,12 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot {

lpm.transferFrom(alice, bob, tokenId);

assertEq(sub.notifyTransferCount(), 1);
assertEq(sub.notifyUnsubscribeCount(), 1);
assertEq(lpm.positionInfo(tokenId).hasSubscriber(), false);
assertEq(address(lpm.subscriber(tokenId)), address(0));
}

function test_notifyTransfer_withTransferFrom_selfDestruct_revert() public {
function test_transferFrom_unsubscribes_selfDestruct() public {
uint256 tokenId = lpm.nextTokenId();
mint(config, 100e18, alice, ZERO_BYTES);

Expand All @@ -250,11 +252,14 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot {
// simulate selfdestruct by etching the bytecode to 0
vm.etch(address(sub), ZERO_BYTES);

vm.expectRevert(INotifier.NoCodeSubscriber.selector);
// unsubscribe happens anyway
lpm.transferFrom(alice, bob, tokenId);

assertEq(lpm.positionInfo(tokenId).hasSubscriber(), false);
assertEq(address(lpm.subscriber(tokenId)), address(0));
}

function test_notifyTransfer_withSafeTransferFrom_succeeds() public {
function test_safeTransferFrom_unsubscribes() public {
uint256 tokenId = lpm.nextTokenId();
mint(config, 100e18, alice, ZERO_BYTES);

Expand All @@ -270,10 +275,12 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot {

lpm.safeTransferFrom(alice, bob, tokenId);

assertEq(sub.notifyTransferCount(), 1);
assertEq(sub.notifyUnsubscribeCount(), 1);
assertEq(lpm.positionInfo(tokenId).hasSubscriber(), false);
assertEq(address(lpm.subscriber(tokenId)), address(0));
}

function test_notifyTransfer_withSafeTransferFrom_selfDestruct_revert() public {
function test_safeTransferFrom_unsubscribes_selfDestruct() public {
uint256 tokenId = lpm.nextTokenId();
mint(config, 100e18, alice, ZERO_BYTES);

Expand All @@ -289,11 +296,14 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot {
// simulate selfdestruct by etching the bytecode to 0
vm.etch(address(sub), ZERO_BYTES);

vm.expectRevert(INotifier.NoCodeSubscriber.selector);
// unsubscribe happens anyway
lpm.safeTransferFrom(alice, bob, tokenId);

assertEq(lpm.positionInfo(tokenId).hasSubscriber(), false);
assertEq(address(lpm.subscriber(tokenId)), address(0));
}

function test_notifyTransfer_withSafeTransferFromData_succeeds() public {
function test_safeTransferFrom_unsubscribes_withData() public {
uint256 tokenId = lpm.nextTokenId();
mint(config, 100e18, alice, ZERO_BYTES);

Expand All @@ -309,7 +319,9 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot {

lpm.safeTransferFrom(alice, bob, tokenId, "");

assertEq(sub.notifyTransferCount(), 1);
assertEq(sub.notifyUnsubscribeCount(), 1);
assertEq(lpm.positionInfo(tokenId).hasSubscriber(), false);
assertEq(address(lpm.subscriber(tokenId)), address(0));
}

function test_unsubscribe_succeeds() public {
Expand Down Expand Up @@ -554,75 +566,6 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot {
lpm.modifyLiquidities(calls, _deadline);
}

function test_notifyTransfer_withTransferFrom_wraps_revert() public {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unsubscribe cannot revert, so these tests are no longer relevant

uint256 tokenId = lpm.nextTokenId();
mint(config, 100e18, alice, ZERO_BYTES);

// approve this contract to operate on alices liq
vm.startPrank(alice);
lpm.approve(address(this), tokenId);
vm.stopPrank();

lpm.subscribe(tokenId, address(revertSubscriber), ZERO_BYTES);

vm.expectRevert(
abi.encodeWithSelector(
CustomRevert.WrappedError.selector,
address(revertSubscriber),
ISubscriber.notifyTransfer.selector,
abi.encodeWithSelector(MockRevertSubscriber.TestRevert.selector, "notifyTransfer"),
abi.encodeWithSelector(INotifier.TransferNotificationReverted.selector)
)
);
lpm.transferFrom(alice, bob, tokenId);
}

function test_notifyTransfer_withSafeTransferFrom_wraps_revert() public {
uint256 tokenId = lpm.nextTokenId();
mint(config, 100e18, alice, ZERO_BYTES);

// approve this contract to operate on alices liq
vm.startPrank(alice);
lpm.approve(address(this), tokenId);
vm.stopPrank();

lpm.subscribe(tokenId, address(revertSubscriber), ZERO_BYTES);

vm.expectRevert(
abi.encodeWithSelector(
CustomRevert.WrappedError.selector,
address(revertSubscriber),
ISubscriber.notifyTransfer.selector,
abi.encodeWithSelector(MockRevertSubscriber.TestRevert.selector, "notifyTransfer"),
abi.encodeWithSelector(INotifier.TransferNotificationReverted.selector)
)
);
lpm.safeTransferFrom(alice, bob, tokenId);
}

function test_notifyTransfer_withSafeTransferFromData_wraps_revert() public {
uint256 tokenId = lpm.nextTokenId();
mint(config, 100e18, alice, ZERO_BYTES);

// approve this contract to operate on alices liq
vm.startPrank(alice);
lpm.approve(address(this), tokenId);
vm.stopPrank();

lpm.subscribe(tokenId, address(revertSubscriber), ZERO_BYTES);

vm.expectRevert(
abi.encodeWithSelector(
CustomRevert.WrappedError.selector,
address(revertSubscriber),
ISubscriber.notifyTransfer.selector,
abi.encodeWithSelector(MockRevertSubscriber.TestRevert.selector, "notifyTransfer"),
abi.encodeWithSelector(INotifier.TransferNotificationReverted.selector)
)
);
lpm.safeTransferFrom(alice, bob, tokenId, "");
}

/// @notice burning a position will automatically notify unsubscribe
function test_burn_unsubscribe() public {
uint256 tokenId = lpm.nextTokenId();
Expand Down
Loading