Skip to content

Commit

Permalink
Merge the develop branch to the master branch, preparation to v1.0.0-…
Browse files Browse the repository at this point in the history
…rc2 (#13)
  • Loading branch information
akolotov authored Feb 22, 2021
2 parents c30d285 + a7c9618 commit ea0ffa6
Show file tree
Hide file tree
Showing 46 changed files with 2,438 additions and 891 deletions.
45 changes: 45 additions & 0 deletions contracts/helpers/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Omnibridge helpers contracts

## WETHOmnibridgeRouter

This contract allows reducing the number of actions required for native-to-erc20 bridging over Omnibridge.
An example of such bridging scenarios might be:
* Bridging ETH from Mainnet to the xDAI chain in a form of [WETH on xDAI](https://blockscout.com/poa/xdai/tokens/0x6A023CCd1ff6F2045C3309768eAd9E68F978f6e1/token-transfers)
* Bridging BNB from BSC to the xDAI chain in a form of [WBNB on xDAI](https://blockscout.com/poa/xdai/tokens/0xCa8d20f3e0144a72C6B5d576e9Bd3Fd8557E2B04/token-transfers)

Helper supports full-duplex bridging operations.
Thus, there are 2 main usage scenarios for the helper contract.

Even though the below examples are given for the BNB native coin from the BSC,
the mechanism works exactly the same for other native coins as well.

### BNB => WBNB => WBNB on xDAI

In order to wrap native coins and bridge them through the Omnibridge to the other chain, make the following transaction:
* Call `Helper.wrapAndRelayTokens()`/`Helper.wrapAndRelayTokens(receiver)` in BSC

Desired bridged amount of BNB should be send as the transaction value argument.
If the given amount of BNB cannot be bridged immediately, the whole transaction will revert.

The upper call is identical to the following 3 sequential transactions:
* Call `WBNB.deposit()` in BSC
* Call `WBNB.approve(Omnibridge, value)` in BSC
* Call `Omnibridge.relayTokens(WBNB, value)` in BSC

### WBNB on xDAI => WBNB => BNB

In order to bridged wrapped tokens back to the origin chain through the Omnibridge and automatically withdraw them, make the following transaction:
* Call `WBNB_on_xDAI.transferAndCall(Omnibridge, value, Helper ++ receiver)` in xDAI

Alternatively, make the following 2 transactions:
* Call `WBNB_on_xDAI.approve(Omnibridge, value)` in xDAI
* Call `Omnibridge.relayTokensAndCall(WBNB_on_xDAI, Helper, value, receiver)` in xDAI

It is crucially important to correctly specify the receiver in the upper approaches.
This should be an address of the final BNB receiver in BSC.
Always include it explicitly in the call, even if msg.sender is equal to the receiver.

The upper calls are identical to the following 3 sequential transactions:
* Call `WBNB_on_xDAI.approve(Omnibridge, value)` in xDAI
* Call `Omnibridge.relayTokens(WBNB_on_xDAI, value)` in xDAI
* Call `WBNB.withdraw(value - fee)` in BSC
98 changes: 98 additions & 0 deletions contracts/helpers/WETHOmnibridgeRouter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
pragma solidity 0.7.5;

import "../interfaces/IOmnibridge.sol";
import "../interfaces/IWETH.sol";
import "../libraries/AddressHelper.sol";
import "../upgradeable_contracts/modules/OwnableModule.sol";
import "../upgradeable_contracts/Claimable.sol";

/**
* @title WETHOmnibridgeRouter
* @dev Omnibridge extension for processing native and wrapped native assets.
* Intended to work with WETH/WBNB/WXDAI tokens, see:
* https://etherscan.io/address/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
* https://bscscan.com/address/0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c
* https://blockscout.com/poa/xdai/address/0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d
*/
contract WETHOmnibridgeRouter is OwnableModule, Claimable {
IOmnibridge public immutable bridge;
// solhint-disable-next-line var-name-mixedcase
IWETH public immutable WETH;

/**
* @dev Initializes this contract.
* @param _bridge address of the HomeOmnibridge/ForeignOmnibridge contract.
* @param _weth address of the WETH token used for wrapping/unwrapping native coins (e.g. WETH/WBNB/WXDAI).
* @param _owner address of the contract owner.
*/
constructor(
IOmnibridge _bridge,
IWETH _weth,
address _owner
) OwnableModule(_owner) {
bridge = _bridge;
WETH = _weth;
_weth.approve(address(_bridge), uint256(-1));
}

/**
* @dev Wraps native assets and relays wrapped ERC20 tokens to the other chain.
* Call msg.sender will receive assets on the other side of the bridge.
*/
function wrapAndRelayTokens() external payable {
wrapAndRelayTokens(msg.sender);
}

/**
* @dev Wraps native assets and relays wrapped ERC20 tokens to the other chain.
* @param _receiver bridged assets receiver on the other side of the bridge.
*/
function wrapAndRelayTokens(address _receiver) public payable {
WETH.deposit{ value: msg.value }();
bridge.relayTokens(address(WETH), _receiver, msg.value);
}

/**
* @dev Bridged callback function used for unwrapping received tokens.
* Can only be called by the associated Omnibridge contract.
* @param _token bridged token contract address, should be WETH.
* @param _value amount of bridged/received tokens.
* @param _data extra data passed alongside with relayTokensAndCall on the other side of the bridge.
* Should contain coins receiver address.
*/
function onTokenBridged(
address _token,
uint256 _value,
bytes calldata _data
) external {
require(_token == address(WETH));
require(msg.sender == address(bridge));
require(_data.length == 20);

WETH.withdraw(_value);

address payable receiver;
assembly {
receiver := calldataload(120)
}
AddressHelper.safeSendValue(receiver, _value);
}

/**
* @dev Claims stuck coins/tokens.
* Only contract owner can call this method.
* @param _token address of claimed token contract, address(0) for native coins.
* @param _to address of tokens receiver
*/
function claimTokens(address _token, address _to) external onlyOwner {
claimValues(_token, _to);
}

/**
* @dev Ether receive function.
* Should be only called from the WETH contract when withdrawing native coins. Will revert otherwise.
*/
receive() external payable {
require(msg.sender == address(WETH));
}
}
9 changes: 9 additions & 0 deletions contracts/interfaces/IERC20Receiver.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
pragma solidity 0.7.5;

interface IERC20Receiver {
function onTokenBridged(
address token,
uint256 value,
bytes calldata data
) external;
}
9 changes: 9 additions & 0 deletions contracts/interfaces/IOmnibridge.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
pragma solidity 0.7.5;

interface IOmnibridge {
function relayTokens(
address _token,
address _receiver,
uint256 _value
) external;
}
9 changes: 9 additions & 0 deletions contracts/interfaces/IWETH.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
pragma solidity 0.7.5;

interface IWETH {
function deposit() external payable;

function withdraw(uint256 _value) external;

function approve(address _to, uint256 _value) external;
}
6 changes: 3 additions & 3 deletions contracts/mocks/AMBMock.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
pragma solidity 0.7.5;

contract AMBMock {
event MockedEvent(bytes32 indexed messageId, address executor, uint8 dataType, bytes data);
event MockedEvent(bytes32 indexed messageId, address executor, uint8 dataType, bytes data, uint256 gas);

address public messageSender;
uint256 public immutable maxGasPerTx;
Expand Down Expand Up @@ -62,7 +62,7 @@ contract AMBMock {
function _sendMessage(
address _contract,
bytes calldata _data,
uint256,
uint256 _gas,
uint256 _dataType
) internal returns (bytes32) {
require(messageId == bytes32(0));
Expand All @@ -73,7 +73,7 @@ contract AMBMock {
bytes32 _messageId = bytes32(uint256(0x11223344 << 224)) | bridgeId | bytes32(nonce);
nonce += 1;

emit MockedEvent(_messageId, _contract, uint8(_dataType), _data);
emit MockedEvent(_messageId, _contract, uint8(_dataType), _data, _gas);
return _messageId;
}

Expand Down
21 changes: 21 additions & 0 deletions contracts/mocks/TokenReceiver.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
pragma solidity 0.7.5;

import "../interfaces/IERC20Receiver.sol";

contract TokenReceiver is IERC20Receiver {
address public token;
address public from;
uint256 public value;
bytes public data;

function onTokenBridged(
address _token,
uint256 _value,
bytes memory _data
) external override {
token = _token;
from = msg.sender;
value = _value;
data = _data;
}
}
64 changes: 64 additions & 0 deletions contracts/mocks/WETH.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
pragma solidity 0.7.5;

// solhint-disable
contract WETH {
string public name = "Wrapped Ether";
string public symbol = "WETH";
uint8 public decimals = 18;

event Approval(address indexed src, address indexed guy, uint wad);
event Transfer(address indexed src, address indexed dst, uint wad);
event Deposit(address indexed dst, uint wad);
event Withdrawal(address indexed src, uint wad);

mapping (address => uint) public balanceOf;
mapping (address => mapping (address => uint)) public allowance;

receive() external payable {
deposit();
}
function deposit() public payable {
balanceOf[msg.sender] += msg.value;
emit Deposit(msg.sender, msg.value);
}
function withdraw(uint wad) public {
require(balanceOf[msg.sender] >= wad);
balanceOf[msg.sender] -= wad;
msg.sender.transfer(wad);
emit Withdrawal(msg.sender, wad);
}

function totalSupply() public view returns (uint) {
return address(this).balance;
}

function approve(address guy, uint wad) public returns (bool) {
allowance[msg.sender][guy] = wad;
emit Approval(msg.sender, guy, wad);
return true;
}

function transfer(address dst, uint wad) public returns (bool) {
return transferFrom(msg.sender, dst, wad);
}

function transferFrom(address src, address dst, uint wad)
public
returns (bool)
{
require(balanceOf[src] >= wad);

if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) {
require(allowance[src][msg.sender] >= wad);
allowance[src][msg.sender] -= wad;
}

balanceOf[src] -= wad;
balanceOf[dst] += wad;

emit Transfer(src, dst, wad);

return true;
}
}
// solhint-enable
Loading

0 comments on commit ea0ffa6

Please sign in to comment.