diff --git a/contracts/optimism/connectors/authority/events.sol b/contracts/optimism/connectors/authority/events.sol
new file mode 100644
index 00000000..3d273d2a
--- /dev/null
+++ b/contracts/optimism/connectors/authority/events.sol
@@ -0,0 +1,7 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.7.0;
+
+contract Events {
+ event LogAddAuth(address indexed _msgSender, address indexed _authority);
+ event LogRemoveAuth(address indexed _msgSender, address indexed _authority);
+}
\ No newline at end of file
diff --git a/contracts/optimism/connectors/authority/helpers.sol b/contracts/optimism/connectors/authority/helpers.sol
new file mode 100644
index 00000000..7e65a77e
--- /dev/null
+++ b/contracts/optimism/connectors/authority/helpers.sol
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.7.0;
+pragma experimental ABIEncoderV2;
+
+import { DSMath } from "../../common/math.sol";
+import { Basic } from "../../common/basic.sol";
+import { ListInterface } from "./interface.sol";
+
+abstract contract Helpers is DSMath, Basic {
+ ListInterface internal constant listContract = ListInterface(0x9926955e0Dd681Dc303370C52f4Ad0a4dd061687);
+
+ function checkAuthCount() internal view returns (uint count) {
+ uint64 accountId = listContract.accountID(address(this));
+ count = listContract.accountLink(accountId).count;
+ }
+}
diff --git a/contracts/optimism/connectors/authority/interface.sol b/contracts/optimism/connectors/authority/interface.sol
new file mode 100644
index 00000000..b0d73b29
--- /dev/null
+++ b/contracts/optimism/connectors/authority/interface.sol
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.7.0;
+pragma experimental ABIEncoderV2;
+
+interface ListInterface {
+ struct AccountLink {
+ address first;
+ address last;
+ uint64 count;
+ }
+
+ function accountID(address) external view returns (uint64);
+ function accountLink(uint64) external view returns (AccountLink memory);
+}
\ No newline at end of file
diff --git a/contracts/optimism/connectors/authority/main.sol b/contracts/optimism/connectors/authority/main.sol
new file mode 100644
index 00000000..29d58c09
--- /dev/null
+++ b/contracts/optimism/connectors/authority/main.sol
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.7.0;
+
+/**
+ * @title Authority.
+ * @dev Manage Authorities to DSA.
+ */
+
+import { AccountInterface } from "../../common/interfaces.sol";
+import { Helpers } from "./helpers.sol";
+import { Events } from "./events.sol";
+
+abstract contract AuthorityResolver is Events, Helpers {
+ /**
+ * @dev Add New authority
+ * @notice Add an address as account authority
+ * @param authority The authority Address.
+ */
+ function add(
+ address authority
+ ) external payable returns (string memory _eventName, bytes memory _eventParam) {
+ require(authority != address(0), "Not-valid-authority");
+ AccountInterface _dsa = AccountInterface(address(this));
+ if (_dsa.isAuth(authority)) {
+ authority = address(0);
+ } else {
+ _dsa.enable(authority);
+ }
+
+ _eventName = "LogAddAuth(address,address)";
+ _eventParam = abi.encode(msg.sender, authority);
+ }
+
+ /**
+ * @dev Remove authority
+ * @notice Remove an address as account authority
+ * @param authority The authority Address.
+ */
+ function remove(
+ address authority
+ ) external payable returns (string memory _eventName, bytes memory _eventParam) {
+ require(checkAuthCount() > 1, "Removing-all-authorities");
+ require(authority != address(0), "Not-valid-authority");
+ AccountInterface _dsa = AccountInterface(address(this));
+ if (_dsa.isAuth(authority)) {
+ _dsa.disable(authority);
+ } else {
+ authority = address(0);
+ }
+
+ _eventName = "LogRemoveAuth(address,address)";
+ _eventParam = abi.encode(msg.sender, authority);
+ }
+}
+
+contract ConnectV2AuthOptimism is AuthorityResolver {
+ string public constant name = "Auth-v1.1";
+}
diff --git a/contracts/optimism/connectors/basic/events.sol b/contracts/optimism/connectors/basic/events.sol
new file mode 100644
index 00000000..b68f3801
--- /dev/null
+++ b/contracts/optimism/connectors/basic/events.sol
@@ -0,0 +1,6 @@
+pragma solidity ^0.7.0;
+
+contract Events {
+ event LogDeposit(address indexed erc20, uint256 tokenAmt, uint256 getId, uint256 setId);
+ event LogWithdraw(address indexed erc20, uint256 tokenAmt, address indexed to, uint256 getId, uint256 setId);
+}
diff --git a/contracts/optimism/connectors/basic/main.sol b/contracts/optimism/connectors/basic/main.sol
new file mode 100644
index 00000000..30c7b03c
--- /dev/null
+++ b/contracts/optimism/connectors/basic/main.sol
@@ -0,0 +1,81 @@
+pragma solidity ^0.7.0;
+
+/**
+ * @title Basic.
+ * @dev Deposit & Withdraw from DSA.
+ */
+
+import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
+import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
+
+import { DSMath } from "../../common/math.sol";
+import { Basic } from "../../common/basic.sol";
+import { Events } from "./events.sol";
+
+abstract contract BasicResolver is Events, DSMath, Basic {
+ using SafeERC20 for IERC20;
+
+ /**
+ * @dev Deposit Assets To Smart Account.
+ * @notice Deposit a token to DSA.
+ * @param token The address of the token to deposit.
(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE and need to pass `value` parameter equal to `amt` in cast function ```dsa.cast({..., value: amt})```.
For ERC20: Need to give allowance prior casting spells.)
+ * @param amt The amount of tokens to deposit. (For max: `uint256(-1)` (Not valid for ETH))
+ * @param getId ID to retrieve amt.
+ * @param setId ID stores the amount of tokens deposited.
+ */
+ function deposit(
+ address token,
+ uint256 amt,
+ uint256 getId,
+ uint256 setId
+ ) public payable returns (string memory _eventName, bytes memory _eventParam) {
+ uint _amt = getUint(getId, amt);
+ if (token != ethAddr) {
+ IERC20 tokenContract = IERC20(token);
+ _amt = _amt == uint(-1) ? tokenContract.balanceOf(msg.sender) : _amt;
+ tokenContract.safeTransferFrom(msg.sender, address(this), _amt);
+ } else {
+ require(msg.value == _amt || _amt == uint(-1), "invalid-ether-amount");
+ _amt = msg.value;
+ }
+ setUint(setId, _amt);
+
+ _eventName = "LogDeposit(address,uint256,uint256,uint256)";
+ _eventParam = abi.encode(token, _amt, getId, setId);
+ }
+
+ /**
+ * @dev Withdraw Assets from Smart Account
+ * @notice Withdraw a token from DSA
+ * @param token The address of the token to withdraw. (For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
+ * @param amt The amount of tokens to withdraw. (For max: `uint256(-1)`)
+ * @param to The address to receive the token upon withdrawal
+ * @param getId ID to retrieve amt.
+ * @param setId ID stores the amount of tokens withdrawn.
+ */
+ function withdraw(
+ address token,
+ uint amt,
+ address payable to,
+ uint getId,
+ uint setId
+ ) public payable returns (string memory _eventName, bytes memory _eventParam) {
+ uint _amt = getUint(getId, amt);
+ if (token == ethAddr) {
+ _amt = _amt == uint(-1) ? address(this).balance : _amt;
+ to.call{value: _amt}("");
+ } else {
+ IERC20 tokenContract = IERC20(token);
+ _amt = _amt == uint(-1) ? tokenContract.balanceOf(address(this)) : _amt;
+ tokenContract.safeTransfer(to, _amt);
+ }
+ setUint(setId, _amt);
+
+ _eventName = "LogWithdraw(address,uint256,address,uint256,uint256)";
+ _eventParam = abi.encode(token, _amt, to, getId, setId);
+ }
+}
+
+contract ConnectV2BasicOptimism is BasicResolver {
+ string constant public name = "Basic-v1";
+}
diff --git a/contracts/optimism/connectors/uniswap/v3/events.sol b/contracts/optimism/connectors/uniswap/v3/events.sol
new file mode 100644
index 00000000..6d04f06c
--- /dev/null
+++ b/contracts/optimism/connectors/uniswap/v3/events.sol
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.7.0;
+
+contract Events {
+ event LogMint(
+ uint256 indexed tokenId,
+ uint256 liquidity,
+ uint256 amtA,
+ uint256 amtB,
+ int24 tickLower,
+ int24 tickUpper
+ );
+
+ event LogDeposit(
+ uint256 indexed tokenId,
+ uint256 liquidity,
+ uint256 amountA,
+ uint256 amountB
+ );
+
+ event LogWithdraw(
+ uint256 indexed tokenId,
+ uint256 liquidity,
+ uint256 amountA,
+ uint256 amountB
+ );
+
+ event LogCollect(
+ uint256 tokenId,
+ uint256 amountA,
+ uint256 amountB
+ );
+
+ event LogBurnPosition(uint256 tokenId);
+}
diff --git a/contracts/optimism/connectors/uniswap/v3/helpers.sol b/contracts/optimism/connectors/uniswap/v3/helpers.sol
new file mode 100644
index 00000000..85c05a46
--- /dev/null
+++ b/contracts/optimism/connectors/uniswap/v3/helpers.sol
@@ -0,0 +1,288 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.7.6;
+pragma abicoder v2;
+
+import {TokenInterface} from "../../../common/interfaces.sol";
+import {DSMath} from "../../../common/math.sol";
+import {Basic} from "../../../common/basic.sol";
+import "./interface.sol";
+import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
+import "@uniswap/v3-core/contracts/libraries/TickMath.sol";
+import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
+import "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
+
+abstract contract Helpers is DSMath, Basic {
+ /**
+ * @dev uniswap v3 NFT Position Manager & Swap Router
+ */
+ INonfungiblePositionManager constant nftManager =
+ INonfungiblePositionManager(0xC36442b4a4522E871399CD717aBDD847Ab11FE88);
+ ISwapRouter constant swapRouter =
+ ISwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564);
+
+ struct MintParams {
+ address tokenA;
+ address tokenB;
+ uint24 fee;
+ int24 tickLower;
+ int24 tickUpper;
+ uint256 amtA;
+ uint256 amtB;
+ uint256 slippage;
+ }
+
+ /**
+ * @dev Get Last NFT Index
+ * @param user: User address
+ */
+ function _getLastNftId(address user)
+ internal
+ view
+ returns (uint256 tokenId)
+ {
+ uint256 len = nftManager.balanceOf(user);
+ tokenId = nftManager.tokenOfOwnerByIndex(user, len - 1);
+ }
+
+ function getMinAmount(
+ TokenInterface token,
+ uint256 amt,
+ uint256 slippage
+ ) internal view returns (uint256 minAmt) {
+ uint256 _amt18 = convertTo18(token.decimals(), amt);
+ minAmt = wmul(_amt18, sub(WAD, slippage));
+ minAmt = convert18ToDec(token.decimals(), minAmt);
+ }
+
+ function sortTokenAddress(address _token0, address _token1)
+ internal
+ view
+ returns (address token0, address token1)
+ {
+ if (_token0 > _token1) {
+ (token0, token1) = (_token1, _token0);
+ } else {
+ (token0, token1) = (_token0, _token1);
+ }
+ }
+
+ /**
+ * @dev Mint function which interact with Uniswap v3
+ */
+ function _mint(MintParams memory params)
+ internal
+ returns (
+ uint256 tokenId,
+ uint128 liquidity,
+ uint256 amountA,
+ uint256 amountB
+ )
+ {
+ (TokenInterface _token0, TokenInterface _token1) = changeEthAddress(
+ params.tokenA,
+ params.tokenB
+ );
+
+ uint256 _amount0 = params.amtA == uint256(-1)
+ ? getTokenBal(TokenInterface(params.tokenA))
+ : params.amtA;
+ uint256 _amount1 = params.amtB == uint256(-1)
+ ? getTokenBal(TokenInterface(params.tokenB))
+ : params.amtB;
+
+ convertEthToWeth(address(_token0) == wethAddr, _token0, _amount0);
+ convertEthToWeth(address(_token1) == wethAddr, _token1, _amount1);
+
+ approve(_token0, address(nftManager), _amount0);
+ approve(_token1, address(nftManager), _amount1);
+
+ {
+ (address token0, ) = sortTokenAddress(
+ address(_token0),
+ address(_token1)
+ );
+
+ if (token0 != address(_token0)) {
+ (_token0, _token1) = (_token1, _token0);
+ (_amount0, _amount1) = (_amount1, _amount0);
+ }
+ }
+ uint256 _minAmt0 = getMinAmount(_token0, _amount0, params.slippage);
+ uint256 _minAmt1 = getMinAmount(_token1, _amount1, params.slippage);
+
+ INonfungiblePositionManager.MintParams
+ memory params = INonfungiblePositionManager.MintParams(
+ address(_token0),
+ address(_token1),
+ params.fee,
+ params.tickLower,
+ params.tickUpper,
+ _amount0,
+ _amount1,
+ _minAmt0,
+ _minAmt1,
+ address(this),
+ block.timestamp
+ );
+
+ (tokenId, liquidity, amountA, amountB) = nftManager.mint(params);
+ }
+
+ function getNftTokenPairAddresses(uint256 _tokenId)
+ internal
+ view
+ returns (address token0, address token1)
+ {
+ (bool success, bytes memory data) = address(nftManager).staticcall(
+ abi.encodeWithSelector(nftManager.positions.selector, _tokenId)
+ );
+ require(success, "fetching positions failed");
+ {
+ (, , token0, token1, , , , ) = abi.decode(
+ data,
+ (
+ uint96,
+ address,
+ address,
+ address,
+ uint24,
+ int24,
+ int24,
+ uint128
+ )
+ );
+ }
+ }
+
+ /**
+ * @dev Check if token address is etherAddr and convert it to weth
+ */
+ function _checkETH(
+ address _token0,
+ address _token1,
+ uint256 _amount0,
+ uint256 _amount1
+ ) internal {
+ bool isEth0 = _token0 == wethAddr;
+ bool isEth1 = _token1 == wethAddr;
+ convertEthToWeth(isEth0, TokenInterface(_token0), _amount0);
+ convertEthToWeth(isEth1, TokenInterface(_token1), _amount1);
+ approve(TokenInterface(_token0), address(nftManager), _amount0);
+ approve(TokenInterface(_token1), address(nftManager), _amount1);
+ }
+
+ /**
+ * @dev addLiquidityWrapper function wrapper of _addLiquidity
+ */
+ function _addLiquidityWrapper(
+ uint256 tokenId,
+ uint256 amountA,
+ uint256 amountB,
+ uint256 slippage
+ )
+ internal
+ returns (
+ uint256 liquidity,
+ uint256 amtA,
+ uint256 amtB
+ )
+ {
+ (address token0, address token1) = getNftTokenPairAddresses(tokenId);
+
+ (liquidity, amtA, amtB) = _addLiquidity(
+ tokenId,
+ token0,
+ token1,
+ amountA,
+ amountB,
+ slippage
+ );
+ }
+
+ /**
+ * @dev addLiquidity function which interact with Uniswap v3
+ */
+ function _addLiquidity(
+ uint256 _tokenId,
+ address _token0,
+ address _token1,
+ uint256 _amount0,
+ uint256 _amount1,
+ uint256 _slippage
+ )
+ internal
+ returns (
+ uint128 liquidity,
+ uint256 amount0,
+ uint256 amount1
+ )
+ {
+ _checkETH(_token0, _token1, _amount0, _amount1);
+ uint256 _amount0Min = getMinAmount(
+ TokenInterface(_token0),
+ _amount0,
+ _slippage
+ );
+ uint256 _amount1Min = getMinAmount(
+ TokenInterface(_token1),
+ _amount1,
+ _slippage
+ );
+ INonfungiblePositionManager.IncreaseLiquidityParams
+ memory params = INonfungiblePositionManager.IncreaseLiquidityParams(
+ _tokenId,
+ _amount0,
+ _amount1,
+ _amount0Min,
+ _amount1Min,
+ block.timestamp
+ );
+
+ (liquidity, amount0, amount1) = nftManager.increaseLiquidity(params);
+ }
+
+ /**
+ * @dev decreaseLiquidity function which interact with Uniswap v3
+ */
+ function _decreaseLiquidity(
+ uint256 _tokenId,
+ uint128 _liquidity,
+ uint256 _amount0Min,
+ uint256 _amount1Min
+ ) internal returns (uint256 amount0, uint256 amount1) {
+ INonfungiblePositionManager.DecreaseLiquidityParams
+ memory params = INonfungiblePositionManager.DecreaseLiquidityParams(
+ _tokenId,
+ _liquidity,
+ _amount0Min,
+ _amount1Min,
+ block.timestamp
+ );
+ (amount0, amount1) = nftManager.decreaseLiquidity(params);
+ }
+
+ /**
+ * @dev collect function which interact with Uniswap v3
+ */
+ function _collect(
+ uint256 _tokenId,
+ uint128 _amount0Max,
+ uint128 _amount1Max
+ ) internal returns (uint256 amount0, uint256 amount1) {
+ INonfungiblePositionManager.CollectParams
+ memory params = INonfungiblePositionManager.CollectParams(
+ _tokenId,
+ address(this),
+ _amount0Max,
+ _amount1Max
+ );
+ (amount0, amount1) = nftManager.collect(params);
+ }
+
+ /**
+ * @dev Burn Function
+ */
+ function _burn(uint256 _tokenId) internal {
+ nftManager.burn(_tokenId);
+ }
+}
diff --git a/contracts/optimism/connectors/uniswap/v3/interface.sol b/contracts/optimism/connectors/uniswap/v3/interface.sol
new file mode 100644
index 00000000..ed76b240
--- /dev/null
+++ b/contracts/optimism/connectors/uniswap/v3/interface.sol
@@ -0,0 +1,420 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.7.6;
+pragma abicoder v2;
+
+import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
+import "@openzeppelin/contracts/token/ERC721/IERC721Metadata.sol";
+import "@openzeppelin/contracts/token/ERC721/IERC721Enumerable.sol";
+
+/**
+ * @title Callback for IUniswapV3PoolActions#swap
+ * @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface
+ */
+interface IUniswapV3SwapCallback {
+ /**
+ * @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.
+ * @dev In the implementation you must pay the pool tokens owed for the swap.
+ * The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.
+ * amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
+ * @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
+ * the end of the swap. If positive, the callback must send that amount of token0 to the pool.
+ * @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
+ * the end of the swap. If positive, the callback must send that amount of token1 to the pool.
+ * @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call
+ */
+ function uniswapV3SwapCallback(
+ int256 amount0Delta,
+ int256 amount1Delta,
+ bytes calldata data
+ ) external;
+}
+
+interface ISwapRouter is IUniswapV3SwapCallback {
+ struct ExactInputSingleParams {
+ address tokenIn;
+ address tokenOut;
+ uint24 fee;
+ address recipient;
+ uint256 deadline;
+ uint256 amountIn;
+ uint256 amountOutMinimum;
+ uint160 sqrtPriceLimitX96;
+ }
+ /**
+ * @notice Swaps `amountIn` of one token for as much as possible of another token
+ * @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata
+ * @return amountOut The amount of the received token
+ */
+ function exactInputSingle(ExactInputSingleParams calldata params)
+ external
+ payable
+ returns (uint256 amountOut);
+
+ struct ExactInputParams {
+ bytes path;
+ address recipient;
+ uint256 deadline;
+ uint256 amountIn;
+ uint256 amountOutMinimum;
+ }
+ /**
+ * @notice Swaps `amountIn` of one token for as much as possible of another along the specified path
+ * @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata
+ * @return amountOut The amount of the received token
+ */
+ function exactInput(ExactInputParams calldata params)
+ external
+ payable
+ returns (uint256 amountOut);
+
+ struct ExactOutputSingleParams {
+ address tokenIn;
+ address tokenOut;
+ uint24 fee;
+ address recipient;
+ uint256 deadline;
+ uint256 amountOut;
+ uint256 amountInMaximum;
+ uint160 sqrtPriceLimitX96;
+ }
+ /**
+ * @notice Swaps as little as possible of one token for `amountOut` of another token
+ * @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata
+ * @return amountIn The amount of the input token
+ */
+ function exactOutputSingle(ExactOutputSingleParams calldata params)
+ external
+ payable
+ returns (uint256 amountIn);
+
+ struct ExactOutputParams {
+ bytes path;
+ address recipient;
+ uint256 deadline;
+ uint256 amountOut;
+ uint256 amountInMaximum;
+ }
+
+ /**
+ * @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)
+ * @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata
+ * @return amountIn The amount of the input token
+ */
+ function exactOutput(ExactOutputParams calldata params)
+ external
+ payable
+ returns (uint256 amountIn);
+}
+/**
+ * @title Creates and initializes V3 Pools
+ * @notice Provides a method for creating and initializing a pool, if necessary, for bundling with other methods that
+ * require the pool to exist.
+ */
+interface IPoolInitializer {
+ /**
+ * @notice Creates a new pool if it does not exist, then initializes if not initialized
+ * @dev This method can be bundled with others via IMulticall for the first action (e.g. mint) performed against a pool
+ * @param token0 The contract address of token0 of the pool
+ * @param token1 The contract address of token1 of the pool
+ * @param fee The fee amount of the v3 pool for the specified token pair
+ * @param sqrtPriceX96 The initial square root price of the pool as a Q64.96 value
+ * @return pool Returns the pool address based on the pair of tokens and fee, will return the newly created pool address if necessary
+ */
+ function createAndInitializePoolIfNecessary(
+ address token0,
+ address token1,
+ uint24 fee,
+ uint160 sqrtPriceX96
+ ) external payable returns (address pool);
+}
+/**
+ * @title Immutable state
+ * @notice Functions that return immutable state of the router
+ */
+interface IPeripheryImmutableState {
+ /// @return Returns the address of the Uniswap V3 factory
+ function factory() external view returns (address);
+
+ /// @return Returns the address of WETH9
+ function WETH9() external view returns (address);
+}
+
+/**
+ * @title Periphery Payments
+ * @notice Functions to ease deposits and withdrawals of ETH
+ */
+interface IPeripheryPayments {
+ /**
+ * @notice Unwraps the contract's WETH9 balance and sends it to recipient as ETH.
+ * @dev The amountMinimum parameter prevents malicious contracts from stealing WETH9 from users.
+ * @param amountMinimum The minimum amount of WETH9 to unwrap
+ * @param recipient The address receiving ETH
+ */
+ function unwrapWETH9(uint256 amountMinimum, address recipient)
+ external
+ payable;
+
+ /**
+ * @notice Refunds any ETH balance held by this contract to the `msg.sender`
+ * @dev Useful for bundling with mint or increase liquidity that uses ether, or exact output swaps
+ * that use ether for the input amount
+ */
+ function refundETH() external payable;
+
+ /**
+ * @notice Transfers the full amount of a token held by this contract to recipient
+ * @dev The amountMinimum parameter prevents malicious contracts from stealing the token from users
+ * @param token The contract address of the token which will be transferred to `recipient`
+ * @param amountMinimum The minimum amount of token required for a transfer
+ * @param recipient The destination address of the token
+ */
+ function sweepToken(
+ address token,
+ uint256 amountMinimum,
+ address recipient
+ ) external payable;
+}
+
+/**
+ * @title ERC721 with permit
+ * @notice Extension to ERC721 that includes a permit function for signature based approvals
+ */
+interface IERC721Permit is IERC721 {
+ /**
+ * @notice The permit typehash used in the permit signature
+ * @return The typehash for the permit
+ */
+ function PERMIT_TYPEHASH() external pure returns (bytes32);
+
+ /**
+ * @notice The domain separator used in the permit signature
+ * @return The domain seperator used in encoding of permit signature
+ */
+ function DOMAIN_SEPARATOR() external view returns (bytes32);
+
+ /**
+ * @notice Approve of a specific token ID for spending by spender via signature
+ * @param spender The account that is being approved
+ * @param tokenId The ID of the token that is being approved for spending
+ * @param deadline The deadline timestamp by which the call must be mined for the approve to work
+ * @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
+ * @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
+ * @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
+ */
+ function permit(
+ address spender,
+ uint256 tokenId,
+ uint256 deadline,
+ uint8 v,
+ bytes32 r,
+ bytes32 s
+ ) external payable;
+}
+
+/**
+ * @title Non-fungible token for positions
+ * @notice Wraps Uniswap V3 positions in a non-fungible token interface which allows for them to be transferred
+ * and authorized.
+ */
+interface INonfungiblePositionManager is
+ IPoolInitializer,
+ IPeripheryPayments,
+ IPeripheryImmutableState,
+ IERC721Metadata,
+ IERC721Enumerable,
+ IERC721Permit
+{
+ /**
+ * @notice Emitted when liquidity is increased for a position NFT
+ * @dev Also emitted when a token is minted
+ * @param tokenId The ID of the token for which liquidity was increased
+ * @param liquidity The amount by which liquidity for the NFT position was increased
+ * @param amount0 The amount of token0 that was paid for the increase in liquidity
+ * @param amount1 The amount of token1 that was paid for the increase in liquidity
+ */
+ event IncreaseLiquidity(
+ uint256 indexed tokenId,
+ uint128 liquidity,
+ uint256 amount0,
+ uint256 amount1
+ );
+
+ /**
+ * @notice Emitted when liquidity is decreased for a position NFT
+ * @param tokenId The ID of the token for which liquidity was decreased
+ * @param liquidity The amount by which liquidity for the NFT position was decreased
+ * @param amount0 The amount of token0 that was accounted for the decrease in liquidity
+ * @param amount1 The amount of token1 that was accounted for the decrease in liquidity
+ */
+ event DecreaseLiquidity(
+ uint256 indexed tokenId,
+ uint128 liquidity,
+ uint256 amount0,
+ uint256 amount1
+ );
+
+ /**
+ * @notice Emitted when tokens are collected for a position NFT
+ * @dev The amounts reported may not be exactly equivalent to the amounts transferred, due to rounding behavior
+ * @param tokenId The ID of the token for which underlying tokens were collected
+ * @param recipient The address of the account that received the collected tokens
+ * @param amount0 The amount of token0 owed to the position that was collected
+ * @param amount1 The amount of token1 owed to the position that was collected
+ */
+ event Collect(
+ uint256 indexed tokenId,
+ address recipient,
+ uint256 amount0,
+ uint256 amount1
+ );
+
+ /**
+ * @notice Returns the position information associated with a given token ID.
+ * @dev Throws if the token ID is not valid.
+ * @param tokenId The ID of the token that represents the position
+ * @return nonce The nonce for permits
+ * @return operator The address that is approved for spending
+ * @return token0 The address of the token0 for a specific pool
+ * @return token1 The address of the token1 for a specific pool
+ * @return fee The fee associated with the pool
+ * @return tickLower The lower end of the tick range for the position
+ * @return tickUpper The higher end of the tick range for the position
+ * @return liquidity The liquidity of the position
+ * @return feeGrowthInside0LastX128 The fee growth of token0 as of the last action on the individual position
+ * @return feeGrowthInside1LastX128 The fee growth of token1 as of the last action on the individual position
+ * @return tokensOwed0 The uncollected amount of token0 owed to the position as of the last computation
+ * @return tokensOwed1 The uncollected amount of token1 owed to the position as of the last computation
+ */
+ function positions(uint256 tokenId)
+ external
+ view
+ returns (
+ uint96 nonce,
+ address operator,
+ address token0,
+ address token1,
+ uint24 fee,
+ int24 tickLower,
+ int24 tickUpper,
+ uint128 liquidity,
+ uint256 feeGrowthInside0LastX128,
+ uint256 feeGrowthInside1LastX128,
+ uint128 tokensOwed0,
+ uint128 tokensOwed1
+ );
+
+ struct MintParams {
+ address token0;
+ address token1;
+ uint24 fee;
+ int24 tickLower;
+ int24 tickUpper;
+ uint256 amount0Desired;
+ uint256 amount1Desired;
+ uint256 amount0Min;
+ uint256 amount1Min;
+ address recipient;
+ uint256 deadline;
+ }
+
+ /**
+ * @notice Creates a new position wrapped in a NFT
+ * @dev Call this when the pool does exist and is initialized. Note that if the pool is created but not initialized
+ * a method does not exist, i.e. the pool is assumed to be initialized.
+ * @param params The params necessary to mint a position, encoded as `MintParams` in calldata
+ * @return tokenId The ID of the token that represents the minted position
+ * @return liquidity The amount of liquidity for this position
+ * @return amount0 The amount of token0
+ * @return amount1 The amount of token1
+ */
+ function mint(MintParams calldata params)
+ external
+ payable
+ returns (
+ uint256 tokenId,
+ uint128 liquidity,
+ uint256 amount0,
+ uint256 amount1
+ );
+
+ struct IncreaseLiquidityParams {
+ uint256 tokenId;
+ uint256 amount0Desired;
+ uint256 amount1Desired;
+ uint256 amount0Min;
+ uint256 amount1Min;
+ uint256 deadline;
+ }
+
+ /**
+ * @notice Increases the amount of liquidity in a position, with tokens paid by the `msg.sender`
+ * @param params tokenId The ID of the token for which liquidity is being increased,
+ * amount0Desired The desired amount of token0 to be spent,
+ * amount1Desired The desired amount of token1 to be spent,
+ * amount0Min The minimum amount of token0 to spend, which serves as a slippage check,
+ * amount1Min The minimum amount of token1 to spend, which serves as a slippage check,
+ * deadline The time by which the transaction must be included to effect the change
+ * @return liquidity The new liquidity amount as a result of the increase
+ * @return amount0 The amount of token0 to acheive resulting liquidity
+ * @return amount1 The amount of token1 to acheive resulting liquidity
+ */
+ function increaseLiquidity(IncreaseLiquidityParams calldata params)
+ external
+ payable
+ returns (
+ uint128 liquidity,
+ uint256 amount0,
+ uint256 amount1
+ );
+
+ struct DecreaseLiquidityParams {
+ uint256 tokenId;
+ uint128 liquidity;
+ uint256 amount0Min;
+ uint256 amount1Min;
+ uint256 deadline;
+ }
+
+ /**
+ * @notice Decreases the amount of liquidity in a position and accounts it to the position
+ * @param params tokenId The ID of the token for which liquidity is being decreased,
+ * amount The amount by which liquidity will be decreased,
+ * amount0Min The minimum amount of token0 that should be accounted for the burned liquidity,
+ * amount1Min The minimum amount of token1 that should be accounted for the burned liquidity,
+ * deadline The time by which the transaction must be included to effect the change
+ * @return amount0 The amount of token0 accounted to the position's tokens owed
+ * @return amount1 The amount of token1 accounted to the position's tokens owed
+ */
+ function decreaseLiquidity(DecreaseLiquidityParams calldata params)
+ external
+ payable
+ returns (uint256 amount0, uint256 amount1);
+
+ struct CollectParams {
+ uint256 tokenId;
+ address recipient;
+ uint128 amount0Max;
+ uint128 amount1Max;
+ }
+
+ /**
+ * @notice Collects up to a maximum amount of fees owed to a specific position to the recipient
+ * @param params tokenId The ID of the NFT for which tokens are being collected,
+ * recipient The account that should receive the tokens,
+ * amount0Max The maximum amount of token0 to collect,
+ * amount1Max The maximum amount of token1 to collect
+ * @return amount0 The amount of fees collected in token0
+ * @return amount1 The amount of fees collected in token1
+ */
+ function collect(CollectParams calldata params)
+ external
+ payable
+ returns (uint256 amount0, uint256 amount1);
+
+ /**
+ * @notice Burns a token ID, which deletes it from the NFT contract. The token must have 0 liquidity and all tokens
+ * must be collected first.
+ * @param tokenId The ID of the token that is being burned
+ */
+ function burn(uint256 tokenId) external payable;
+}
diff --git a/contracts/optimism/connectors/uniswap/v3/main.sol b/contracts/optimism/connectors/uniswap/v3/main.sol
new file mode 100644
index 00000000..5e079019
--- /dev/null
+++ b/contracts/optimism/connectors/uniswap/v3/main.sol
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.7.6;
+pragma abicoder v2;
+
+/**
+ * @title Uniswap v3.
+ * @dev Decentralized Exchange.
+ */
+
+import {TokenInterface} from "../../../common/interfaces.sol";
+import {Helpers} from "./helpers.sol";
+import {Events} from "./events.sol";
+
+abstract contract UniswapResolver is Helpers, Events {
+ /**
+ * @dev Mint New Position
+ * @notice Mint New NFT LP Position
+ * @param tokenA tokenA addreess
+ * @param tokenB tokenB addreess
+ * @param fee fee percentage
+ * @param tickLower Lower tick
+ * @param tickUpper Upper tick
+ * @param amtA amount of tokenA
+ * @param amtB amount of tokenB
+ * @param slippage slippage percentage
+ * @param getIds ID to retrieve amtA
+ * @param setId ID stores the amount of LP token
+ */
+ function mint(
+ address tokenA,
+ address tokenB,
+ uint24 fee,
+ int24 tickLower,
+ int24 tickUpper,
+ uint256 amtA,
+ uint256 amtB,
+ uint256 slippage,
+ uint256[] calldata getIds,
+ uint256 setId
+ )
+ external
+ payable
+ returns (string memory _eventName, bytes memory _eventParam)
+ {
+
+ MintParams memory params;
+ {
+ params = MintParams(
+ tokenA,
+ tokenB,
+ fee,
+ tickLower,
+ tickUpper,
+ amtA,
+ amtB,
+ slippage
+ );
+ }
+ params.amtA = getUint(getIds[0], params.amtA);
+ params.amtB = getUint(getIds[1], params.amtB);
+
+ (
+ uint256 _tokenId,
+ uint256 liquidity,
+ uint256 amountA,
+ uint256 amountB
+ ) = _mint(params);
+
+ setUint(setId, liquidity);
+
+ _eventName = "LogMint(uint256,uint256,uint256,uint256,int24,int24)";
+ _eventParam = abi.encode(
+ _tokenId,
+ liquidity,
+ amountA,
+ amountB,
+ params.tickLower,
+ params.tickUpper
+ );
+ }
+
+ /**
+ * @dev Increase Liquidity
+ * @notice Increase Liquidity of NFT Position
+ * @param tokenId NFT LP Token ID.
+ * @param amountA tokenA amounts.
+ * @param amountB tokenB amounts.
+ * @param slippage slippage.
+ * @param getIds IDs to retrieve token amounts
+ * @param setId stores the liquidity amount
+ */
+ function deposit(
+ uint256 tokenId,
+ uint256 amountA,
+ uint256 amountB,
+ uint256 slippage,
+ uint256[] calldata getIds,
+ uint256 setId
+ )
+ external
+ payable
+ returns (string memory _eventName, bytes memory _eventParam)
+ {
+ if (tokenId == 0) tokenId = _getLastNftId(address(this));
+ amountA = getUint(getIds[0], amountA);
+ amountB = getUint(getIds[1], amountB);
+ (
+ uint256 _liquidity,
+ uint256 _amtA,
+ uint256 _amtB
+ ) = _addLiquidityWrapper(tokenId, amountA, amountB, slippage);
+ setUint(setId, _liquidity);
+
+ _eventName = "LogDeposit(uint256,uint256,uint256,uint256)";
+ _eventParam = abi.encode(tokenId, _liquidity, _amtA, _amtB);
+ }
+
+ /**
+ * @dev Decrease Liquidity
+ * @notice Decrease Liquidity of NFT Position
+ * @param tokenId NFT LP Token ID.
+ * @param liquidity LP Token amount.
+ * @param amountAMin Min amount of tokenA.
+ * @param amountBMin Min amount of tokenB.
+ * @param getId ID to retrieve LP token amounts
+ * @param setIds stores the amount of output tokens
+ */
+ function withdraw(
+ uint256 tokenId,
+ uint256 liquidity,
+ uint256 amountAMin,
+ uint256 amountBMin,
+ uint256 getId,
+ uint256[] calldata setIds
+ )
+ external
+ payable
+ returns (string memory _eventName, bytes memory _eventParam)
+ {
+ if (tokenId == 0) tokenId = _getLastNftId(address(this));
+ uint128 _liquidity = uint128(getUint(getId, liquidity));
+
+ (uint256 _amtA, uint256 _amtB) = _decreaseLiquidity(
+ tokenId,
+ _liquidity,
+ amountAMin,
+ amountBMin
+ );
+
+ setUint(setIds[0], _amtA);
+ setUint(setIds[1], _amtB);
+
+ _eventName = "LogWithdraw(uint256,uint256,uint256,uint256)";
+ _eventParam = abi.encode(tokenId, _liquidity, _amtA, _amtB);
+ }
+
+ /**
+ * @dev Collect function
+ * @notice Collect from NFT Position
+ * @param tokenId NFT LP Token ID.
+ * @param amount0Max Max amount of token0.
+ * @param amount1Max Max amount of token1.
+ * @param getIds IDs to retrieve amounts
+ * @param setIds stores the amount of output tokens
+ */
+ function collect(
+ uint256 tokenId,
+ uint256 amount0Max,
+ uint256 amount1Max,
+ uint256[] calldata getIds,
+ uint256[] calldata setIds
+ )
+ external
+ payable
+ returns (string memory _eventName, bytes memory _eventParam)
+ {
+ if (tokenId == 0) tokenId = _getLastNftId(address(this));
+ uint128 _amount0Max = uint128(getUint(getIds[0], amount0Max));
+ uint128 _amount1Max = uint128(getUint(getIds[1], amount1Max));
+ (uint256 amount0, uint256 amount1) = _collect(
+ tokenId,
+ _amount0Max,
+ _amount1Max
+ );
+
+ setUint(setIds[0], amount0);
+ setUint(setIds[1], amount1);
+ _eventName = "LogCollect(uint256,uint256,uint256)";
+ _eventParam = abi.encode(tokenId, amount0, amount1);
+ }
+
+ /**
+ * @dev Burn Function
+ * @notice Burn NFT LP Position
+ * @param tokenId NFT LP Token ID
+ */
+ function burn(uint256 tokenId)
+ external
+ payable
+ returns (string memory _eventName, bytes memory _eventParam)
+ {
+ if (tokenId == 0) tokenId = _getLastNftId(address(this));
+ _burn(tokenId);
+ _eventName = "LogBurnPosition(uint256)";
+ _eventParam = abi.encode(tokenId);
+ }
+}
+
+contract ConnectV2UniswapV3Optimism is UniswapResolver {
+ string public constant name = "UniswapV3-v1";
+}
diff --git a/contracts/optimism/connectors/uniswap/v3_staker/events.sol b/contracts/optimism/connectors/uniswap/v3_staker/events.sol
new file mode 100644
index 00000000..d670cfaa
--- /dev/null
+++ b/contracts/optimism/connectors/uniswap/v3_staker/events.sol
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+pragma solidity ^0.7.0;
+
+contract Events {
+ event LogDeposit(uint256 tokenId);
+
+ event LogDepositAndStake(uint256 tokenId, bytes32 incentiveId);
+
+ event LogWithdraw(uint256 indexed tokenId);
+
+ event LogDepositTransfer(uint256 indexed tokenId, address to);
+
+ event LogStake(uint256 indexed tokenId, bytes32 incentiveId);
+
+ event LogUnstake(uint256 indexed tokenId, bytes32 incentiveId);
+
+ event LogRewardClaimed(
+ address indexed rewardToken,
+ uint256 amount
+ );
+
+ event LogIncentiveCreated(
+ bytes32 incentiveId,
+ address poolAddr,
+ address refundee,
+ uint256 startTime,
+ uint256 endTime,
+ uint256 reward
+ );
+}
diff --git a/contracts/optimism/connectors/uniswap/v3_staker/helpers.sol b/contracts/optimism/connectors/uniswap/v3_staker/helpers.sol
new file mode 100644
index 00000000..04460272
--- /dev/null
+++ b/contracts/optimism/connectors/uniswap/v3_staker/helpers.sol
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+pragma solidity ^0.7.6;
+pragma abicoder v2;
+
+import {TokenInterface} from "../../../common/interfaces.sol";
+import {DSMath} from "../../../common/math.sol";
+import {Basic} from "../../../common/basic.sol";
+import "./interface.sol";
+import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
+import "@uniswap/v3-core/contracts/libraries/TickMath.sol";
+import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
+import "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
+
+abstract contract Helpers is DSMath, Basic {
+ /**
+ * @dev uniswap v3 NFT Position Manager & Swap Router
+ */
+ INonfungiblePositionManager constant nftManager =
+ INonfungiblePositionManager(0xC36442b4a4522E871399CD717aBDD847Ab11FE88);
+ IUniswapV3Staker constant staker =
+ IUniswapV3Staker(0xe34139463bA50bD61336E0c446Bd8C0867c6fE65);
+
+ /**
+ * @dev Get Last NFT Index
+ * @param user: User address
+ */
+ function _getLastNftId(address user)
+ internal
+ view
+ returns (uint256 tokenId)
+ {
+ uint256 len = nftManager.balanceOf(user);
+ tokenId = nftManager.tokenOfOwnerByIndex(user, len - 1);
+ }
+
+ function getPoolAddress(uint256 _tokenId)
+ internal
+ view
+ returns (address pool)
+ {
+ (bool success, bytes memory data) = address(nftManager).staticcall(
+ abi.encodeWithSelector(nftManager.positions.selector, _tokenId)
+ );
+ require(success, "fetching positions failed");
+ {
+ (, , address token0, address token1, uint24 fee, , , ) = abi.decode(
+ data,
+ (
+ uint96,
+ address,
+ address,
+ address,
+ uint24,
+ int24,
+ int24,
+ uint128
+ )
+ );
+
+ pool = PoolAddress.computeAddress(
+ nftManager.factory(),
+ PoolAddress.PoolKey({token0: token0, token1: token1, fee: fee})
+ );
+ }
+ }
+
+ function _stake(
+ uint256 _tokenId,
+ IUniswapV3Staker.IncentiveKey memory _incentiveId
+ ) internal {
+ staker.stakeToken(_incentiveId, _tokenId);
+ }
+
+ function _unstake(
+ IUniswapV3Staker.IncentiveKey memory _key,
+ uint256 _tokenId
+ ) internal {
+ staker.unstakeToken(_key, _tokenId);
+ }
+
+ function _claimRewards(
+ IERC20Minimal _rewardToken,
+ address _to,
+ uint256 _amountRequested
+ ) internal returns (uint256 rewards) {
+ rewards = staker.claimReward(_rewardToken, _to, _amountRequested);
+ }
+}
diff --git a/contracts/optimism/connectors/uniswap/v3_staker/interface.sol b/contracts/optimism/connectors/uniswap/v3_staker/interface.sol
new file mode 100644
index 00000000..11a9926b
--- /dev/null
+++ b/contracts/optimism/connectors/uniswap/v3_staker/interface.sol
@@ -0,0 +1,231 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+pragma solidity =0.7.6;
+pragma abicoder v2;
+
+import '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol';
+
+import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol';
+import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol';
+import '@uniswap/v3-core/contracts/interfaces/IERC20Minimal.sol';
+
+import '@uniswap/v3-periphery/contracts/interfaces/INonfungiblePositionManager.sol';
+import '@uniswap/v3-periphery/contracts/interfaces/IMulticall.sol';
+
+/**
+ * @title Uniswap V3 Staker Interface
+ * @notice Allows staking nonfungible liquidity tokens in exchange for reward tokens
+ */
+interface IUniswapV3Staker is IERC721Receiver, IMulticall {
+ /**
+ * @param rewardToken The token being distributed as a reward
+ * @param pool The Uniswap V3 pool
+ * @param startTime The time when the incentive program begins
+ * @param endTime The time when rewards stop accruing
+ * @param refundee The address which receives any remaining reward tokens when the incentive is ended
+ */
+ struct IncentiveKey {
+ IERC20Minimal rewardToken;
+ IUniswapV3Pool pool;
+ uint256 startTime;
+ uint256 endTime;
+ address refundee;
+ }
+
+ /**
+ * @notice The Uniswap V3 Factory
+ */
+ function factory() external view returns (IUniswapV3Factory);
+
+ /**
+ * @notice The nonfungible position manager with which this staking contract is compatible
+ */
+ function nonfungiblePositionManager() external view returns (INonfungiblePositionManager);
+
+ /**
+ * @notice The max duration of an incentive in seconds
+ */
+ function maxIncentiveDuration() external view returns (uint256);
+
+ /**
+ * @notice The max amount of seconds into the future the incentive startTime can be set
+ */
+ function maxIncentiveStartLeadTime() external view returns (uint256);
+
+ /**
+ * @notice Represents a staking incentive
+ * @param incentiveId The ID of the incentive computed from its parameters
+ * @return totalRewardUnclaimed The amount of reward token not yet claimed by users
+ * @return totalSecondsClaimedX128 Total liquidity-seconds claimed, represented as a UQ32.128
+ * @return numberOfStakes The count of deposits that are currently staked for the incentive
+ */
+ function incentives(bytes32 incentiveId)
+ external
+ view
+ returns (
+ uint256 totalRewardUnclaimed,
+ uint160 totalSecondsClaimedX128,
+ uint96 numberOfStakes
+ );
+
+ /**
+ * @notice Returns information about a deposited NFT
+ * @return owner The owner of the deposited NFT
+ * @return numberOfStakes Counter of how many incentives for which the liquidity is staked
+ * @return tickLower The lower tick of the range
+ * @return tickUpper The upper tick of the range
+ */
+ function deposits(uint256 tokenId)
+ external
+ view
+ returns (
+ address owner,
+ uint48 numberOfStakes,
+ int24 tickLower,
+ int24 tickUpper
+ );
+
+ /**
+ * @notice Returns information about a staked liquidity NFT
+ * @param tokenId The ID of the staked token
+ * @param incentiveId The ID of the incentive for which the token is staked
+ * @return secondsPerLiquidityInsideInitialX128 secondsPerLiquidity represented as a UQ32.128
+ * @return liquidity The amount of liquidity in the NFT as of the last time the rewards were computed
+ */
+ function stakes(uint256 tokenId, bytes32 incentiveId)
+ external
+ view
+ returns (uint160 secondsPerLiquidityInsideInitialX128, uint128 liquidity);
+
+ /**
+ * @notice Returns amounts of reward tokens owed to a given address according to the last time all stakes were updated
+ * @param rewardToken The token for which to check rewards
+ * @param owner The owner for which the rewards owed are checked
+ * @return rewardsOwed The amount of the reward token claimable by the owner
+ */
+ function rewards(IERC20Minimal rewardToken, address owner) external view returns (uint256 rewardsOwed);
+
+ /**
+ * @notice Creates a new liquidity mining incentive program
+ * @param key Details of the incentive to create
+ * @param reward The amount of reward tokens to be distributed
+ */
+ function createIncentive(IncentiveKey memory key, uint256 reward) external;
+
+ /**
+ * @notice Ends an incentive after the incentive end time has passed and all stakes have been withdrawn
+ * @param key Details of the incentive to end
+ * @return refund The remaining reward tokens when the incentive is ended
+ */
+ function endIncentive(IncentiveKey memory key) external returns (uint256 refund);
+
+ /**
+ * @notice Transfers ownership of a deposit from the sender to the given recipient
+ * @param tokenId The ID of the token (and the deposit) to transfer
+ * @param to The new owner of the deposit
+ */
+ function transferDeposit(uint256 tokenId, address to) external;
+
+ /**
+ * @notice Withdraws a Uniswap V3 LP token `tokenId` from this contract to the recipient `to`
+ * @param tokenId The unique identifier of an Uniswap V3 LP token
+ * @param to The address where the LP token will be sent
+ * @param data An optional data array that will be passed along to the `to` address via the NFT safeTransferFrom
+ */
+ function withdrawToken(
+ uint256 tokenId,
+ address to,
+ bytes memory data
+ ) external;
+
+ /**
+ * @notice Stakes a Uniswap V3 LP token
+ * @param key The key of the incentive for which to stake the NFT
+ * @param tokenId The ID of the token to stake
+ */
+ function stakeToken(IncentiveKey memory key, uint256 tokenId) external;
+
+ /**
+ * @notice Unstakes a Uniswap V3 LP token
+ * @param key The key of the incentive for which to unstake the NFT
+ * @param tokenId The ID of the token to unstake
+ */
+ function unstakeToken(IncentiveKey memory key, uint256 tokenId) external;
+
+ /**
+ * @notice Transfers `amountRequested` of accrued `rewardToken` rewards from the contract to the recipient `to`
+ * @param rewardToken The token being distributed as a reward
+ * @param to The address where claimed rewards will be sent to
+ * @param amountRequested The amount of reward tokens to claim. Claims entire reward amount if set to 0.
+ * @return reward The amount of reward tokens claimed
+ */
+ function claimReward(
+ IERC20Minimal rewardToken,
+ address to,
+ uint256 amountRequested
+ ) external returns (uint256 reward);
+
+ /**
+ * @notice Calculates the reward amount that will be received for the given stake
+ * @param key The key of the incentive
+ * @param tokenId The ID of the token
+ * @return reward The reward accrued to the NFT for the given incentive thus far
+ */
+ function getRewardInfo(IncentiveKey memory key, uint256 tokenId)
+ external
+ returns (uint256 reward, uint160 secondsInsideX128);
+
+ /**
+ * @notice Event emitted when a liquidity mining incentive has been created
+ * @param rewardToken The token being distributed as a reward
+ * @param pool The Uniswap V3 pool
+ * @param startTime The time when the incentive program begins
+ * @param endTime The time when rewards stop accruing
+ * @param refundee The address which receives any remaining reward tokens after the end time
+ * @param reward The amount of reward tokens to be distributed
+ */
+ event IncentiveCreated(
+ IERC20Minimal indexed rewardToken,
+ IUniswapV3Pool indexed pool,
+ uint256 startTime,
+ uint256 endTime,
+ address refundee,
+ uint256 reward
+ );
+
+ /**
+ * @notice Event that can be emitted when a liquidity mining incentive has ended
+ * @param incentiveId The incentive which is ending
+ * @param refund The amount of reward tokens refunded
+ */
+ event IncentiveEnded(bytes32 indexed incentiveId, uint256 refund);
+
+ /**
+ * @notice Emitted when ownership of a deposit changes
+ * @param tokenId The ID of the deposit (and token) that is being transferred
+ * @param oldOwner The owner before the deposit was transferred
+ * @param newOwner The owner after the deposit was transferred
+ */
+ event DepositTransferred(uint256 indexed tokenId, address indexed oldOwner, address indexed newOwner);
+
+ /**
+ * @notice Event emitted when a Uniswap V3 LP token has been staked
+ * @param tokenId The unique identifier of an Uniswap V3 LP token
+ * @param liquidity The amount of liquidity staked
+ * @param incentiveId The incentive in which the token is staking
+ */
+ event TokenStaked(uint256 indexed tokenId, bytes32 indexed incentiveId, uint128 liquidity);
+
+ /**
+ * @notice Event emitted when a Uniswap V3 LP token has been unstaked
+ * @param tokenId The unique identifier of an Uniswap V3 LP token
+ * @param incentiveId The incentive in which the token is staking
+ */
+ event TokenUnstaked(uint256 indexed tokenId, bytes32 indexed incentiveId);
+
+ /**
+ * @notice Event emitted when a reward token has been claimed
+ * @param to The address where claimed rewards were sent to
+ * @param reward The amount of reward tokens claimed
+ */
+ event RewardClaimed(address indexed to, uint256 reward);
+}
diff --git a/contracts/optimism/connectors/uniswap/v3_staker/main.sol b/contracts/optimism/connectors/uniswap/v3_staker/main.sol
new file mode 100644
index 00000000..947ba7bd
--- /dev/null
+++ b/contracts/optimism/connectors/uniswap/v3_staker/main.sol
@@ -0,0 +1,256 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+pragma solidity ^0.7.6;
+pragma abicoder v2;
+
+/**
+ * @title Uniswap v3.
+ * @dev Decentralized Exchange.
+ */
+
+import {TokenInterface} from "../../../common/interfaces.sol";
+import "./interface.sol";
+import {Helpers} from "./helpers.sol";
+import {Events} from "./events.sol";
+
+abstract contract UniswapResolver is Helpers, Events {
+ /**
+ * @dev Deposit NFT token
+ * @notice Transfer deposited NFT token
+ * @param _tokenId NFT LP Token ID
+ */
+ function deposit(uint256 _tokenId)
+ external
+ payable
+ returns (string memory _eventName, bytes memory _eventParam)
+ {
+ if (_tokenId == 0) _tokenId = _getLastNftId(address(this));
+ nftManager.safeTransferFrom(
+ address(this),
+ address(staker),
+ _tokenId,
+ ""
+ );
+
+ _eventName = "LogDeposit(uint256)";
+ _eventParam = abi.encode(_tokenId);
+ }
+
+ /**
+ * @dev Deposit and Stake NFT token
+ * @notice To Deposit and Stake NFT for Staking
+ * @param _rewardToken _rewardToken address
+ * @param _startTime stake start time
+ * @param _endTime stake end time
+ * @param _refundee refundee address
+ * @param _tokenId NFT LP token id
+ */
+ function depositAndStake (
+ address _rewardToken,
+ uint256 _startTime,
+ uint256 _endTime,
+ address _refundee,
+ uint256 _tokenId
+ )
+ external
+ payable
+ returns (string memory _eventName, bytes memory _eventParam)
+ {
+ if (_tokenId == 0) _tokenId = _getLastNftId(address(this));
+ nftManager.safeTransferFrom(
+ address(this),
+ address(staker),
+ _tokenId,
+ ""
+ );
+
+ address poolAddr = getPoolAddress(_tokenId);
+
+ IUniswapV3Pool pool = IUniswapV3Pool(poolAddr);
+ IUniswapV3Staker.IncentiveKey memory _key = IUniswapV3Staker
+ .IncentiveKey(
+ IERC20Minimal(_rewardToken),
+ pool,
+ _startTime,
+ _endTime,
+ _refundee
+ );
+ _stake(_tokenId, _key);
+
+ _eventName = "LogDepositAndStake(uint256,bytes32)";
+ _eventParam = abi.encode(_tokenId, keccak256(abi.encode(_key)));
+ }
+
+ /**
+ * @dev Deposit Transfer
+ * @notice Transfer deposited NFT token
+ * @param _tokenId NFT LP Token ID
+ * @param _to address to transfer
+ */
+ function transferDeposit(uint256 _tokenId, address _to)
+ external
+ payable
+ returns (string memory _eventName, bytes memory _eventParam)
+ {
+ staker.transferDeposit(_tokenId, _to);
+
+ _eventName = "LogDepositTransfer(uint256,address)";
+ _eventParam = abi.encode(_tokenId, _to);
+ }
+
+ /**
+ * @dev Withdraw NFT LP token
+ * @notice Withdraw NFT LP token from staking pool
+ * @param _tokenId NFT LP Token ID
+ */
+ function withdraw(uint256 _tokenId)
+ external
+ payable
+ returns (string memory _eventName, bytes memory _eventParam)
+ {
+ staker.withdrawToken(_tokenId, address(this), "");
+
+ _eventName = "LogWithdraw(uint256)";
+ _eventParam = abi.encode(_tokenId);
+ }
+
+ /**
+ * @dev Stake NFT LP token
+ * @notice Stake NFT LP Position
+ * @param _rewardToken _rewardToken address
+ * @param _startTime stake start time
+ * @param _endTime stake end time
+ * @param _refundee refundee address
+ * @param _tokenId NFT LP token id
+ */
+ function stake (
+ address _rewardToken,
+ uint256 _startTime,
+ uint256 _endTime,
+ address _refundee,
+ uint256 _tokenId
+ )
+ external
+ payable
+ returns (string memory _eventName, bytes memory _eventParam)
+ {
+ address poolAddr = getPoolAddress(_tokenId);
+
+ IUniswapV3Pool pool = IUniswapV3Pool(poolAddr);
+ IUniswapV3Staker.IncentiveKey memory _key = IUniswapV3Staker
+ .IncentiveKey(
+ IERC20Minimal(_rewardToken),
+ pool,
+ _startTime,
+ _endTime,
+ _refundee
+ );
+ _stake(_tokenId, _key);
+
+ _eventName = "LogStake(uint256,bytes32)";
+ _eventParam = abi.encode(_tokenId, keccak256(abi.encode(_key)));
+ }
+
+ /**
+ * @dev Unstake NFT LP token
+ * @notice Unstake NFT LP Position
+ * @param _rewardToken _rewardToken address
+ * @param _startTime stake start time
+ * @param _endTime stake end time
+ * @param _refundee refundee address
+ * @param _tokenId NFT LP token id
+ */
+ function unstake(
+ address _rewardToken,
+ uint256 _startTime,
+ uint256 _endTime,
+ address _refundee,
+ uint256 _tokenId
+ )
+ external
+ payable
+ returns (string memory _eventName, bytes memory _eventParam)
+ {
+ address poolAddr = getPoolAddress(_tokenId);
+
+ IUniswapV3Pool pool = IUniswapV3Pool(poolAddr);
+ IUniswapV3Staker.IncentiveKey memory _key = IUniswapV3Staker
+ .IncentiveKey(
+ IERC20Minimal(_rewardToken),
+ pool,
+ _startTime,
+ _endTime,
+ _refundee
+ );
+ _unstake(_key, _tokenId);
+ _eventName = "LogUnstake(uint256,bytes32)";
+ _eventParam = abi.encode(_tokenId, keccak256(abi.encode(_key)));
+ }
+
+ /**
+ * @dev Claim rewards
+ * @notice Claim rewards
+ * @param _rewardToken _rewardToken address
+ * @param _amount requested amount
+ */
+ function claimRewards(
+ address _rewardToken,
+ uint256 _amount
+ )
+ external
+ payable
+ returns (string memory _eventName, bytes memory _eventParam)
+ {
+ uint256 rewards = _claimRewards(
+ IERC20Minimal(_rewardToken),
+ address(this),
+ _amount
+ );
+
+ _eventName = "LogRewardClaimed(address,uint256)";
+ _eventParam = abi.encode(_rewardToken, rewards);
+ }
+
+ /**
+ * @dev Create incentive
+ * @notice Create incentive
+ * @param _rewardToken _rewardToken address
+ * @param _length incentive length
+ * @param _refundee refundee address
+ * @param _poolAddr Uniswap V3 Pool address
+ * @param _reward reward amount
+ */
+ function createIncentive(
+ address _rewardToken,
+ uint256 _length,
+ address _refundee,
+ address _poolAddr,
+ uint256 _reward
+ )
+ external
+ payable
+ returns (string memory _eventName, bytes memory _eventParam)
+ {
+ IUniswapV3Pool pool = IUniswapV3Pool(_poolAddr);
+ uint256 _startTime = block.timestamp;
+ uint256 _endTime = _startTime + _length;
+ IUniswapV3Staker.IncentiveKey memory _key = IUniswapV3Staker
+ .IncentiveKey(
+ IERC20Minimal(_rewardToken),
+ pool,
+ _startTime,
+ _endTime,
+ _refundee
+ );
+ if (_rewardToken != ethAddr) {
+ IERC20Minimal(_rewardToken).approve(address(staker), _reward);
+ }
+ staker.createIncentive(_key, _reward);
+
+ _eventName = "LogIncentiveCreated(bytes32,address,address,uint256,uint256,uint256)";
+ _eventParam = abi.encode(keccak256(abi.encode(_key)), _poolAddr, _refundee, _startTime, _endTime, _reward);
+ }
+}
+
+contract ConnectV2UniswapV3StakerOptimism is UniswapResolver {
+ string public constant name = "Uniswap-V3-Staker-v1.1";
+}
diff --git a/contracts/optimism/connectors/weth/events.sol b/contracts/optimism/connectors/weth/events.sol
new file mode 100644
index 00000000..f8ec366a
--- /dev/null
+++ b/contracts/optimism/connectors/weth/events.sol
@@ -0,0 +1,6 @@
+pragma solidity ^0.7.0;
+
+contract Events {
+ event LogDeposit(uint256 tokenAmt, uint256 getId, uint256 setId);
+ event LogWithdraw(uint256 tokenAmt, uint256 getId, uint256 setId);
+}
diff --git a/contracts/optimism/connectors/weth/helpers.sol b/contracts/optimism/connectors/weth/helpers.sol
new file mode 100644
index 00000000..4c3e5c41
--- /dev/null
+++ b/contracts/optimism/connectors/weth/helpers.sol
@@ -0,0 +1,7 @@
+pragma solidity ^0.7.0;
+
+import { TokenInterface } from "../../common/interfaces.sol";
+
+abstract contract Helpers {
+ TokenInterface constant internal wethContract = TokenInterface(0x4200000000000000000000000000000000000006);
+}
diff --git a/contracts/optimism/connectors/weth/main.sol b/contracts/optimism/connectors/weth/main.sol
new file mode 100644
index 00000000..0be13fd6
--- /dev/null
+++ b/contracts/optimism/connectors/weth/main.sol
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.7.0;
+
+/**
+ * @title WETH.
+ * @dev Wrap and Unwrap WETH.
+ */
+
+import { DSMath } from "../../common/math.sol";
+import { Basic } from "../../common/basic.sol";
+import { Events } from "./events.sol";
+import { Helpers } from "./helpers.sol";
+
+abstract contract Resolver is Events, DSMath, Basic, Helpers {
+ /**
+ * @dev Deposit ETH into WETH.
+ * @notice Wrap ETH into WETH
+ * @param amt The amount of ETH to deposit. (For max: `uint256(-1)`)
+ * @param getId ID to retrieve amt.
+ * @param setId ID stores the amount of ETH deposited.
+ */
+ function deposit(
+ uint256 amt,
+ uint256 getId,
+ uint256 setId
+ ) public payable returns (string memory _eventName, bytes memory _eventParam) {
+ uint _amt = getUint(getId, amt);
+
+ _amt = _amt == uint(-1) ? address(this).balance : _amt;
+ wethContract.deposit{value: _amt}();
+
+ setUint(setId, _amt);
+
+ _eventName = "LogDeposit(uint256,uint256,uint256)";
+ _eventParam = abi.encode(_amt, getId, setId);
+ }
+
+ /**
+ * @dev Withdraw ETH from WETH from Smart Account
+ * @notice Unwrap ETH from WETH
+ * @param amt The amount of weth to withdraw. (For max: `uint256(-1)`)
+ * @param getId ID to retrieve amt.
+ * @param setId ID stores the amount of ETH withdrawn.
+ */
+ function withdraw(
+ uint amt,
+ uint getId,
+ uint setId
+ ) public payable returns (string memory _eventName, bytes memory _eventParam) {
+ uint _amt = getUint(getId, amt);
+
+ _amt = _amt == uint(-1) ? wethContract.balanceOf(address(this)) : _amt;
+ approve(wethContract, wethAddr, _amt);
+ wethContract.withdraw(_amt);
+
+ setUint(setId, _amt);
+
+ _eventName = "LogWithdraw(uint256,uint256,uint256)";
+ _eventParam = abi.encode(_amt, getId, setId);
+ }
+}
+
+contract ConnectV2WETHOptimism is Resolver {
+ string constant public name = "WETH-v1.0";
+}
diff --git a/scripts/deployment/deploy.ts b/scripts/deployment/deploy.ts
index 2803e45f..09159d7a 100644
--- a/scripts/deployment/deploy.ts
+++ b/scripts/deployment/deploy.ts
@@ -21,7 +21,7 @@ async function main() {
"QUICKSWAP-A": "ConnectV2QuickswapPolygon",
"UniswapV3-v1" : "ConnectV2UniswapV3Polygon",
"Uniswap-V3-Staker-v1.1" : "ConnectV2UniswapV3StakerPolygon",
- "Paraswap-v5" : "ConnectV2ParaswapV5Polygon"
+ "Paraswap-v5" : "ConnectV2ParaswapV5Polygon",
"1INCH-V4" : "ConnectV2OneInchV4Polygon",
"ZEROEX-A": "ConnectV2ZeroExAvalanche",
};