Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion contracts/old/MOR.sol → contracts/MOR.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ERC20Burnable} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import {ERC20, ERC20Capped} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Capped.sol";

import {IMOR, IERC20, IERC165} from "../interfaces/old/IMOR.sol";
import {IMOR, IERC20, IERC165} from "./interfaces/IMOR.sol";

contract MOR is IMOR, ERC20Capped, ERC20Burnable, Ownable {
constructor(uint256 cap_) ERC20("MOR", "MOR") ERC20Capped(cap_) {}
Expand Down
2 changes: 1 addition & 1 deletion contracts/capital-protocol/DistributorV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {IRewardsController} from "../interfaces/aave/IRewardsController.sol";
import {DecimalsConverter} from "@solarity/solidity-lib/libs/decimals/DecimalsConverter.sol";

import {IDistributor, IERC165} from "../interfaces/capital-protocol/IDistributor.sol";
import {IL1SenderV2} from "../interfaces/capital-protocol/IL1SenderV2.sol";
import {IL1SenderV2} from "../interfaces/capital-protocol/old/IL1SenderV2.sol";
import {IChainLinkDataConsumer} from "../interfaces/capital-protocol/IChainLinkDataConsumer.sol";
import {IDepositPool} from "../interfaces/capital-protocol/IDepositPool.sol";
import {IRewardPool} from "../interfaces/capital-protocol/IRewardPool.sol";
Expand Down
241 changes: 241 additions & 0 deletions contracts/capital-protocol/L1SenderV3.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

import {ISwapRouter} from "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
import {TransferHelper} from "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";

import {ILayerZeroEndpoint} from "@layerzerolabs/lz-evm-sdk-v1-0.7/contracts/interfaces/ILayerZeroEndpoint.sol";

import {IGatewayRouter} from "@arbitrum/token-bridge-contracts/contracts/tokenbridge/libraries/gateway/IGatewayRouter.sol";

import {IL1SenderV3, IERC165} from "../interfaces/capital-protocol/IL1SenderV3.sol";
import {IDistributor} from "../interfaces/capital-protocol/IDistributor.sol";
import {IWStETH} from "../interfaces/tokens/IWStETH.sol";
import {IL1ERC20Bridge} from "../interfaces/@lidofinance/lido-l2/contracts/optimism/interfaces/IL1ERC20Bridge.sol";

contract L1SenderV3 is IL1SenderV3, OwnableUpgradeable, UUPSUpgradeable {
/** @dev stETH token address */
address public stETH;

/** @dev `Distributor` contract address. */
address public distributor;

/** @dev The config for Arbitrum bridge. Send wstETH to the Arbitrum */
TokenBridgeConfig public tokenBridgeConfig;

/** @dev The config for LayerZero. Send MOR mint message to the Arbitrum */
MessageBridgeConfig public messageBridgeConfig;

/** @dev UPGRADE `L1SenderV2` storage updates, add Uniswap integration */
address public uniswapSwapRouter;

/**********************************************************************************************/
/*** Init, IERC165 ***/
/**********************************************************************************************/

constructor() {
_disableInitializers();
}

function L1SenderV3__init() external initializer {
__Ownable_init();
__UUPSUpgradeable_init();
}

function supportsInterface(bytes4 interfaceId_) external pure returns (bool) {
return interfaceId_ == type(IL1SenderV3).interfaceId || interfaceId_ == type(IERC165).interfaceId;
}

/**********************************************************************************************/
/*** Global contract management functionality for the contract `owner()` ***/
/**********************************************************************************************/

function setStETh(address value_) external onlyOwner {
require(value_ != address(0), "L1S: invalid stETH address");

stETH = value_;

emit stETHSet(value_);
}

function setDistributor(address value_) external onlyOwner {
require(IERC165(value_).supportsInterface(type(IDistributor).interfaceId), "L1S: invalid distributor address");

distributor = value_;

emit DistributorSet(value_);
}

/**
* https://docs.uniswap.org/contracts/v3/reference/deployments/ethereum-deployments
*/
function setUniswapSwapRouter(address value_) external onlyOwner {
require(value_ != address(0), "L1S: invalid `uniswapSwapRouter` address");

uniswapSwapRouter = value_;

emit UniswapSwapRouterSet(value_);
}

/**********************************************************************************************/
/*** LayerZero functionality ***/
/**********************************************************************************************/

/**
* @dev https://docs.layerzero.network/v1/deployments/deployed-contracts
* Gateway - see `EndpointV1` at the link
* Receiver - `L2MessageReceiver` address
* Receiver Chain Id - see `EndpointId` at the link
* Zro Payment Address - the address of the ZRO token holder who would pay for the transaction
* Adapter Params - parameters for custom functionality. e.g. receive airdropped native gas from the relayer on destination
*/
function setMessageBridgeConfig(MessageBridgeConfig calldata config_) external onlyOwner {
messageBridgeConfig = config_;

emit MessageBridgeConfigSet(messageBridgeConfig);
}

function sendMintMessage(address user_, uint256 amount_, address refundTo_) external payable {
require(_msgSender() == distributor, "L1S: the `msg.sender` isn't `distributor`");

MessageBridgeConfig storage config = messageBridgeConfig;

bytes memory receiverAndSenderAddresses_ = abi.encodePacked(config.receiver, address(this));
bytes memory payload_ = abi.encode(user_, amount_);

// https://docs.layerzero.network/v1/developers/evm/evm-guides/send-messages
ILayerZeroEndpoint(config.gateway).send{value: msg.value}(
config.receiverChainId,
receiverAndSenderAddresses_,
payload_,
payable(refundTo_),
config.zroPaymentAddress,
config.adapterParams
);

emit MintMessageSent(user_, amount_);
}

/**********************************************************************************************/
/*** Lido bridge functionality ***/
/**********************************************************************************************/

/**
* @dev https://docs.lido.fi/deployed-contracts/#base
* wstETH - see `WstETH ERC20Bridged (proxy)` at the link
* Gateway - see `L1ERC20TokenBridge (proxy)` at the link
* Receiver - token receiver address on L2
*/
function setTokenBridgeConfig(TokenBridgeConfig calldata config_) external onlyOwner {
require(stETH != address(0), "L1S: stETH is not set");
require(config_.receiver != address(0), "L1S: invalid receiver");

TokenBridgeConfig memory oldConfig_ = tokenBridgeConfig;

if (oldConfig_.wstETH != address(0)) {
IERC20(stETH).approve(oldConfig_.wstETH, 0);

address oldGateway_;
try IGatewayRouter(oldConfig_.gateway).getGateway(oldConfig_.wstETH) returns (address gateway_) {
oldGateway_ = gateway_;
} catch {
oldGateway_ = oldConfig_.gateway;
}
IERC20(oldConfig_.wstETH).approve(oldGateway_, 0);
}

IERC20(stETH).approve(config_.wstETH, type(uint256).max);
IERC20(config_.wstETH).approve(config_.gateway, type(uint256).max);

tokenBridgeConfig = config_;

emit TokenBridgeConfigSet(tokenBridgeConfig);
}

function sendWstETH(uint32 l2Gas_, bytes calldata data_) external onlyOwner {
TokenBridgeConfig memory config_ = tokenBridgeConfig;
require(config_.wstETH != address(0), "L1S: wstETH isn't set");

uint256 stETHBalance_ = IERC20(stETH).balanceOf(address(this));
if (stETHBalance_ > 0) {
IWStETH(config_.wstETH).wrap(stETHBalance_);
}

uint256 amount_ = IWStETH(config_.wstETH).balanceOf(address(this));

IL1ERC20Bridge l1ERC20Bridge_ = IL1ERC20Bridge(config_.gateway);
l1ERC20Bridge_.depositERC20To(
config_.wstETH,
l1ERC20Bridge_.l2Token(),
config_.receiver,
amount_,
l2Gas_,
data_
);

emit TokenSent(amount_, config_.receiver, l2Gas_, data_);
}

/**********************************************************************************************/
/*** Uniswap functionality ***/
/**********************************************************************************************/

/**
* @dev https://docs.uniswap.org/contracts/v3/guides/swaps/multihop-swaps
*
* Multiple pool swaps are encoded through bytes called a `path`. A path is a sequence
* of token addresses and poolFees that define the pools used in the swaps.
* The format for pool encoding is (tokenIn, fee, tokenOut/tokenIn, fee, tokenOut) where
* tokenIn/tokenOut parameter is the shared token across the pools.
* Since we are swapping DAI to USDC and then USDC to WETH9 the path encoding is (DAI, 0.3%, USDC, 0.3%, WETH9).
*/
function swapExactInputMultihop(
address[] calldata tokens_,
uint24[] calldata poolsFee_,
uint256 amountIn_,
uint256 amountOutMinimum_,
uint256 deadline_
) external onlyOwner returns (uint256) {
require(tokens_.length >= 2 && tokens_.length == poolsFee_.length + 1, "L1S: invalid array length");
require(amountIn_ != 0, "L1S: invalid `amountIn_` value");
require(amountOutMinimum_ != 0, "L1S: invalid `amountOutMinimum_` value");

TransferHelper.safeApprove(tokens_[0], uniswapSwapRouter, amountIn_);

// START create the `path`
bytes memory path_;
for (uint256 i = 0; i < poolsFee_.length; i++) {
path_ = abi.encodePacked(path_, tokens_[i], poolsFee_[i]);
}
path_ = abi.encodePacked(path_, tokens_[tokens_.length - 1]);
// END

ISwapRouter.ExactInputParams memory params_ = ISwapRouter.ExactInputParams({
path: path_,
recipient: address(this),
deadline: deadline_,
amountIn: amountIn_,
amountOutMinimum: amountOutMinimum_
});

uint256 amountOut_ = ISwapRouter(uniswapSwapRouter).exactInput(params_);

emit TokensSwapped(path_, amountIn_, amountOut_);

return amountOut_;
}

/**********************************************************************************************/
/*** UUPS ***/
/**********************************************************************************************/

function version() external pure returns (uint256) {
return 3;
}

function _authorizeUpgrade(address) internal view override onlyOwner {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ pragma solidity ^0.8.20;
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

import {IMOROFT} from "../../interfaces/IMOROFT.sol";
import {IL2MessageReceiver} from "../../interfaces/capital-protocol/old/IL2MessageReceiver.sol";
import {IMOROFT} from "../interfaces/IMOROFT.sol";
import {IL2MessageReceiver} from "../interfaces/capital-protocol/IL2MessageReceiver.sol";

contract L2MessageReceiver is IL2MessageReceiver, OwnableUpgradeable, UUPSUpgradeable {
address public rewardToken;
Expand Down
2 changes: 1 addition & 1 deletion contracts/capital-protocol/old/Distributor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {IRewardsController} from "../../interfaces/aave/IRewardsController.sol";
import {DecimalsConverter} from "@solarity/solidity-lib/libs/decimals/DecimalsConverter.sol";

import {IDistributor, IERC165} from "../../interfaces/capital-protocol/IDistributor.sol";
import {IL1SenderV2} from "../../interfaces/capital-protocol/IL1SenderV2.sol";
import {IL1SenderV2} from "../../interfaces/capital-protocol/old/IL1SenderV2.sol";
import {IChainLinkDataConsumer} from "../../interfaces/capital-protocol/IChainLinkDataConsumer.sol";
import {IDepositPool} from "../../interfaces/capital-protocol/IDepositPool.sol";
import {IRewardPool} from "../../interfaces/capital-protocol/IRewardPool.sol";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import {ILayerZeroEndpoint} from "@layerzerolabs/lz-evm-sdk-v1-0.7/contracts/int

import {IGatewayRouter} from "@arbitrum/token-bridge-contracts/contracts/tokenbridge/libraries/gateway/IGatewayRouter.sol";

import {IL1SenderV2, IERC165} from "../interfaces/capital-protocol/IL1SenderV2.sol";
import {IDistributor} from "../interfaces/capital-protocol/IDistributor.sol";
import {IWStETH} from "../interfaces/tokens/IWStETH.sol";
import {IL1SenderV2, IERC165} from "../../interfaces/capital-protocol/old/IL1SenderV2.sol";
import {IDistributor} from "../../interfaces/capital-protocol/IDistributor.sol";
import {IWStETH} from "../../interfaces/tokens/IWStETH.sol";

contract L1SenderV2 is IL1SenderV2, OwnableUpgradeable, UUPSUpgradeable {
/** @dev stETH token address */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.20;

interface IBridgeableTokens {
function l1Token() external returns (address);
function l2Token() external returns (address);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// SPDX-FileCopyrightText: 2022 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.20;

import {IBridgeableTokens} from "./IBridgeableTokens.sol";

/// @notice The L1 Standard bridge locks bridged tokens on the L1 side, sends deposit messages
/// on the L2 side, and finalizes token withdrawals from L2.
interface IL1ERC20Bridge is IBridgeableTokens {
event ERC20DepositInitiated(
address indexed _l1Token,
address indexed _l2Token,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);

event ERC20WithdrawalFinalized(
address indexed _l1Token,
address indexed _l2Token,
address indexed _from,
address _to,
uint256 _amount,
bytes _data
);

/// @notice get the address of the corresponding L2 bridge contract.
/// @return Address of the corresponding L2 bridge contract.
function l2TokenBridge() external returns (address);

/// @notice deposit an amount of the ERC20 to the caller's balance on L2.
/// @param l1Token_ Address of the L1 ERC20 we are depositing
/// @param l2Token_ Address of the L1 respective L2 ERC20
/// @param amount_ Amount of the ERC20 to deposit
/// @param l2Gas_ Gas limit required to complete the deposit on L2.
/// @param data_ Optional data to forward to L2. This data is provided
/// solely as a convenience for external contracts. Aside from enforcing a maximum
/// length, these contracts provide no guarantees about its content.
function depositERC20(
address l1Token_,
address l2Token_,
uint256 amount_,
uint32 l2Gas_,
bytes calldata data_
) external;

/// @notice deposit an amount of ERC20 to a recipient's balance on L2.
/// @param l1Token_ Address of the L1 ERC20 we are depositing
/// @param l2Token_ Address of the L1 respective L2 ERC20
/// @param to_ L2 address to credit the withdrawal to.
/// @param amount_ Amount of the ERC20 to deposit.
/// @param l2Gas_ Gas limit required to complete the deposit on L2.
/// @param data_ Optional data to forward to L2. This data is provided
/// solely as a convenience for external contracts. Aside from enforcing a maximum
/// length, these contracts provide no guarantees about its content.
function depositERC20To(
address l1Token_,
address l2Token_,
address to_,
uint256 amount_,
uint32 l2Gas_,
bytes calldata data_
) external;

/// @notice Complete a withdrawal from L2 to L1, and credit funds to the recipient's balance of the
/// L1 ERC20 token.
/// @dev This call will fail if the initialized withdrawal from L2 has not been finalized.
/// @param l1Token_ Address of L1 token to finalizeWithdrawal for.
/// @param l2Token_ Address of L2 token where withdrawal was initiated.
/// @param from_ L2 address initiating the transfer.
/// @param to_ L1 address to credit the withdrawal to.
/// @param amount_ Amount of the ERC20 to deposit.
/// @param data_ Data provided by the sender on L2. This data is provided
/// solely as a convenience for external contracts. Aside from enforcing a maximum
/// length, these contracts provide no guarantees about its content.
function finalizeERC20Withdrawal(
address l1Token_,
address l2Token_,
address from_,
address to_,
uint256 amount_,
bytes calldata data_
) external;
}
File renamed without changes.
Loading