Skip to content

Commit

Permalink
refactor: load owner via method
Browse files Browse the repository at this point in the history
build: bump prb-proxy
docs: improve writing in NatSpec comments
refactor: remove storage imports
test: update fork block number and addresses
test: update tests according to new contract logic
  • Loading branch information
PaulRBerg committed Jun 30, 2023
1 parent f859815 commit e55e985
Show file tree
Hide file tree
Showing 11 changed files with 42 additions and 51 deletions.
2 changes: 1 addition & 1 deletion lib/prb-proxy
Submodule prb-proxy updated 108 files
8 changes: 4 additions & 4 deletions src/SablierV2ProxyPlugin.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pragma solidity >=0.8.19;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { PRBProxyPlugin } from "@prb/proxy/abstracts/PRBProxyPlugin.sol";
import { IPRBProxy } from "@prb/proxy/interfaces/IPRBProxy.sol";
import { IPRBProxyPlugin } from "@prb/proxy/interfaces/IPRBProxyPlugin.sol";
import { ISablierV2Lockup } from "@sablier/v2-core/interfaces/ISablierV2Lockup.sol";
import { ISablierV2LockupSender } from "@sablier/v2-core/interfaces/hooks/ISablierV2LockupSender.sol";
Expand Down Expand Up @@ -35,8 +35,7 @@ import { Errors } from "./libraries/Errors.sol";
/// @notice See the documentation in {ISablierV2ProxyPlugin}.
contract SablierV2ProxyPlugin is
OnlyDelegateCall, // 0 inherited components
ISablierV2ProxyPlugin, // 3 inherited components
PRBProxyPlugin // 3 inherited components
ISablierV2ProxyPlugin // 2 inherited components
{
using SafeERC20 for IERC20;

Expand All @@ -60,7 +59,7 @@ contract SablierV2ProxyPlugin is
//////////////////////////////////////////////////////////////////////////*/

/// @inheritdoc IPRBProxyPlugin
function methodList() external pure returns (bytes4[] memory methods) {
function getMethods() external pure returns (bytes4[] memory methods) {
methods = new bytes4[](1);
methods[0] = this.onStreamCanceled.selector;
}
Expand Down Expand Up @@ -96,6 +95,7 @@ contract SablierV2ProxyPlugin is

// Effects: forward the refunded assets to the proxy owner.
IERC20 asset = lockup.getAsset(streamId);
address owner = IPRBProxy(address(this)).owner();
asset.safeTransfer({ to: owner, value: senderAmount });
}
}
22 changes: 13 additions & 9 deletions src/SablierV2ProxyTarget.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pragma solidity >=0.8.19;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { PRBProxyStorage } from "@prb/proxy/abstracts/PRBProxyStorage.sol";
import { IPRBProxy } from "@prb/proxy/interfaces/IPRBProxy.sol";
import { ISablierV2Lockup } from "@sablier/v2-core/interfaces/ISablierV2Lockup.sol";
import { ISablierV2LockupLinear } from "@sablier/v2-core/interfaces/ISablierV2LockupLinear.sol";
import { ISablierV2LockupDynamic } from "@sablier/v2-core/interfaces/ISablierV2LockupDynamic.sol";
Expand Down Expand Up @@ -38,8 +38,7 @@ import { Batch, Permit2Params } from "./types/DataTypes.sol";
/// @notice See the documentation in {ISablierV2ProxyTarget}.
contract SablierV2ProxyTarget is
ISablierV2ProxyTarget, // 0 inherited components
OnlyDelegateCall, // 0 inherited components
PRBProxyStorage // 1 inherited component
OnlyDelegateCall // 0 inherited components
{
using SafeERC20 for IERC20;

Expand Down Expand Up @@ -113,7 +112,7 @@ contract SablierV2ProxyTarget is
uint256 deltaBalance = finalBalance - initialBalance;

// Forward the delta to the proxy owner. This cannot be zero because settled streams cannot be canceled.
asset.safeTransfer({ to: owner, value: deltaBalance });
asset.safeTransfer({ to: _getOwner(), value: deltaBalance });
}

/// @inheritdoc ISablierV2ProxyTarget
Expand Down Expand Up @@ -650,7 +649,12 @@ contract SablierV2ProxyTarget is
}
}

/// @dev Shared logic between `cancelMultiple` and `batchCancelMultiple`.
/// @dev Helper function to retrieve the proxy's owner, which is stored as an immutable variable in the proxy.
function _getOwner() internal view returns (address) {
return IPRBProxy(address(this)).owner();
}

/// @dev Shared logic between {cancelMultiple} and {batchCancelMultiple}.
function _postMultipleCancellations(uint256[] memory initialBalances, IERC20[] calldata assets) internal {
uint256 assetCount = assets.length;
uint256 finalBalance;
Expand All @@ -661,7 +665,7 @@ contract SablierV2ProxyTarget is
deltaBalance = finalBalance - initialBalances[i];

// Forward the delta to the proxy owner. This cannot be zero because settled streams cannot be canceled.
assets[i].safeTransfer({ to: owner, value: deltaBalance });
assets[i].safeTransfer({ to: _getOwner(), value: deltaBalance });

// Increment the for loop iterator.
unchecked {
Expand Down Expand Up @@ -700,13 +704,13 @@ contract SablierV2ProxyTarget is
internal
{
// Retrieve the proxy owner.
address owner_ = owner;
address owner = _getOwner();

// Permit the proxy to spend funds from the proxy owner.
PERMIT2.permit({ owner: owner_, permitSingle: permit2Params.permitSingle, signature: permit2Params.signature });
PERMIT2.permit({ owner: owner, permitSingle: permit2Params.permitSingle, signature: permit2Params.signature });

// Transfer funds from the proxy owner to the proxy.
PERMIT2.transferFrom({ from: owner_, to: address(this), amount: amount, token: address(asset) });
PERMIT2.transferFrom({ from: owner, to: address(this), amount: amount, token: address(asset) });

// Approve the Sablier contract to spend funds.
_approve(sablierContract, asset, amount);
Expand Down
6 changes: 3 additions & 3 deletions src/interfaces/ISablierV2ProxyPlugin.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import { ISablierV2LockupSender } from "@sablier/v2-core/interfaces/hooks/ISabli
import { ISablierV2Archive } from "./ISablierV2Archive.sol";

/// @title ISablierV2ProxyPlugin
/// @notice Proxy plugin for forwarding the refunded assets to the proxy owner when the recipient cancels a stream
/// @notice Proxy plugin that forwards the refunded assets to the proxy owner when the recipient cancels a stream
/// whose sender is the proxy contract.
/// @dev Requirements:
/// - The call must not be a standard call.
/// - The call must be a delegate call.
/// - The caller must be Sablier.
interface ISablierV2ProxyPlugin is
ISablierV2LockupSender, // 0 inherited components
IPRBProxyPlugin // 1 inherited component
IPRBProxyPlugin // 0 inherited components
{
/// @notice Retrieves the address of the archive contract.
function archive() external view returns (ISablierV2Archive);
Expand Down
11 changes: 0 additions & 11 deletions test/Base.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ pragma solidity >=0.8.19 <0.9.0;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { IPRBProxy } from "@prb/proxy/interfaces/IPRBProxy.sol";
import { IPRBProxyAnnex } from "@prb/proxy/interfaces/IPRBProxyAnnex.sol";
import { IPRBProxyRegistry } from "@prb/proxy/interfaces/IPRBProxyRegistry.sol";
import { ISablierV2Lockup } from "@sablier/v2-core/interfaces/ISablierV2Lockup.sol";
import { ISablierV2LockupDynamic } from "@sablier/v2-core/interfaces/ISablierV2LockupDynamic.sol";
Expand Down Expand Up @@ -49,7 +48,6 @@ abstract contract Base_Test is Assertions, Events, StdCheats, V2CoreUtils {
ISablierV2LockupLinear internal lockupLinear;
IAllowanceTransfer internal permit2;
ISablierV2ProxyPlugin internal plugin;
IPRBProxyAnnex internal proxyAnnex;
IPRBProxyRegistry internal proxyRegistry;
ISablierV2ProxyTarget internal target;
IWrappedNativeAsset internal weth;
Expand Down Expand Up @@ -299,15 +297,6 @@ abstract contract Base_Test is Assertions, Events, StdCheats, V2CoreUtils {
vm.expectCall({ callee: asset_, count: count, data: abi.encodeCall(IERC20.transferFrom, (from, to, amount)) });
}

/*//////////////////////////////////////////////////////////////////////////
PLUGIN
//////////////////////////////////////////////////////////////////////////*/

function installPlugin() internal {
bytes memory data = abi.encodeCall(proxyAnnex.installPlugin, (plugin));
aliceProxy.execute(address(proxyAnnex), data);
}

/*//////////////////////////////////////////////////////////////////////////
TARGET
//////////////////////////////////////////////////////////////////////////*/
Expand Down
8 changes: 3 additions & 5 deletions test/fork/Fork.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ pragma solidity >=0.8.19 <0.9.0;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IPRBProxy } from "@prb/proxy/interfaces/IPRBProxy.sol";
import { IPRBProxyAnnex } from "@prb/proxy/interfaces/IPRBProxyAnnex.sol";
import { IPRBProxyRegistry } from "@prb/proxy/interfaces/IPRBProxyRegistry.sol";
import { ISablierV2LockupDynamic } from "@sablier/v2-core/interfaces/ISablierV2LockupDynamic.sol";
import { ISablierV2LockupLinear } from "@sablier/v2-core/interfaces/ISablierV2LockupLinear.sol";
Expand Down Expand Up @@ -38,7 +37,7 @@ abstract contract Fork_Test is Base_Test, V2CoreFuzzers {

function setUp() public virtual override {
// Fork the Goerli testnet.
vm.createSelectFork({ blockNumber: 9_093_100, urlOrAlias: "goerli" });
vm.createSelectFork({ blockNumber: 9_261_300, urlOrAlias: "goerli" });

// Set up the base test contract.
Base_Test.setUp();
Expand Down Expand Up @@ -85,8 +84,7 @@ abstract contract Fork_Test is Base_Test, V2CoreFuzzers {
/// @dev Loads all dependencies pre-deployed on Goerli.
function loadDependencies() private {
weth = IWrappedNativeAsset(WETH_ADDRESS);
proxyAnnex = IPRBProxyAnnex(0x0254C4467cBbdbe8d5E01e68de0DF7b20dD2A167);
proxyRegistry = IPRBProxyRegistry(0xa87bc4C1Bc54E1C1B28d2dD942A094A6B665B8C9);
proxyRegistry = IPRBProxyRegistry(0x33e200B5fb5e0C57d370d5202c26A35d07A46B98);
aliceProxy = loadOrDeployProxy(users.alice.addr);
permit2 = IAllowanceTransfer(0x000000000022D473030F116dDEE9F6B43aC78BA3);
lockupDynamic = ISablierV2LockupDynamic(0xB2CF57EdDEf081b97A4F2a02f5f6DF1271d0071E);
Expand All @@ -95,7 +93,7 @@ abstract contract Fork_Test is Base_Test, V2CoreFuzzers {

/// @dev Retrieves the proxy and deploys one if none is found.
function loadOrDeployProxy(address user) internal returns (IPRBProxy proxy) {
proxy = proxyRegistry.proxies(user);
proxy = proxyRegistry.getProxy({ owner: user });
if (address(proxy) == address(0)) {
proxy = proxyRegistry.deployFor(user);
}
Expand Down
2 changes: 1 addition & 1 deletion test/fork/plugin/onStreamCanceled.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ abstract contract OnStreamCanceled_Fork_Test is Fork_Test, PermitSignature {

function setUp() public virtual override {
Fork_Test.setUp();
installPlugin();
proxyRegistry.installPlugin(plugin);

if (!archive.isListed(address(lockupLinear))) {
address archiveAdmin = archive.admin();
Expand Down
2 changes: 1 addition & 1 deletion test/integration/Integration.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ abstract contract Integration_Test is Base_Test {
function deployDependencies() private {
weth = new WETH();
wlc = new WLC();
(proxyAnnex, proxyRegistry) = new PRBProxyPrecompiles().deploySystem();
proxyRegistry = new PRBProxyPrecompiles().deployRegistry();
aliceProxy = proxyRegistry.deployFor(users.alice.addr);
permit2 = IAllowanceTransfer(new DeployPermit2().run());
(, lockupDynamic, lockupLinear,) = new V2CorePrecompiles().deployCore(users.admin.addr);
Expand Down
15 changes: 15 additions & 0 deletions test/integration/plugin/get-methods/getMethods.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.19 <0.9.0;

import { SablierV2ProxyPlugin } from "src/SablierV2ProxyPlugin.sol";

import { Integration_Test } from "../../Integration.t.sol";

contract GetMethods_Integration_Test is Integration_Test {
function test_GetMethods() external {
bytes4[] memory actualMethods = plugin.getMethods();
bytes4[] memory expectedMethods = new bytes4[](1);
expectedMethods[0] = SablierV2ProxyPlugin.onStreamCanceled.selector;
assertEq(actualMethods, expectedMethods, "methods do not match");
}
}
15 changes: 0 additions & 15 deletions test/integration/plugin/method-list/methodList.t.sol

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ contract OnStreamCanceled_Integration_Test is Integration_Test {

function setUp() public virtual override {
Integration_Test.setUp();
installPlugin();
proxyRegistry.installPlugin(plugin);
streamId = createWithRange();

// List the {LockupLinear} contract in the archive.
Expand Down

0 comments on commit e55e985

Please sign in to comment.