Skip to content

Commit

Permalink
Add frontrun protection using witness and secret (#3)
Browse files Browse the repository at this point in the history
* Add frontrun protection using witness and secret

* Remove readWitness and use fromToken on depositEth
  • Loading branch information
Agusx1211 authored and nachomazzara committed Sep 19, 2019
1 parent b5d771f commit 2bd1326
Show file tree
Hide file tree
Showing 7 changed files with 721 additions and 305 deletions.
97 changes: 70 additions & 27 deletions contracts/UniswapEX.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pragma solidity ^0.5.11;


import "./commons/SafeMath.sol";
import "./commons/SigUtils.sol";
import "./interfaces/IERC20.sol";
import "./interfaces/UniswapExchange.sol";
import "./interfaces/UniswapFactory.sol";
Expand All @@ -26,7 +27,7 @@ contract UniswapEX {
uint256 _minReturn,
uint256 _fee,
address _owner,
bytes32 _salt,
address _witness,
address _relayer,
uint256 _amount,
uint256 _bought
Expand All @@ -39,7 +40,7 @@ contract UniswapEX {
uint256 _minReturn,
uint256 _fee,
address _owner,
bytes32 _salt,
address _witness,
uint256 _amount
);

Expand All @@ -61,7 +62,27 @@ contract UniswapEX {
) external payable {
require(msg.value > 0, "No value provided");

bytes32 key = keccak256(_data);
(
address fromToken,
address toToken,
uint256 minReturn,
uint256 fee,
address payable owner,
,
address witness
) = decodeOrder(_data);

require(fromToken == ETH_ADDRESS, "order is not from ETH");

bytes32 key = _keyOf(
IERC20(fromToken),
IERC20(toToken),
minReturn,
fee,
owner,
witness
);

ethDeposits[key] = ethDeposits[key].add(msg.value);
emit DepositETH(key, msg.sender, msg.value, _data);
}
Expand All @@ -72,7 +93,7 @@ contract UniswapEX {
uint256 _minReturn,
uint256 _fee,
address payable _owner,
bytes32 _salt
address _witness
) external {
require(msg.sender == _owner, "Only the owner of the order can cancel it");
bytes32 key = _keyOf(
Expand All @@ -81,7 +102,7 @@ contract UniswapEX {
_minReturn,
_fee,
_owner,
_salt
_witness
);

uint256 amount;
Expand All @@ -100,7 +121,7 @@ contract UniswapEX {
_minReturn,
_fee,
_owner,
_salt,
_witness,
amount
);
}
Expand All @@ -111,15 +132,23 @@ contract UniswapEX {
uint256 _minReturn,
uint256 _fee,
address payable _owner,
bytes32 _salt
bytes calldata _witnesses
) external {
// Calculate witness using signature
// avoid front-run by requiring msg.sender to know
// the secret
address witness = SigUtils.ecrecover2(
keccak256(abi.encodePacked(msg.sender)),
_witnesses
);

bytes32 key = _keyOf(
_fromToken,
_toToken,
_minReturn,
_fee,
_owner,
_salt
witness
);

// Pull amount
Expand Down Expand Up @@ -159,7 +188,7 @@ contract UniswapEX {
_minReturn,
_fee,
_owner,
_salt,
witness,
msg.sender,
amount,
bought
Expand All @@ -173,7 +202,8 @@ contract UniswapEX {
uint256 _minReturn,
uint256 _fee,
address payable _owner,
bytes32 _salt
bytes32 _secret,
address _witness
) external view returns (bytes memory) {
return abi.encodeWithSelector(
_fromToken.transfer.selector,
Expand All @@ -183,7 +213,7 @@ contract UniswapEX {
_minReturn,
_fee,
_owner,
_salt
_witness
),
_amount,
abi.encode(
Expand All @@ -192,7 +222,8 @@ contract UniswapEX {
_minReturn,
_fee,
_owner,
_salt
_secret,
_witness
)
);
}
Expand All @@ -203,38 +234,50 @@ contract UniswapEX {
uint256 _minReturn,
uint256 _fee,
address payable _owner,
bytes32 _salt
bytes32 _secret,
address _witness
) external pure returns (bytes memory) {
return abi.encode(
_fromToken,
_toToken,
_minReturn,
_fee,
_owner,
_salt
_secret,
_witness
);
}

function decodeOrder(
bytes calldata _data
) external pure returns (
bytes memory _data
) public pure returns (
address fromToken,
address toToken,
uint256 minReturn,
uint256 fee,
address payable owner,
bytes32 salt
bytes32 secret,
address witness
) {
(
fromToken,
toToken,
minReturn,
fee,
owner,
salt
secret,
witness
) = abi.decode(
_data,
(address, address, uint256, uint256, address, bytes32)
(
address,
address,
uint256,
uint256,
address,
bytes32,
address
)
);
}

Expand All @@ -244,15 +287,15 @@ contract UniswapEX {
uint256 _minReturn,
uint256 _fee,
address payable _owner,
bytes32 _salt
address _witness
) external view returns (bool) {
bytes32 key = _keyOf(
_fromToken,
_toToken,
_minReturn,
_fee,
_owner,
_salt
_witness
);

if (address(_fromToken) == ETH_ADDRESS) {
Expand All @@ -268,15 +311,15 @@ contract UniswapEX {
uint256 _minReturn,
uint256 _fee,
address payable _owner,
bytes32 _salt
address _witness
) external view returns (bool) {
bytes32 key = _keyOf(
_fromToken,
_toToken,
_minReturn,
_fee,
_owner,
_salt
_witness
);

// Pull amount
Expand Down Expand Up @@ -321,15 +364,15 @@ contract UniswapEX {
uint256 _minReturn,
uint256 _fee,
address payable _owner,
bytes32 _salt
address _witness
) public view returns (address) {
return _keyOf(
_fromToken,
_toToken,
_minReturn,
_fee,
_owner,
_salt
_witness
).getVault();
}

Expand Down Expand Up @@ -393,7 +436,7 @@ contract UniswapEX {
uint256 _minReturn,
uint256 _fee,
address payable _owner,
bytes32 _salt
address _witness
) private pure returns (bytes32) {
return keccak256(
abi.encode(
Expand All @@ -402,7 +445,7 @@ contract UniswapEX {
_minReturn,
_fee,
_owner,
_salt
_witness
)
);
}
Expand Down
35 changes: 35 additions & 0 deletions contracts/commons/SigUtils.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
pragma solidity ^0.5.5;


library SigUtils {
/**
@dev Recovers address who signed the message
@param _hash operation ethereum signed message hash
@param _signature message `hash` signature
*/
function ecrecover2 (
bytes32 _hash,
bytes memory _signature
) internal pure returns (address) {
bytes32 r;
bytes32 s;
uint8 v;

assembly {
r := mload(add(_signature, 32))
s := mload(add(_signature, 64))
v := and(mload(add(_signature, 65)), 255)
}

if (v < 27) {
v += 27;
}

return ecrecover(
_hash,
v,
r,
s
);
}
}
Loading

0 comments on commit 2bd1326

Please sign in to comment.