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

Checkout plugin and proxy #598

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 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
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@
[submodule "lib/dynamic-contracts"]
path = lib/dynamic-contracts
url = https://github.com/thirdweb-dev/dynamic-contracts
[submodule "lib/prb-proxy"]
path = lib/prb-proxy
url = https://github.com/PaulRBerg/prb-proxy
251 changes: 251 additions & 0 deletions contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.18;

Check warning

Code scanning / Slither

Different pragma directives are used Warning

Different versions of Solidity are used:
- Version used: ['>=0.8.18', '>=0.8.4', '^0.8.0', '^0.8.1', '^0.8.10', '^0.8.11', '^0.8.12', '^0.8.2', '^0.8.4', '^0.8.8', '^0.8.9']
- >=0.8.18
- >=0.8.18
- >=0.8.4
- >=0.8.4
- >=0.8.4
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- ^0.8.0
- [^0.8.0](contracts/extension/SignatureAction.sol#L2

import { IPRBProxy } from "@prb/proxy/src/interfaces/IPRBProxy.sol";
import { IPRBProxyPlugin } from "@prb/proxy/src/interfaces/IPRBProxyPlugin.sol";
import { IPRBProxyRegistry } from "@prb/proxy/src/interfaces/IPRBProxyRegistry.sol";
import { PRBProxy } from "@prb/proxy/src/PRBProxy.sol";

/// @author Modified from prb-proxy (https://github.com/PaulRBerg/prb-proxy/blob/main/src/PRBProxyRegistry.sol)
/// @title PRBProxyRegistry
/// @dev See the documentation in {IPRBProxyRegistry}.
contract PRBProxyRegistryModified is IPRBProxyRegistry {
/*//////////////////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////////////////*/

/// @inheritdoc IPRBProxyRegistry
string public constant override VERSION = "4.0.2";

/// @dev Magic value to override target permissions. Holders can execute on any target.
address public constant MAGIC_TARGET = address(0x42);

/*//////////////////////////////////////////////////////////////////////////
USER-FACING STORAGE
//////////////////////////////////////////////////////////////////////////*/

/// @inheritdoc IPRBProxyRegistry
ConstructorParams public override constructorParams;

/*//////////////////////////////////////////////////////////////////////////
INTERNAL STORAGE
//////////////////////////////////////////////////////////////////////////*/

mapping(address owner => mapping(IPRBProxyPlugin plugin => bytes4[] methods)) internal _methods;

mapping(address owner => mapping(address envoy => mapping(address target => bool permission)))
internal _permissions;

mapping(address owner => mapping(bytes4 method => IPRBProxyPlugin plugin)) internal _plugins;

mapping(address owner => IPRBProxy proxy) internal _proxies;

/*//////////////////////////////////////////////////////////////////////////
MODIFIERS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Checks that the caller has a proxy.
modifier onlyCallerWithProxy() {
if (address(_proxies[msg.sender]) == address(0)) {
revert PRBProxyRegistry_UserDoesNotHaveProxy(msg.sender);
}
_;
}

/// @notice Check that the user does not have a proxy.
modifier onlyNonProxyOwner(address user) {
IPRBProxy proxy = _proxies[user];
if (address(proxy) != address(0)) {
revert PRBProxyRegistry_UserHasProxy(user, proxy);
}
_;
}

/*//////////////////////////////////////////////////////////////////////////
USER-FACING CONSTANT FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

/// @inheritdoc IPRBProxyRegistry
function getMethodsByOwner(address owner, IPRBProxyPlugin plugin) external view returns (bytes4[] memory methods) {
methods = _methods[owner][plugin];

Check warning on line 70 in contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol

View check run for this annotation

Codecov / codecov/patch

contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol#L70

Added line #L70 was not covered by tests
}

/// @inheritdoc IPRBProxyRegistry
function getMethodsByProxy(
IPRBProxy proxy,
IPRBProxyPlugin plugin
) external view returns (bytes4[] memory methods) {
methods = _methods[proxy.owner()][plugin];

Check warning on line 78 in contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol

View check run for this annotation

Codecov / codecov/patch

contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol#L78

Added line #L78 was not covered by tests
}

/// @inheritdoc IPRBProxyRegistry
function getPermissionByOwner(
address owner,
address envoy,
address target
) external view returns (bool permission) {
permission = _permissions[owner][envoy][target] || _permissions[owner][envoy][MAGIC_TARGET];
}

/// @inheritdoc IPRBProxyRegistry
function getPermissionByProxy(
IPRBProxy proxy,
address envoy,
address target
) external view returns (bool permission) {
permission = _permissions[proxy.owner()][envoy][target] || _permissions[proxy.owner()][envoy][MAGIC_TARGET];
}

/// @inheritdoc IPRBProxyRegistry
function getPluginByOwner(address owner, bytes4 method) external view returns (IPRBProxyPlugin plugin) {
plugin = _plugins[owner][method];
}

/// @inheritdoc IPRBProxyRegistry
function getPluginByProxy(IPRBProxy proxy, bytes4 method) external view returns (IPRBProxyPlugin plugin) {
plugin = _plugins[proxy.owner()][method];

Check warning on line 106 in contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol

View check run for this annotation

Codecov / codecov/patch

contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol#L106

Added line #L106 was not covered by tests
}

/// @inheritdoc IPRBProxyRegistry
function getProxy(address user) external view returns (IPRBProxy proxy) {
proxy = _proxies[user];

Check warning on line 111 in contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol

View check run for this annotation

Codecov / codecov/patch

contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol#L111

Added line #L111 was not covered by tests
}

/*//////////////////////////////////////////////////////////////////////////
USER-FACING NON-CONSTANT FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

/// @inheritdoc IPRBProxyRegistry
function deploy() external override onlyNonProxyOwner(msg.sender) returns (IPRBProxy proxy) {
proxy = _deploy({ owner: msg.sender, target: address(0), data: "" });

Check warning on line 120 in contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol

View check run for this annotation

Codecov / codecov/patch

contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol#L120

Added line #L120 was not covered by tests
}

/// @inheritdoc IPRBProxyRegistry
function deployAndExecute(
address target,
bytes calldata data
) external override onlyNonProxyOwner(msg.sender) returns (IPRBProxy proxy) {
proxy = _deploy({ owner: msg.sender, target: target, data: data });

Check warning on line 128 in contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol

View check run for this annotation

Codecov / codecov/patch

contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol#L128

Added line #L128 was not covered by tests
}

/// @inheritdoc IPRBProxyRegistry
function deployFor(address user) external override onlyNonProxyOwner(user) returns (IPRBProxy proxy) {
proxy = _deploy({ owner: user, target: address(0), data: "" });

Check warning on line 133 in contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol

View check run for this annotation

Codecov / codecov/patch

contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol#L133

Added line #L133 was not covered by tests
}

/// @inheritdoc IPRBProxyRegistry
function deployAndExecuteAndInstallPlugin(
address target,
bytes calldata data,
IPRBProxyPlugin plugin
) external override onlyNonProxyOwner(msg.sender) returns (IPRBProxy proxy) {
proxy = _deploy({ owner: msg.sender, target: target, data: data });
_installPlugin(plugin);

Check warning on line 143 in contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol

View check run for this annotation

Codecov / codecov/patch

contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol#L142-L143

Added lines #L142 - L143 were not covered by tests
}
Comment on lines +137 to +144

/// @inheritdoc IPRBProxyRegistry
function deployAndInstallPlugin(
IPRBProxyPlugin plugin
) external onlyNonProxyOwner(msg.sender) returns (IPRBProxy proxy) {
proxy = _deploy({ owner: msg.sender, target: address(0), data: "" });
_installPlugin(plugin);
}
Comment on lines +147 to +152
Comment on lines +147 to +152

/// @inheritdoc IPRBProxyRegistry
function installPlugin(IPRBProxyPlugin plugin) external override onlyCallerWithProxy {
_installPlugin(plugin);

Check warning on line 156 in contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol

View check run for this annotation

Codecov / codecov/patch

contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol#L156

Added line #L156 was not covered by tests
}

/// @inheritdoc IPRBProxyRegistry
function setPermission(address envoy, address target, bool permission) external override onlyCallerWithProxy {
address owner = msg.sender;
_permissions[owner][envoy][target] = permission;
emit SetPermission(owner, _proxies[owner], envoy, target, permission);
}

/// @inheritdoc IPRBProxyRegistry
function uninstallPlugin(IPRBProxyPlugin plugin) external override onlyCallerWithProxy {
// Retrieve the methods originally installed by this plugin.
address owner = msg.sender;
bytes4[] memory methods = _methods[owner][plugin];

Check warning on line 170 in contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol

View check run for this annotation

Codecov / codecov/patch

contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol#L169-L170

Added lines #L169 - L170 were not covered by tests

// The plugin must be a known, previously installed plugin.
uint256 length = methods.length;
if (length == 0) {
revert PRBProxyRegistry_PluginUnknown(plugin);

Check warning on line 175 in contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol

View check run for this annotation

Codecov / codecov/patch

contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol#L173-L175

Added lines #L173 - L175 were not covered by tests
}

// Uninstall every method in the list.
for (uint256 i = 0; i < length; ) {
delete _plugins[owner][methods[i]];

Check warning on line 180 in contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol

View check run for this annotation

Codecov / codecov/patch

contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol#L179-L180

Added lines #L179 - L180 were not covered by tests
unchecked {
i += 1;

Check warning on line 182 in contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol

View check run for this annotation

Codecov / codecov/patch

contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol#L182

Added line #L182 was not covered by tests
}
}

// Remove the methods from the reverse mapping.
delete _methods[owner][plugin];

Check warning on line 187 in contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol

View check run for this annotation

Codecov / codecov/patch

contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol#L187

Added line #L187 was not covered by tests

// Log the plugin uninstallation.
emit UninstallPlugin(owner, _proxies[owner], plugin, methods);

Check warning on line 190 in contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol

View check run for this annotation

Codecov / codecov/patch

contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol#L190

Added line #L190 was not covered by tests
}

/*//////////////////////////////////////////////////////////////////////////
INTERNAL NON-CONSTANT FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

/// @dev See the documentation for the user-facing functions that call this internal function.
function _deploy(address owner, address target, bytes memory data) internal returns (IPRBProxy proxy) {
// Use the address of the owner as the CREATE2 salt.
bytes32 salt = bytes32(abi.encodePacked(owner));

// Set the owner and empty out the target and the data to prevent reentrancy.
constructorParams = ConstructorParams({ owner: owner, target: target, data: data });

// Deploy the proxy with CREATE2.
proxy = new PRBProxy{ salt: salt }();
delete constructorParams;

// Associate the owner and the proxy.
_proxies[owner] = proxy;

// Log the creation of the proxy.
emit DeployProxy({ operator: msg.sender, owner: owner, proxy: proxy });
}
Comment on lines +198 to +214

Check notice

Code scanning / Slither

Reentrancy vulnerabilities Low

Reentrancy in PRBProxyRegistryModified._deploy(address,address,bytes):
External calls:
- proxy = new PRBProxy()
State variables written after the call(s):
- _proxies[owner] = proxy
- delete constructorParams
Comment on lines +198 to +214

Check notice

Code scanning / Slither

Reentrancy vulnerabilities Low


/// @dev See the documentation for the user-facing functions that call this internal function.
function _installPlugin(IPRBProxyPlugin plugin) internal {
// Retrieve the methods to install.
bytes4[] memory methods = plugin.getMethods();

// The plugin must implement at least one method.
uint256 length = methods.length;
if (length == 0) {
revert PRBProxyRegistry_PluginWithZeroMethods(plugin);

Check warning on line 224 in contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol

View check run for this annotation

Codecov / codecov/patch

contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol#L224

Added line #L224 was not covered by tests
}

// Install every method in the list.
address owner = msg.sender;
for (uint256 i = 0; i < length; ) {
// Check for collisions.
bytes4 method = methods[i];
if (address(_plugins[owner][method]) != address(0)) {
revert PRBProxyRegistry_PluginMethodCollision({

Check warning on line 233 in contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol

View check run for this annotation

Codecov / codecov/patch

contracts/prebuilts/unaudited/checkout/PRBProxyRegistryModified.sol#L233

Added line #L233 was not covered by tests
currentPlugin: _plugins[owner][method],
newPlugin: plugin,
method: method
});
}
_plugins[owner][method] = plugin;
unchecked {
i += 1;
}
}

// Set the methods in the reverse mapping.
_methods[owner][plugin] = methods;

// Log the plugin installation.
emit InstallPlugin(owner, _proxies[owner], plugin, methods);
}
Comment on lines +217 to +250

Check notice

Code scanning / Slither

Reentrancy vulnerabilities Low

Comment on lines +217 to +250

Check notice

Code scanning / Slither

Reentrancy vulnerabilities Low

}
26 changes: 26 additions & 0 deletions contracts/prebuilts/unaudited/checkout/PluginCheckout.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.11;

// $$\ $$\ $$\ $$\ $$\
// $$ | $$ | \__| $$ | $$ |
// $$$$$$\ $$$$$$$\ $$\ $$$$$$\ $$$$$$$ |$$\ $$\ $$\ $$$$$$\ $$$$$$$\
// \_$$ _| $$ __$$\ $$ |$$ __$$\ $$ __$$ |$$ | $$ | $$ |$$ __$$\ $$ __$$\
// $$ | $$ | $$ |$$ |$$ | \__|$$ / $$ |$$ | $$ | $$ |$$$$$$$$ |$$ | $$ |
// $$ |$$\ $$ | $$ |$$ |$$ | $$ | $$ |$$ | $$ | $$ |$$ ____|$$ | $$ |
// \$$$$ |$$ | $$ |$$ |$$ | \$$$$$$$ |\$$$$$\$$$$ |\$$$$$$$\ $$$$$$$ |
// \____/ \__| \__|\__|\__| \_______| \_____\____/ \_______|\_______/

import { IPRBProxyPlugin } from "@prb/proxy/src/interfaces/IPRBProxyPlugin.sol";

import "./TargetCheckout.sol";

contract PluginCheckout is IPRBProxyPlugin, TargetCheckout {
function getMethods() external pure override returns (bytes4[] memory) {
bytes4[] memory methods = new bytes4[](4);
methods[0] = this.withdraw.selector;
methods[1] = this.execute.selector;
methods[2] = this.swapAndExecute.selector;
methods[3] = this.approveSwapRouter.selector;
return methods;
}
}
82 changes: 82 additions & 0 deletions contracts/prebuilts/unaudited/checkout/TargetCheckout.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.11;

import "../../../lib/CurrencyTransferLib.sol";
import "../../../eip/interface/IERC20.sol";

import { IPRBProxy } from "@prb/proxy/src/interfaces/IPRBProxy.sol";
import "./interface/IPluginCheckout.sol";

// $$\ $$\ $$\ $$\ $$\
// $$ | $$ | \__| $$ | $$ |
// $$$$$$\ $$$$$$$\ $$\ $$$$$$\ $$$$$$$ |$$\ $$\ $$\ $$$$$$\ $$$$$$$\
// \_$$ _| $$ __$$\ $$ |$$ __$$\ $$ __$$ |$$ | $$ | $$ |$$ __$$\ $$ __$$\
// $$ | $$ | $$ |$$ |$$ | \__|$$ / $$ |$$ | $$ | $$ |$$$$$$$$ |$$ | $$ |
// $$ |$$\ $$ | $$ |$$ |$$ | $$ | $$ |$$ | $$ | $$ |$$ ____|$$ | $$ |
// \$$$$ |$$ | $$ |$$ |$$ | \$$$$$$$ |\$$$$$\$$$$ |\$$$$$$$\ $$$$$$$ |
// \____/ \__| \__|\__|\__| \_______| \_____\____/ \_______|\_______/

contract TargetCheckout is IPluginCheckout {
mapping(address => bool) public isApprovedRouter;

function withdraw(address _token, uint256 _amount) external {
require(msg.sender == IPRBProxy(address(this)).owner(), "Not authorized");

CurrencyTransferLib.transferCurrency(_token, address(this), msg.sender, _amount);
}

function approveSwapRouter(address _swapRouter, bool _toApprove) external {
require(msg.sender == IPRBProxy(address(this)).owner(), "Not authorized");
require(_swapRouter != address(0), "Zero address");

Check warning on line 30 in contracts/prebuilts/unaudited/checkout/TargetCheckout.sol

View check run for this annotation

Codecov / codecov/patch

contracts/prebuilts/unaudited/checkout/TargetCheckout.sol#L29-L30

Added lines #L29 - L30 were not covered by tests

isApprovedRouter[_swapRouter] = _toApprove;

Check warning on line 32 in contracts/prebuilts/unaudited/checkout/TargetCheckout.sol

View check run for this annotation

Codecov / codecov/patch

contracts/prebuilts/unaudited/checkout/TargetCheckout.sol#L32

Added line #L32 was not covered by tests
}

function execute(UserOp memory op) external {
require(_canExecute(op, msg.sender), "Not authorized");

_execute(op);
}

function swapAndExecute(UserOp memory op, UserOp memory swapOp) external {
require(isApprovedRouter[swapOp.target], "Invalid router address");
require(_canExecute(op, msg.sender), "Not authorized");

Check warning on line 43 in contracts/prebuilts/unaudited/checkout/TargetCheckout.sol

View check run for this annotation

Codecov / codecov/patch

contracts/prebuilts/unaudited/checkout/TargetCheckout.sol#L42-L43

Added lines #L42 - L43 were not covered by tests

_execute(swapOp);
_execute(op);

Check warning on line 46 in contracts/prebuilts/unaudited/checkout/TargetCheckout.sol

View check run for this annotation

Codecov / codecov/patch

contracts/prebuilts/unaudited/checkout/TargetCheckout.sol#L45-L46

Added lines #L45 - L46 were not covered by tests
}

// =================================================
// =============== Internal functions ==============
// =================================================

function _execute(UserOp memory op) internal {
bool success;
if (op.currency == CurrencyTransferLib.NATIVE_TOKEN) {
(success, ) = op.target.call{ value: op.valueToSend }(op.data);

Check warning on line 56 in contracts/prebuilts/unaudited/checkout/TargetCheckout.sol

View check run for this annotation

Codecov / codecov/patch

contracts/prebuilts/unaudited/checkout/TargetCheckout.sol#L56

Added line #L56 was not covered by tests
} else {
if (op.valueToSend != 0 && op.approvalRequired) {
IERC20(op.currency).approve(op.target, op.valueToSend);
}

(success, ) = op.target.call(op.data);
}

require(success, "Execution failed");
}

Check failure

Code scanning / Slither

Functions that send Ether to arbitrary destinations High

Check warning

Code scanning / Slither

Unused return Medium


function _canExecute(UserOp memory op, address caller) internal view returns (bool) {
address owner = IPRBProxy(address(this)).owner();
if (owner != caller) {
bool permission = IPRBProxy(address(this)).registry().getPermissionByOwner({
owner: owner,
envoy: caller,
target: op.target
});

return permission;
}

return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.11;

interface IPluginCheckout {
/**
* @notice Details of the transaction to execute on target contract.
*
* @param target Address to send the transaction to
*
* @param currency Represents both native token and erc20 token
*
* @param approvalRequired If need to approve erc20 to the target contract
*
* @param valueToSend Transaction value to send - both native and erc20
*
* @param data Transaction calldata
*/
struct UserOp {
address target;
address currency;
bool approvalRequired;
uint256 valueToSend;
bytes data;
}

function execute(UserOp calldata op) external;

function swapAndExecute(UserOp calldata op, UserOp memory swapOp) external;
}
Loading
Loading