Skip to content

Commit

Permalink
send rate in data and time during deposit flow
Browse files Browse the repository at this point in the history
  • Loading branch information
kovalgek committed Nov 9, 2023
1 parent c3bc0db commit eb98d0b
Show file tree
Hide file tree
Showing 12 changed files with 148 additions and 54 deletions.
8 changes: 8 additions & 0 deletions contracts/BridgeableTokens.sol
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ contract BridgeableTokens {
_;
}

function isRebasableTokenFlow(address l1Token_, address l2Token_) internal view returns (bool) {
return l1Token_ == l1TokenRebasable && l2Token_ == l2TokenRebasable;
}

function isNonRebasableTokenFlow(address l1Token_, address l2Token_) internal view returns (bool) {
return l1Token_ == l1TokenNonRebasable && l2Token_ == l2TokenNonRebasable;
}

error ErrorUnsupportedL1Token();
error ErrorUnsupportedL2Token();
error ErrorAccountIsZeroAddress();
Expand Down
38 changes: 38 additions & 0 deletions contracts/optimism/DepositDataCodec.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// SPDX-FileCopyrightText: 2023 Lido <[email protected]>
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.10;

contract DepositDataCodec {

struct DepositData {
uint256 rate;
uint256 time;
bytes data;
}

function encodeDepositData(DepositData memory depositData) internal pure returns (bytes memory) {
bytes memory data = bytes.concat(
abi.encodePacked(depositData.rate),
abi.encodePacked(depositData.time),
abi.encodePacked(depositData.data)
);
return data;
}

function decodeDepositData(bytes calldata buffer) internal pure returns (DepositData memory) {

if (buffer.length < 32 * 2) {
revert ErrorDepositDataLength();
}

DepositData memory depositData;

Check warning

Code scanning / Slither

Uninitialized local variables Medium

DepositDataCodec.decodeDepositData(bytes).depositData is a local variable never initialized
depositData.rate = uint256(bytes32(buffer[0:31]));
depositData.time = uint256(bytes32(buffer[32:63]));
depositData.data = buffer[64:];

return depositData;
}

error ErrorDepositDataLength();
}
24 changes: 16 additions & 8 deletions contracts/optimism/L1ERC20TokenBridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {IL2ERC20Bridge} from "./interfaces/IL2ERC20Bridge.sol";
import {BridgingManager} from "../BridgingManager.sol";
import {BridgeableTokens} from "../BridgeableTokens.sol";
import {CrossDomainEnabled} from "./CrossDomainEnabled.sol";
import {DepositDataCodec} from "./DepositDataCodec.sol";

import {IERC20Wrapable} from "../token/interfaces/IERC20Wrapable.sol";
import "hardhat/console.sol";

Check failure on line 19 in contracts/optimism/L1ERC20TokenBridge.sol

View workflow job for this annotation

GitHub Actions / solhint

Unexpected import of console file
Expand All @@ -25,7 +26,8 @@ contract L1ERC20TokenBridge is
IL1ERC20Bridge,
BridgingManager,
BridgeableTokens,
CrossDomainEnabled
CrossDomainEnabled,
DepositDataCodec
{
using SafeERC20 for IERC20;

Expand Down Expand Up @@ -65,16 +67,22 @@ contract L1ERC20TokenBridge is
if (Address.isContract(msg.sender)) {
revert ErrorSenderNotEOA();
}

DepositData memory depositData;

Check warning

Code scanning / Slither

Uninitialized local variables Medium

depositData.rate = IERC20Wrapable(l1TokenNonRebasable).tokensPerStEth();
depositData.time = block.timestamp;
depositData.data = data_;

if(l1Token_ == l1TokenRebasable) {
bytes memory data = bytes.concat(hex'01', data_);
bytes memory encodedDepositData = encodeDepositData(depositData);

if (isRebasableTokenFlow(l1Token_, l2Token_)) {
IERC20(l1TokenRebasable).safeTransferFrom(msg.sender, address(this), amount_);
IERC20(l1TokenRebasable).approve(l1TokenNonRebasable, amount_);
uint256 wstETHAmount = IERC20Wrapable(l1TokenNonRebasable).wrap(amount_);
_initiateERC20Deposit(l1Token_, l2Token_, msg.sender, msg.sender, wstETHAmount, l2Gas_, data);
} else {
_initiateERC20Deposit(l1TokenRebasable, l2TokenRebasable, msg.sender, msg.sender, wstETHAmount, l2Gas_, encodedDepositData);
} else if (isNonRebasableTokenFlow(l1Token_, l2Token_)) {
IERC20(l1TokenNonRebasable).safeTransferFrom(msg.sender, address(this), amount_);
_initiateERC20Deposit(l1Token_, l2Token_, msg.sender, msg.sender, amount_, l2Gas_, data_);
_initiateERC20Deposit(l1TokenNonRebasable, l2TokenNonRebasable, msg.sender, msg.sender, amount_, l2Gas_, encodedDepositData);
}
}

Expand Down Expand Up @@ -111,10 +119,10 @@ contract L1ERC20TokenBridge is
onlySupportedL2Token(l2Token_)
onlyFromCrossDomainAccount(l2TokenBridge)
{
if (data_.length > 0 && data_[0] == hex'01') {
if (isRebasableTokenFlow(l1Token_, l2Token_)) {
uint256 stETHAmount = IERC20Wrapable(l1TokenNonRebasable).unwrap(amount_);
IERC20(l1TokenRebasable).safeTransfer(to_, stETHAmount);
} else {
} else if (isNonRebasableTokenFlow(l1Token_, l2Token_)) {
IERC20(l1Token_).safeTransfer(to_, amount_);
}

Expand Down
29 changes: 19 additions & 10 deletions contracts/optimism/L2ERC20TokenBridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pragma solidity 0.8.10;
import {IL1ERC20Bridge} from "./interfaces/IL1ERC20Bridge.sol";
import {IL2ERC20Bridge} from "./interfaces/IL2ERC20Bridge.sol";
import {IERC20Bridged} from "../token/interfaces/IERC20Bridged.sol";
import {ITokensRateOracle} from "../token/interfaces/ITokensRateOracle.sol";
import {ERC20Rebasable} from "../token/ERC20Rebasable.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Wrapable} from "../token/interfaces/IERC20Wrapable.sol";
Expand All @@ -14,6 +15,7 @@ import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
import {BridgingManager} from "../BridgingManager.sol";
import {BridgeableTokens} from "../BridgeableTokens.sol";
import {CrossDomainEnabled} from "./CrossDomainEnabled.sol";
import {DepositDataCodec} from "./DepositDataCodec.sol";

import { console } from "hardhat/console.sol";

Check failure on line 20 in contracts/optimism/L2ERC20TokenBridge.sol

View workflow job for this annotation

GitHub Actions / solhint

Unexpected import of console file

Expand All @@ -27,13 +29,18 @@ contract L2ERC20TokenBridge is
IL2ERC20Bridge,
BridgingManager,
BridgeableTokens,
CrossDomainEnabled
CrossDomainEnabled,
DepositDataCodec
{
using SafeERC20 for IERC20;

/// @inheritdoc IL2ERC20Bridge
address public immutable l1TokenBridge;

address public immutable tokensRateOracle;



/// @param messenger_ L2 messenger address being used for cross-chain communications
/// @param l1TokenBridge_ Address of the corresponding L1 bridge
/// @param l1TokenNonRebasable_ Address of the bridged token in the L1 chain
Expand All @@ -46,9 +53,11 @@ contract L2ERC20TokenBridge is
address l1TokenNonRebasable_,
address l1TokenRebasable_,
address l2TokenNonRebasable_,
address l2TokenRebasable_
address l2TokenRebasable_,
address tokensRateOracle_
) CrossDomainEnabled(messenger_) BridgeableTokens(l1TokenNonRebasable_, l1TokenRebasable_, l2TokenNonRebasable_, l2TokenRebasable_) {
l1TokenBridge = l1TokenBridge_;
tokensRateOracle = tokensRateOracle_;
}

/// @inheritdoc IL2ERC20Bridge
Expand All @@ -60,10 +69,9 @@ contract L2ERC20TokenBridge is
bytes calldata data_
) external whenWithdrawalsEnabled onlySupportedL2Token(l2Token_) {
if(l2Token_ == l2TokenRebasable) {
bytes memory data = bytes.concat(hex'01', data_);
uint256 shares = ERC20Rebasable(l2TokenRebasable).getSharesByTokens(amount_);
ERC20Rebasable(l2TokenRebasable).burnShares(msg.sender, shares);
_initiateWithdrawal(l1Token_, l2Token_, msg.sender, msg.sender, shares, l1Gas_, data);
_initiateWithdrawal(l1Token_, l2Token_, msg.sender, msg.sender, shares, l1Gas_, data_);
} else {
IERC20Bridged(l2TokenNonRebasable).bridgeBurn(msg.sender, amount_);
_initiateWithdrawal(l1Token_, l2Token_, msg.sender, msg.sender, amount_, l1Gas_, data_);
Expand Down Expand Up @@ -96,14 +104,15 @@ contract L2ERC20TokenBridge is
onlySupportedL2Token(l2Token_)
onlyFromCrossDomainAccount(l1TokenBridge)
{
if (data_.length > 0 && data_[0] == hex'01') {
DepositData memory depositData = decodeDepositData(data_);
ITokensRateOracle(tokensRateOracle).updateRate(int256(depositData.rate), depositData.time);

if (isRebasableTokenFlow(l1Token_, l2Token_)) {
ERC20Rebasable(l2TokenRebasable).mintShares(to_, amount_);
bytes memory data = data_[1:];
emit DepositFinalized(l1Token_, l2Token_, from_, to_, amount_, data);
} else {
IERC20Bridged(l2Token_).bridgeMint(to_, amount_);
emit DepositFinalized(l1Token_, l2Token_, from_, to_, amount_, data_);
} else if (isNonRebasableTokenFlow(l1Token_, l2Token_)) {
IERC20Bridged(l2TokenNonRebasable).bridgeMint(to_, amount_);
}
emit DepositFinalized(l1Token_, l2Token_, from_, to_, amount_, depositData.data);
}

/// @notice Performs the logic for withdrawals by burning the token and informing
Expand Down
4 changes: 4 additions & 0 deletions contracts/stubs/ERC20WrapableStub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,8 @@ contract ERC20WrapableStub is IERC20Wrapable, ERC20 {

return stETHAmount;
}

function tokensPerStEth() external view returns (uint256) {
return tokensRate;
}
}
5 changes: 5 additions & 0 deletions contracts/stubs/TokensRateOracleStub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,9 @@ contract TokensRateOracleStub is ITokensRateOracle {
) {
return (0,latestRoundDataAnswer,0,latestRoundDataUpdatedAt,0);
}

function updateRate(int256 rate, uint256 updatedAt) external {
latestRoundDataAnswer = rate;
latestRoundDataUpdatedAt = updatedAt;
}
}
4 changes: 4 additions & 0 deletions contracts/token/ERC20Rebasable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ contract ERC20Rebasable is IERC20Wrapable, IERC20, ERC20Metadata {
return sharesAmount;
}

Check failure

Code scanning / Slither

Unchecked transfer High


function tokensPerStEth() external view returns (uint256) {
return 0;
}

function mintShares(address account_, uint256 amount_) external returns (uint256) {
return _mintShares(account_, amount_);
}
Expand Down
31 changes: 5 additions & 26 deletions contracts/token/interfaces/IERC20Wrapable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,30 +30,9 @@ interface IERC20Wrapable {
*/
function unwrap(uint256 wrapableTokenAmount_) external returns (uint256);

// TODO:
// /**
// * @notice Get amount of wstETH for a given amount of stETH
// * @param _stETHAmount amount of stETH
// * @return Amount of wstETH for a given stETH amount
// */
// function getWstETHByStETH(uint256 _stETHAmount) external view returns (uint256);

// /**
// * @notice Get amount of stETH for a given amount of wstETH
// * @param _wstETHAmount amount of wstETH
// * @return Amount of stETH for a given wstETH amount
// */
// function getStETHByWstETH(uint256 _wstETHAmount) external view returns (uint256);

// /**
// * @notice Get amount of stETH for a one wstETH
// * @return Amount of stETH for 1 wstETH
// */
// function stEthPerToken() external view returns (uint256);

// /**
// * @notice Get amount of wstETH for a one stETH
// * @return Amount of wstETH for a 1 stETH
// */
// function tokensPerStEth() external view returns (uint256);
/**
* @notice Get amount of wstETH for a one stETH
* @return Amount of wstETH for a 1 stETH
*/
function tokensPerStEth() external view returns (uint256);
}
6 changes: 6 additions & 0 deletions contracts/token/interfaces/ITokensRateOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ pragma solidity 0.8.10;
/// @notice Oracle interface for two tokens rate
interface ITokensRateOracle {

function updateRate(int256 rate, uint256 updatedAt) external;

/**
* @notice represents the number of decimals the oracle responses represent.
*/
Expand All @@ -25,4 +27,8 @@ interface ITokensRateOracle {
uint256 updatedAt,
uint80 answeredInRound
);
}

interface ITokensRateOracleUpdatable {
function updateRate(int256 rate, uint256 updatedAt) external;
}
Loading

0 comments on commit eb98d0b

Please sign in to comment.