//SPDX-License-Identifier: UNLICENSED
pragma experimental ABIEncoderV2;
pragma solidity >=0.7.0 <0.9.0;
interface IERC20 {
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
event Transfer(address indexed from, address indexed to, uint256 value);
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint256);
function balanceOf(address owner) external view returns (uint256);
function allowance(address owner, address spender)
external
view
returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transfer(address to, uint256 value) external returns (bool);
function transferFrom(
address from,
address to,
uint256 value
) external returns (bool);
}
pragma solidity >=0.7.0 <0.9.0;
library SafeMath {
function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x + y) >= x, "ds-math-add-overflow");
}
function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x - y) <= x, "ds-math-sub-underflow");
}
function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow");
}
}
pragma solidity >=0.7.0 <0.9.0;
contract Bebop2 {
event OrderExecuted(
address maker_address,
address taker_address,
address base_token,
address quote_token,
uint256 base_quantity,
uint256 quote_quantity,
address receiver
);
event OrderExecuted2(
address maker_address,
address taker_address,
address[] base_tokens,
address quote_token,
uint256[] base_quantities,
uint256 quote_quantity,
address receiver
);
event OrderExecuted3(
address maker_address,
address taker_address,
address base_token,
address[] quote_tokens,
uint256 base_quantity,
uint256[] quote_quantities,
address receiver
);
uint256 chainId = block.chainid;
address verifyingContract = address(this);
string private constant EIP712_DOMAIN =
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)";
bytes32 public constant EIP712_DOMAIN_TYPEHASH =
keccak256(abi.encodePacked(EIP712_DOMAIN));
address constant ETH_ADD = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
struct Order {
// one to one
uint256 expiry;
address taker_address;
address base_token;
address quote_token;
uint256 base_quantity;
uint256 quote_quantity;
address receiver;
}
struct Order2 {
//Many to one
uint256 expiry;
address taker_address;
bytes32 base_tokens;
address quote_token;
bytes32 base_quantities;
uint256 quote_quantity;
address receiver;
}
struct Order3 {
//One to many
uint256 expiry;
address taker_address;
address base_token;
bytes32 quote_tokens;
uint256 base_quantity;
bytes32 quote_quantities;
address receiver;
}
string constant ORDER_TYPE =
"Order(uint256 expiry,address taker_address,address base_token,address quote_token,uint256 base_quantity,uint256 quote_quantity,address receiver)";
bytes32 constant ORDER_TYPEHASH = keccak256(abi.encodePacked(ORDER_TYPE));
string constant ORDER_TYPE2 =
"Order2(uint256 expiry,address taker_address,bytes32 base_tokens,address quote_token,bytes32 base_quantities,uint256 quote_quantity,address receiver)";
bytes32 constant ORDER_TYPEHASH2 = keccak256(abi.encodePacked(ORDER_TYPE2));
string constant ORDER_TYPE3 =
"Order3(uint256 expiry,address taker_address,address base_token,bytes32 quote_tokens,uint256 base_quantity,bytes32 quote_quantities,address receiver)";
bytes32 constant ORDER_TYPEHASH3 = keccak256(abi.encodePacked(ORDER_TYPE3));
bytes32 private DOMAIN_SEPARATOR;
mapping(bytes32 => bool) public Signatures;
constructor() {
DOMAIN_SEPARATOR = keccak256(
abi.encode(
EIP712_DOMAIN_TYPEHASH,
keccak256("Bebop2"),
keccak256("1"),
chainId,
verifyingContract
)
);
}
function getRsv(bytes memory sig)
public
pure
returns (
bytes32,
bytes32,
uint8
)
{
bytes32 r;
bytes32 s;
uint8 v;
assembly {
r := mload(add(sig, 32))
s := mload(add(sig, 64))
v := and(mload(add(sig, 65)), 255)
}
if (v < 27) v += 27;
return (r, s, v);
}
function hashTokens(address[] memory tokens) public pure returns (bytes32) {
return keccak256(abi.encode(tokens));
}
function hashTokenQuantities(uint256[] memory token_quantities)
public
pure
returns (bytes32)
{
return keccak256(abi.encode(token_quantities));
}
function hashOrder(Order memory order) private view returns (bytes32) {
return
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR,
keccak256(
abi.encode(
ORDER_TYPEHASH,
order.expiry,
order.taker_address,
order.base_token,
order.quote_token,
order.base_quantity,
order.quote_quantity,
order.receiver
)
)
)
);
}
function hashOrder2(Order2 memory order) private view returns (bytes32) {
return
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR,
keccak256(
abi.encode(
ORDER_TYPEHASH2,
order.expiry,
order.taker_address,
order.base_tokens,
order.quote_token,
order.base_quantities,
order.quote_quantity,
order.receiver
)
)
)
);
}
function hashOrder3(Order3 memory order) private view returns (bytes32) {
return
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR,
keccak256(
abi.encode(
ORDER_TYPEHASH3,
order.expiry,
order.taker_address,
order.base_token,
order.quote_tokens,
order.base_quantity,
order.quote_quantities,
order.receiver
)
)
)
);
}
function assertValidOrder(Order memory order, bytes memory sig)
public
view
returns (bytes32)
{
(bytes32 r, bytes32 s, uint8 v) = getRsv(sig);
bytes32 h = hashOrder(order);
address trader = ecrecover(h, v, r, s);
require(trader == order.taker_address, "Invalid signature");
require(msg.sender != trader, "Maker/taker must be different address");
require(order.expiry > block.timestamp, "Signature expired");
require(
order.base_quantity > 0 && order.quote_quantity > 0,
"Invalid base/quote amount"
);
require(!Signatures[h], "Signature reuse"); //Ensure no replay attacks
return h;
}
function assertValidOrder2(
Order2 memory order,
bytes memory sig,
address[] memory base_tokens,
uint256[] memory base_quantities
) public view returns (bytes32) {
(bytes32 r, bytes32 s, uint8 v) = getRsv(sig);
bytes32 h = hashOrder2(order);
address trader = ecrecover(h, v, r, s);
require(trader == order.taker_address, "Invalid signature");
require(
order.base_tokens == keccak256(abi.encode(base_tokens)),
"base token hash mismatch"
);
require(
order.base_quantities == keccak256(abi.encode(base_quantities)),
"base quantities hash mismatch"
);
require(msg.sender != trader, "Maker/taker must be different address");
require(order.expiry > block.timestamp, "Signature expired");
require(!Signatures[h], "Signature reuse"); //Ensure no replay attacks
return h;
}
function assertValidOrder3(
Order3 memory order,
bytes memory sig,
address[] memory quote_tokens,
uint256[] memory quote_quantities
) public view returns (bytes32) {
(bytes32 r, bytes32 s, uint8 v) = getRsv(sig);
bytes32 h = hashOrder3(order);
address trader = ecrecover(h, v, r, s);
require(trader == order.taker_address, "Invalid signature");
require(
order.quote_tokens == keccak256(abi.encode(quote_tokens)),
"quote tokens hash mismatch"
);
require(
order.quote_quantities == keccak256(abi.encode(quote_quantities)),
"quote quantities hash mismatch"
);
require(msg.sender != trader, "Maker/taker must be different address");
require(order.expiry > block.timestamp, "Signature expired");
require(!Signatures[h], "Signature reuse"); //Ensure no replay attacks
return h;
}
function makerTransferFunds(
address from,
address to,
uint256 quantity,
address token
) private returns (bool) {
if (token == ETH_ADD) {
require(msg.value == quantity);
payable(to).transfer(msg.value);
} else {
require(IERC20(token).transferFrom(from, to, quantity));
}
return true;
}
//Can only be called by anyone with the signature from trader
function SettleOrder(Order memory order, bytes memory sig)
public
payable
returns (bool)
{
bytes32 h = assertValidOrder(order, sig);
Signatures[h] = true;
require(
makerTransferFunds(
msg.sender,
order.receiver,
order.quote_quantity,
order.quote_token
)
);
require(
IERC20(order.base_token).transferFrom(
order.taker_address,
msg.sender,
order.base_quantity
)
);
emit OrderExecuted(
msg.sender,
order.taker_address,
order.base_token,
order.quote_token,
order.base_quantity,
order.quote_quantity,
order.receiver
);
return true;
}
//Can only be called by anyone with the signature from trader
function SettleOrder2(
Order2 memory order,
bytes memory sig,
address[] memory base_tokens,
uint256[] memory base_quantities
) public payable returns (bool) {
bytes32 h = assertValidOrder2(order, sig, base_tokens, base_quantities);
Signatures[h] = true;
require(
makerTransferFunds(
msg.sender,
order.receiver,
order.quote_quantity,
order.quote_token
)
);
for (uint256 i = 0; i < base_tokens.length; i++) {
require(
IERC20(address(base_tokens[i])).transferFrom(
order.taker_address,
msg.sender,
base_quantities[i]
)
);
}
emit OrderExecuted2(
msg.sender,
order.taker_address,
base_tokens,
order.quote_token,
base_quantities,
order.quote_quantity,
order.receiver
);
return true;
}
//Can only be called by anyone with the signature from trader
function SettleOrder3(
Order3 memory order,
bytes memory sig,
address[] memory quote_tokens,
uint256[] memory quote_quantities
) public payable returns (bool) {
bytes32 h = assertValidOrder3(
order,
sig,
quote_tokens,
quote_quantities
);
Signatures[h] = true;
for (uint256 i = 0; i < quote_tokens.length; i++) {
require(
makerTransferFunds(
msg.sender,
order.receiver,
quote_quantities[i],
quote_tokens[i]
)
);
}
require(
IERC20(address(order.base_token)).transferFrom(
order.taker_address,
msg.sender,
order.base_quantity
)
);
emit OrderExecuted3(
msg.sender,
order.taker_address,
order.base_token,
quote_tokens,
order.base_quantity,
quote_quantities,
order.receiver
);
return true;
}
}
{
"compilationTarget": {
"Bebop2.sol": "Bebop2"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 100000
},
"remappings": []
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"maker_address","type":"address"},{"indexed":false,"internalType":"address","name":"taker_address","type":"address"},{"indexed":false,"internalType":"address","name":"base_token","type":"address"},{"indexed":false,"internalType":"address","name":"quote_token","type":"address"},{"indexed":false,"internalType":"uint256","name":"base_quantity","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"quote_quantity","type":"uint256"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"}],"name":"OrderExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"maker_address","type":"address"},{"indexed":false,"internalType":"address","name":"taker_address","type":"address"},{"indexed":false,"internalType":"address[]","name":"base_tokens","type":"address[]"},{"indexed":false,"internalType":"address","name":"quote_token","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"base_quantities","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"quote_quantity","type":"uint256"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"}],"name":"OrderExecuted2","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"maker_address","type":"address"},{"indexed":false,"internalType":"address","name":"taker_address","type":"address"},{"indexed":false,"internalType":"address","name":"base_token","type":"address"},{"indexed":false,"internalType":"address[]","name":"quote_tokens","type":"address[]"},{"indexed":false,"internalType":"uint256","name":"base_quantity","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"quote_quantities","type":"uint256[]"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"}],"name":"OrderExecuted3","type":"event"},{"inputs":[],"name":"EIP712_DOMAIN_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"address","name":"taker_address","type":"address"},{"internalType":"address","name":"base_token","type":"address"},{"internalType":"address","name":"quote_token","type":"address"},{"internalType":"uint256","name":"base_quantity","type":"uint256"},{"internalType":"uint256","name":"quote_quantity","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"internalType":"struct Bebop2.Order","name":"order","type":"tuple"},{"internalType":"bytes","name":"sig","type":"bytes"}],"name":"SettleOrder","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"address","name":"taker_address","type":"address"},{"internalType":"bytes32","name":"base_tokens","type":"bytes32"},{"internalType":"address","name":"quote_token","type":"address"},{"internalType":"bytes32","name":"base_quantities","type":"bytes32"},{"internalType":"uint256","name":"quote_quantity","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"internalType":"struct Bebop2.Order2","name":"order","type":"tuple"},{"internalType":"bytes","name":"sig","type":"bytes"},{"internalType":"address[]","name":"base_tokens","type":"address[]"},{"internalType":"uint256[]","name":"base_quantities","type":"uint256[]"}],"name":"SettleOrder2","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"address","name":"taker_address","type":"address"},{"internalType":"address","name":"base_token","type":"address"},{"internalType":"bytes32","name":"quote_tokens","type":"bytes32"},{"internalType":"uint256","name":"base_quantity","type":"uint256"},{"internalType":"bytes32","name":"quote_quantities","type":"bytes32"},{"internalType":"address","name":"receiver","type":"address"}],"internalType":"struct Bebop2.Order3","name":"order","type":"tuple"},{"internalType":"bytes","name":"sig","type":"bytes"},{"internalType":"address[]","name":"quote_tokens","type":"address[]"},{"internalType":"uint256[]","name":"quote_quantities","type":"uint256[]"}],"name":"SettleOrder3","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"Signatures","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"address","name":"taker_address","type":"address"},{"internalType":"address","name":"base_token","type":"address"},{"internalType":"address","name":"quote_token","type":"address"},{"internalType":"uint256","name":"base_quantity","type":"uint256"},{"internalType":"uint256","name":"quote_quantity","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"internalType":"struct Bebop2.Order","name":"order","type":"tuple"},{"internalType":"bytes","name":"sig","type":"bytes"}],"name":"assertValidOrder","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"address","name":"taker_address","type":"address"},{"internalType":"bytes32","name":"base_tokens","type":"bytes32"},{"internalType":"address","name":"quote_token","type":"address"},{"internalType":"bytes32","name":"base_quantities","type":"bytes32"},{"internalType":"uint256","name":"quote_quantity","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"internalType":"struct Bebop2.Order2","name":"order","type":"tuple"},{"internalType":"bytes","name":"sig","type":"bytes"},{"internalType":"address[]","name":"base_tokens","type":"address[]"},{"internalType":"uint256[]","name":"base_quantities","type":"uint256[]"}],"name":"assertValidOrder2","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"address","name":"taker_address","type":"address"},{"internalType":"address","name":"base_token","type":"address"},{"internalType":"bytes32","name":"quote_tokens","type":"bytes32"},{"internalType":"uint256","name":"base_quantity","type":"uint256"},{"internalType":"bytes32","name":"quote_quantities","type":"bytes32"},{"internalType":"address","name":"receiver","type":"address"}],"internalType":"struct Bebop2.Order3","name":"order","type":"tuple"},{"internalType":"bytes","name":"sig","type":"bytes"},{"internalType":"address[]","name":"quote_tokens","type":"address[]"},{"internalType":"uint256[]","name":"quote_quantities","type":"uint256[]"}],"name":"assertValidOrder3","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"sig","type":"bytes"}],"name":"getRsv","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"token_quantities","type":"uint256[]"}],"name":"hashTokenQuantities","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"}],"name":"hashTokens","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"}]