Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add frontrun protection using witness and secret #3

Merged
merged 2 commits into from Sep 19, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
106 changes: 79 additions & 27 deletions contracts/UniswapEX.sol
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(ETH_ADDRESS),
Agusx1211 marked this conversation as resolved.
Show resolved Hide resolved
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,26 +121,43 @@ contract UniswapEX {
_minReturn,
_fee,
_owner,
_salt,
_witness,
amount
);
}

function readWitnesses(
bytes calldata _witnesses
) external view returns (address) {
Agusx1211 marked this conversation as resolved.
Show resolved Hide resolved
return SigUtils.ecrecover2(
keccak256(abi.encodePacked(msg.sender)),
_witnesses
);
}

function executeOrder(
IERC20 _fromToken,
IERC20 _toToken,
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(
Agusx1211 marked this conversation as resolved.
Show resolved Hide resolved
keccak256(abi.encodePacked(msg.sender)),
_witnesses
);

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

// Pull amount
Expand Down Expand Up @@ -159,7 +197,7 @@ contract UniswapEX {
_minReturn,
_fee,
_owner,
_salt,
witness,
msg.sender,
amount,
bought
Expand All @@ -173,7 +211,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 +222,7 @@ contract UniswapEX {
_minReturn,
_fee,
_owner,
_salt
_witness
),
_amount,
abi.encode(
Expand All @@ -192,7 +231,8 @@ contract UniswapEX {
_minReturn,
_fee,
_owner,
_salt
_secret,
_witness
)
);
}
Expand All @@ -203,38 +243,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 +296,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 +320,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 +373,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 +445,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 +454,7 @@ contract UniswapEX {
_minReturn,
_fee,
_owner,
_salt
_witness
)
);
}
Expand Down
35 changes: 35 additions & 0 deletions contracts/commons/SigUtils.sol
@@ -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
);
}
}